diff --git a/.github/actions/setup-native/action.yml b/.github/actions/setup-native/action.yml index 43322760..f8bea73b 100644 --- a/.github/actions/setup-native/action.yml +++ b/.github/actions/setup-native/action.yml @@ -16,7 +16,7 @@ runs: run: | set -euo pipefail case "${NATIVE_TARGET}:${RUNNER_OS}:${RUNNER_ARCH}" in - linux-x86_64:Linux:X64|macos-arm64:macOS:ARM64|macos-x86_64:macOS:X64|windows-x86_64:Windows:X64) ;; + macos-arm64:macOS:ARM64|windows-x86_64:Windows:X64) ;; *) echo "Target ${NATIVE_TARGET} does not match ${RUNNER_OS}/${RUNNER_ARCH}." >&2 exit 1 @@ -47,9 +47,7 @@ runs: set -euo pipefail version="$(awk '/^name = "lbug"/ { found=1 } found && /^version = / { gsub(/"/, "", $3); print $3; exit }' Cargo.lock)" case "$NATIVE_TARGET" in - linux-x86_64) archive='liblbug-static-linux-x86_64-compat.tar.gz' ;; macos-arm64) archive='liblbug-static-osx-arm64.tar.gz' ;; - macos-x86_64) archive='liblbug-static-osx-x86_64.tar.gz' ;; windows-x86_64) archive='liblbug-static-windows-x86_64.zip' ;; *) echo "Unsupported native target: $NATIVE_TARGET" >&2; exit 1 ;; esac diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0051a49b..dac53fa4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,12 +94,8 @@ jobs: fail-fast: false matrix: include: - - target: linux-x86_64 - run-tests: true - target: macos-arm64 run-tests: true - - target: macos-x86_64 - run-tests: false - target: windows-x86_64 run-tests: true uses: ./.github/workflows/native.yml diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml index b810a23f..fee3466c 100644 --- a/.github/workflows/native.yml +++ b/.github/workflows/native.yml @@ -28,9 +28,15 @@ env: CARGO_NET_RETRY: "10" jobs: - native: - name: native artifact (${{ inputs.target }}) - runs-on: ${{ fromJSON('{"linux-x86_64":"ubuntu-latest","macos-arm64":"macos-latest","macos-x86_64":"macos-15-intel","windows-x86_64":"windows-2022"}')[inputs.target] }} + tests: + name: native tests (${{ inputs.target }}, ${{ matrix.cohort }}${{ matrix.shard > 0 && format(', shard {0}', matrix.shard) || '' }}) + if: inputs.run-tests + strategy: + fail-fast: false + matrix: + cohort: [unit, generation, integration] + shard: ${{ fromJSON(inputs.target == 'windows-x86_64' && '[1,2,3,4]' || '[0]') }} + runs-on: ${{ fromJSON('{"macos-arm64":"macos-latest","windows-x86_64":"windows-2022"}')[inputs.target] }} timeout-minutes: 90 permissions: contents: read @@ -46,8 +52,53 @@ jobs: target: ${{ inputs.target }} - name: Run platform tests - if: inputs.run-tests - run: cargo run -p xtask -- native-test --target "${{ inputs.target }}" + id: platform_tests + timeout-minutes: ${{ inputs.target == 'macos-arm64' && 75 || 85 }} + shell: bash + env: + RUST_TEST_NOCAPTURE: "1" + RUST_TEST_THREADS: "1" + SOURCE_SHA: ${{ inputs.source-sha }} + TMP: ${{ runner.temp }} + TEMP: ${{ runner.temp }} + run: | + set -o pipefail + mkdir -p diagnostics + printf '%s\n' "$SOURCE_SHA" > diagnostics/source-sha.txt + shard_args=() + if [[ '${{ inputs.target }}' == 'windows-x86_64' ]]; then + export CGRAPH_CLEANUP_TRACE=1 + shard_args=(--shard '${{ matrix.shard }}/4') + fi + cargo run -p xtask -- native-test --target "${{ inputs.target }}" --cohort "${{ matrix.cohort }}" "${shard_args[@]}" 2>&1 | tee diagnostics/platform-test.log + + - name: Upload native test diagnostics + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: test-diagnostics-${{ inputs.target }}-${{ matrix.cohort }}-shard${{ matrix.shard }}-${{ github.run_attempt }} + path: diagnostics/ + if-no-files-found: warn + retention-days: 3 + + native: + name: native artifact (${{ inputs.target }}) + needs: tests + if: ${{ always() && !cancelled() && (!inputs.run-tests || needs.tests.result == 'success') }} + runs-on: ${{ fromJSON('{"macos-arm64":"macos-latest","windows-x86_64":"windows-2022"}')[inputs.target] }} + timeout-minutes: 90 + permissions: + contents: read + steps: + - name: Check out source SHA + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: ${{ inputs.source-sha }} + + - name: Set up native dependencies + uses: ./.github/actions/setup-native + with: + target: ${{ inputs.target }} - name: Build and smoke release artifact run: cargo run -p xtask -- native-artifact --target "${{ inputs.target }}" --commit-sha "${{ inputs.source-sha }}" --output "dist/${{ inputs.target }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 375f00f0..2a462f0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -212,7 +212,7 @@ jobs: artifact_count="$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$REQUESTED_CI_RUN_ID/artifacts?per_page=100" --jq '[.artifacts[] | select(.expired == false and (.name | startswith("native-")))] | length')" { echo "ci-run-id=$REQUESTED_CI_RUN_ID" - if [[ "$artifact_count" == '4' ]]; then + if [[ "$artifact_count" == '2' ]]; then echo 'artifacts-available=true' else echo 'artifacts-available=false' @@ -229,7 +229,7 @@ jobs: artifact_count="$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$run_id/artifacts?per_page=100" --jq '[.artifacts[] | select(.expired == false and (.name | startswith("native-")))] | length')" { echo "ci-run-id=$run_id" - if [[ "$artifact_count" == '4' ]]; then + if [[ "$artifact_count" == '2' ]]; then echo "artifacts-available=true" else echo "artifacts-available=false" @@ -322,9 +322,7 @@ jobs: fail-fast: false matrix: target: - - linux-x86_64 - macos-arm64 - - macos-x86_64 - windows-x86_64 uses: ./.github/workflows/native.yml with: diff --git a/Cargo.toml b/Cargo.toml index cc283d56..80bb1c01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ resolver = "2" [profile.native-tests] inherits = "release" debug-assertions = true +overflow-checks = true +panic = "unwind" [lib] name = "codebase_graph" @@ -66,7 +68,7 @@ walkdir = "2" cc = "1.2" [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_System_ProcessStatus", "Win32_System_Threading", "Win32_System_JobObjects", "Win32_System_SystemServices", "Win32_Security", "Win32_Security_Authorization", "Win32_System_Memory", "Win32_Storage_FileSystem", "Win32_System_Pipes", "Win32_System_Console", "Win32_System_Diagnostics_ToolHelp"] } +windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Globalization", "Win32_System_ProcessStatus", "Win32_System_Threading", "Win32_System_JobObjects", "Win32_System_SystemServices", "Win32_Security", "Win32_Security_Authorization", "Win32_System_Memory", "Win32_Storage_FileSystem", "Win32_System_Pipes", "Win32_System_Console", "Win32_System_Diagnostics_ToolHelp", "Win32_System_IO", "Wdk_Foundation", "Wdk_Storage_FileSystem"] } [[bin]] name = "codebase-graph" diff --git a/README.md b/README.md index 0e72d279..61eb1d17 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ This is illustrative compact block output; exact matches depend on the graph. ## Quick start +Supported platforms are macOS on Apple Silicon (ARM64) and Windows x86_64. +Linux and Intel macOS are no longer supported. + Install from crates.io with Cargo (Rust 1.82 or newer) from the repository you want to index: diff --git a/assets/graph_schema.json b/assets/graph_schema.json index 38ab664f..1069d82e 100644 --- a/assets/graph_schema.json +++ b/assets/graph_schema.json @@ -1,6731 +1 @@ -{ - "context_profiles": { - "brief": { - "description": "Smallest useful context: matched nodes plus direct defining file/module.", - "max_depth": 1, - "relations": [ - "Contains", - "Defines", - "EvidencedBy" - ] - }, - "callgraph": { - "description": "Callable neighborhood for callers, callees, and call expressions.", - "max_depth": 2, - "relations": [ - "Calls", - "References", - "ResolvesTo" - ] - }, - "change_impact": { - "description": "Context for likely downstream impact of changing a symbol, including documentation and evidence.", - "max_depth": 3, - "relations": [ - "Defines", - "References", - "Calls", - "RoutesTo", - "ExecutesQuery", - "UsesSecret", - "DependsOn", - "Documents", - "EvidencedBy" - ] - }, - "definitions": { - "description": "Definition-oriented context for symbols and scopes.", - "max_depth": 2, - "relations": [ - "Defines", - "Declares", - "HasScope", - "HasParameter", - "HasReturnType", - "HasTypeAnnotation" - ] - }, - "dependencies": { - "description": "Import and dependency context.", - "max_depth": 2, - "relations": [ - "Imports", - "DependsOn", - "References", - "ResolvesTo" - ] - }, - "docs": { - "description": "Documentation context connected to code artifacts.", - "max_depth": 1, - "relations": [ - "Documents", - "EvidencedBy" - ] - }, - "runtime": { - "description": "Runtime surface context for routes, endpoints, queries, and secrets.", - "max_depth": 2, - "relations": [ - "RoutesTo", - "Exposes", - "ExecutesQuery", - "UsesSecret" - ] - } - }, - "node_types": [ - { - "constraints": [], - "description": "A version-controlled repository or source tree boundary.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Repository", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A configured root scanned for source, docs, manifests, and generated evidence.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "SourceRoot", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A source, manifest, configuration, or documentation file.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - }, - { - "description": "Hash of file content at extraction time.", - "name": "content_hash", - "required": false, - "type": "string" - }, - { - "description": "File size in bytes at extraction time.", - "name": "size_bytes", - "required": false, - "type": "integer" - } - ], - "name": "File", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A language-level compilation or namespace unit derived from a source file.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Module", - "parser_node_types": [ - "module", - "program", - "source_file", - "Module" - ] - }, - { - "constraints": [], - "description": "An import/include/use/require declaration.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - }, - { - "description": "Imported module, package, symbol, or path.", - "name": "imported_name", - "required": false, - "type": "string" - } - ], - "name": "ImportDeclaration", - "parser_node_types": [ - "import_statement", - "import_from_statement", - "import_declaration", - "Import", - "ImportFrom" - ] - }, - { - "constraints": [], - "description": "An exported symbol or module boundary declaration.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "ExportDeclaration", - "parser_node_types": [ - "export_statement", - "export_clause", - "export_declaration" - ] - }, - { - "constraints": [], - "description": "A named code artifact when the exact semantic subtype is unresolved.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Symbol", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A lexical or semantic boundary for name resolution.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Scope", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A class, struct, trait, interface, enum class, or similar type container.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Class", - "parser_node_types": [ - "class_definition", - "class_declaration", - "struct_item", - "ClassDef" - ] - }, - { - "constraints": [], - "description": "A standalone function, lambda with stable name, or callable declaration.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Function", - "parser_node_types": [ - "function_definition", - "function_declaration", - "FunctionDef" - ] - }, - { - "constraints": [], - "description": "A function declared inside a class, trait, component, or object scope.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Method", - "parser_node_types": [ - "method_definition", - "method_declaration", - "FunctionDef" - ] - }, - { - "constraints": [], - "description": "A callable parameter.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Parameter", - "parser_node_types": [ - "parameter", - "typed_parameter", - "arg" - ] - }, - { - "constraints": [], - "description": "A callable return type annotation.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "ReturnType", - "parser_node_types": [ - "return_type", - "returns" - ] - }, - { - "constraints": [], - "description": "A type annotation attached to a symbol, parameter, assignment, or return value.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "TypeAnnotation", - "parser_node_types": [ - "type", - "type_identifier", - "type_annotation", - "annotation" - ] - }, - { - "constraints": [], - "description": "A named alias for a type expression.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "TypeAlias", - "parser_node_types": [ - "type_alias", - "type_alias_declaration" - ] - }, - { - "constraints": [], - "description": "A mutable or local named binding.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Variable", - "parser_node_types": [ - "variable_declaration", - "Name" - ] - }, - { - "constraints": [], - "description": "A named binding treated as stable or immutable by convention or syntax.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Constant", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A class-level attribute or static field.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "ClassAttribute", - "parser_node_types": [ - "AnnAssign", - "field_declaration" - ] - }, - { - "constraints": [], - "description": "An instance-level attribute or field assignment.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "InstanceAttribute", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A computed or decorated property exposed as an attribute.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Property", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A decorator, annotation, macro, or attribute attached to a declaration.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Decorator", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A call, constructor invocation, message send, or macro invocation.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "CallExpression", - "parser_node_types": [ - "call", - "Call" - ] - }, - { - "constraints": [], - "description": "An assignment, binding, or destructuring declaration.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Assignment", - "parser_node_types": [ - "assignment", - "Assign", - "AnnAssign" - ] - }, - { - "constraints": [], - "description": "A name or member reference that may resolve to another node.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Reference", - "parser_node_types": [ - "identifier", - "Name" - ] - }, - { - "constraints": [], - "description": "A literal value from source code.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Literal", - "parser_node_types": [ - "string", - "integer", - "float", - "Constant" - ] - }, - { - "constraints": [], - "description": "A non-literal expression worth preserving for context or reasoning.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Expression", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A branch, loop, match, switch, or guard block.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "ControlFlowBlock", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A raise, throw, try, catch, except, rescue, or finally flow unit.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "ExceptionFlow", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A network, RPC, CLI, event, or message endpoint exposed by code.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "APIEndpoint", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A UI, service, package, or runtime component represented in source.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Component", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A route pattern, path binding, or router entry.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Route", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A database, search, analytics, or graph query string/expression.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Query", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A reference to a secret, credential, token, key, or sensitive environment variable.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "SecretRef", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "An external package, library, framework, service, or runtime dependency.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - }, - { - "description": "Declared version or version constraint.", - "name": "version", - "required": false, - "type": "string" - }, - { - "description": "Dependency ecosystem such as pypi, npm, cargo, or go.", - "name": "ecosystem", - "required": false, - "type": "string" - } - ], - "name": "Dependency", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A documentation file or generated documentation artifact.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "DocumentationSource", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "A chunk or heading-level section of documentation.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "DocumentationChunk", - "parser_node_types": [] - }, - { - "constraints": [], - "description": "Raw parser evidence preserving the concrete syntax node and capture name.", - "fields": [ - { - "description": "Stable unique node identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Short human-readable node label.", - "name": "label", - "required": true, - "type": "string" - }, - { - "description": "Ontology-specific subtype or parser-derived role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source language when the node is code-derived.", - "name": "language", - "required": false, - "type": "string" - }, - { - "description": "Versioned grammar identity that produced this syntax node.", - "name": "grammar_version", - "required": false, - "type": "string" - }, - { - "description": "Repository-relative source path.", - "name": "path", - "required": false, - "type": "string" - }, - { - "description": "Best-effort language-neutral qualified name.", - "name": "qualified_name", - "required": false, - "type": "string" - }, - { - "description": "Containing lexical or semantic scope id.", - "name": "scope_id", - "required": false, - "type": "string" - }, - { - "description": "One-based start line in the source file.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based end line in the source file.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based start byte in the source file.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based end byte in the source file.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Raw parser node type that produced this node.", - "name": "tree_sitter_node_type", - "required": false, - "type": "string" - }, - { - "description": "Tree-sitter query capture name when available.", - "name": "capture_name", - "required": false, - "type": "string" - }, - { - "description": "Compact text summary used for context assembly.", - "name": "summary", - "required": false, - "type": "string" - }, - { - "description": "Structured extractor-specific details.", - "name": "metadata", - "required": false, - "type": "json" - }, - { - "description": "Optional S-expression or compact parse-tree fragment.", - "name": "sexp", - "required": false, - "type": "string" - }, - { - "description": "Optional source text captured for this syntax node.", - "name": "text", - "required": false, - "type": "string" - } - ], - "name": "SyntaxCapture", - "parser_node_types": [] - } - ], - "ontology": "code_ontology_v1", - "parser_node_mappings": [ - { - "captures": [ - "module", - "source_file" - ], - "context_rule": "", - "description": "Create one Module node per parser root or language namespace root.", - "name": "module", - "parser_node_types": [ - "module", - "program", - "source_file", - "Module" - ], - "relation_types": [ - "Contains", - "Defines", - "DerivedFrom" - ], - "target_node_types": [ - "Module" - ] - }, - { - "captures": [ - "import", - "reference.import", - "reference.include", - "reference.require", - "reference.use" - ], - "context_rule": "", - "description": "Normalize import-like declarations across languages and attach imported names as metadata.", - "name": "imports", - "parser_node_types": [ - "import_statement", - "import_from_statement", - "import_declaration", - "Import", - "ImportFrom" - ], - "relation_types": [ - "Imports", - "DependsOn", - "DerivedFrom" - ], - "target_node_types": [ - "ImportDeclaration" - ] - }, - { - "captures": [ - "export", - "definition.export" - ], - "context_rule": "", - "description": "Capture public export declarations and declarations marked as exported.", - "name": "exports", - "parser_node_types": [ - "export_statement", - "export_clause", - "export_declaration" - ], - "relation_types": [ - "Exports", - "DerivedFrom" - ], - "target_node_types": [ - "ExportDeclaration" - ] - }, - { - "captures": [ - "definition.class", - "definition.struct", - "definition.interface" - ], - "context_rule": "", - "description": "Map class-like containers to Class nodes with a child Scope.", - "name": "classes", - "parser_node_types": [ - "class_definition", - "class_declaration", - "struct_item", - "interface_declaration", - "ClassDef" - ], - "relation_types": [ - "Defines", - "Declares", - "HasScope", - "DecoratedBy", - "DerivedFrom" - ], - "target_node_types": [ - "Class" - ] - }, - { - "captures": [ - "definition.function", - "definition.method" - ], - "context_rule": "enclosing Class or Component changes the target node from Function to Method", - "description": "Create Function for module-level callables and Method when the callable is enclosed by Class or Component.", - "name": "functions_and_methods", - "parser_node_types": [ - "function_definition", - "function_declaration", - "method_definition", - "method_declaration", - "FunctionDef" - ], - "relation_types": [ - "Defines", - "Declares", - "HasScope", - "HasParameter", - "HasReturnType", - "DecoratedBy", - "DerivedFrom" - ], - "target_node_types": [ - "Function", - "Method" - ] - }, - { - "captures": [ - "definition.parameter", - "parameter" - ], - "context_rule": "", - "description": "Create Parameter nodes for callable parameter declarations.", - "name": "parameters", - "parser_node_types": [ - "parameter", - "typed_parameter", - "default_parameter", - "arg" - ], - "relation_types": [ - "HasParameter", - "HasTypeAnnotation", - "DerivedFrom" - ], - "target_node_types": [ - "Parameter" - ] - }, - { - "captures": [ - "type.return", - "return_type" - ], - "context_rule": "", - "description": "Capture explicit return type annotations.", - "name": "return_types", - "parser_node_types": [ - "return_type", - "type", - "type_identifier", - "returns" - ], - "relation_types": [ - "HasReturnType", - "HasTypeAnnotation", - "References", - "DerivedFrom" - ], - "target_node_types": [ - "ReturnType" - ] - }, - { - "captures": [ - "type", - "type.annotation", - "reference.type" - ], - "context_rule": "", - "description": "Capture type annotation expressions attached to declarations.", - "name": "type_annotations", - "parser_node_types": [ - "type", - "type_identifier", - "type_annotation", - "annotation", - "Name" - ], - "relation_types": [ - "HasTypeAnnotation", - "References", - "ResolvesTo", - "DerivedFrom" - ], - "target_node_types": [ - "TypeAnnotation" - ] - }, - { - "captures": [ - "definition.type_alias" - ], - "context_rule": "", - "description": "Capture named type aliases.", - "name": "type_aliases", - "parser_node_types": [ - "type_alias", - "type_alias_declaration" - ], - "relation_types": [ - "Defines", - "HasTypeAnnotation", - "DerivedFrom" - ], - "target_node_types": [ - "TypeAlias" - ] - }, - { - "captures": [ - "definition.variable", - "definition.constant", - "assignment" - ], - "context_rule": "", - "description": "Normalize assignments; scope, naming convention, and receiver decide variable, constant, or attribute node type.", - "name": "assignments", - "parser_node_types": [ - "assignment", - "assignment_expression", - "variable_declaration", - "Assign", - "AnnAssign" - ], - "relation_types": [ - "Defines", - "Declares", - "Assigns", - "HasTypeAnnotation", - "DerivedFrom" - ], - "target_node_types": [ - "Assignment", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property" - ] - }, - { - "captures": [ - "decorator", - "definition.decorator" - ], - "context_rule": "", - "description": "Capture decorators, annotations, macros, or framework markers that modify declarations.", - "name": "decorators", - "parser_node_types": [ - "decorator", - "attribute_item", - "annotation", - "Call", - "Name" - ], - "relation_types": [ - "DecoratedBy", - "Calls", - "References", - "DerivedFrom" - ], - "target_node_types": [ - "Decorator" - ] - }, - { - "captures": [ - "reference.call", - "call" - ], - "context_rule": "", - "description": "Create call-expression nodes and optionally resolve them to callable targets.", - "name": "calls", - "parser_node_types": [ - "call", - "call_expression", - "invocation_expression", - "Call" - ], - "relation_types": [ - "Calls", - "References", - "ResolvesTo", - "DerivedFrom" - ], - "target_node_types": [ - "CallExpression" - ] - }, - { - "captures": [ - "reference", - "reference.identifier", - "reference.member" - ], - "context_rule": "", - "description": "Capture name and member references before or after semantic resolution.", - "name": "references", - "parser_node_types": [ - "identifier", - "field_identifier", - "attribute", - "Name", - "Attribute" - ], - "relation_types": [ - "References", - "ResolvesTo", - "DerivedFrom" - ], - "target_node_types": [ - "Reference" - ] - }, - { - "captures": [ - "literal", - "string", - "number" - ], - "context_rule": "", - "description": "Capture literals that are useful for docs, routes, queries, secrets, or assignment values.", - "name": "literals", - "parser_node_types": [ - "string", - "integer", - "float", - "true", - "false", - "null", - "none", - "Constant" - ], - "relation_types": [ - "Contains", - "References", - "DerivedFrom" - ], - "target_node_types": [ - "Literal" - ] - }, - { - "captures": [ - "control_flow" - ], - "context_rule": "", - "description": "Capture branch and loop blocks when they affect reasoning or dependency paths.", - "name": "control_flow", - "parser_node_types": [ - "if_statement", - "for_statement", - "while_statement", - "match_statement", - "switch_statement" - ], - "relation_types": [ - "Contains", - "References", - "DerivedFrom" - ], - "target_node_types": [ - "ControlFlowBlock" - ] - }, - { - "captures": [ - "exception", - "raises", - "handles" - ], - "context_rule": "", - "description": "Capture exception raising and handling paths.", - "name": "exception_flow", - "parser_node_types": [ - "try_statement", - "except_clause", - "catch_clause", - "raise_statement", - "throw_statement" - ], - "relation_types": [ - "Raises", - "Handles", - "DerivedFrom" - ], - "target_node_types": [ - "ExceptionFlow" - ] - }, - { - "captures": [ - "entrypoint.api", - "route", - "endpoint" - ], - "context_rule": "", - "description": "Create APIEndpoint and Route nodes from framework route declarations or decorated handlers.", - "name": "routes_and_endpoints", - "parser_node_types": [ - "decorator", - "call", - "route_declaration", - "handler_definition" - ], - "relation_types": [ - "RoutesTo", - "Exposes", - "DecoratedBy", - "DerivedFrom" - ], - "target_node_types": [ - "APIEndpoint", - "Route" - ] - }, - { - "captures": [ - "definition.component", - "component" - ], - "context_rule": "", - "description": "Capture UI, service, runtime, or package components when extractor rules identify them.", - "name": "components", - "parser_node_types": [ - "class_definition", - "function_definition", - "jsx_element", - "component_declaration" - ], - "relation_types": [ - "Defines", - "Contains", - "Exposes", - "DerivedFrom" - ], - "target_node_types": [ - "Component" - ] - }, - { - "captures": [ - "query.sql", - "query.graph", - "query.search" - ], - "context_rule": "", - "description": "Capture query strings or query builder expressions.", - "name": "queries", - "parser_node_types": [ - "string", - "template_string", - "call", - "Call" - ], - "relation_types": [ - "ExecutesQuery", - "References", - "DerivedFrom" - ], - "target_node_types": [ - "Query" - ] - }, - { - "captures": [ - "secret", - "secret.env", - "secret.ref" - ], - "context_rule": "", - "description": "Capture secret-looking names, environment references, keys, and credential handles.", - "name": "secrets", - "parser_node_types": [ - "identifier", - "string", - "attribute", - "Name", - "Constant" - ], - "relation_types": [ - "UsesSecret", - "References", - "DerivedFrom" - ], - "target_node_types": [ - "SecretRef" - ] - }, - { - "captures": [ - "doc", - "doc.string", - "doc.comment" - ], - "context_rule": "", - "description": "Capture documentation sources and chunks from docs, comments, and docstrings.", - "name": "documentation", - "parser_node_types": [ - "comment", - "string", - "docstring", - "DocumentationSource", - "DocumentationChunk" - ], - "relation_types": [ - "Documents", - "EvidencedBy" - ], - "target_node_types": [ - "DocumentationSource", - "DocumentationChunk" - ] - } - ], - "query_helpers": [ - { - "description": "List high-level source roots, files, modules, dependencies, and runtime surfaces.", - "name": "repository_overview", - "parameters": [], - "query": "MATCH (n) WHERE n:SourceRoot OR n:File OR n:Module OR n:Dependency OR n:APIEndpoint OR n:Component RETURN n.id, n.label, n.path LIMIT 100", - "returns": [ - "id", - "label", - "path" - ] - }, - { - "description": "Find concrete semantic definitions by label or qualified name.", - "name": "symbol_lookup", - "parameters": [ - "name" - ], - "query": "MATCH (s:Class) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Function) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Method) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Variable) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Constant) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:ClassAttribute) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:InstanceAttribute) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Property) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Parameter) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:TypeAlias) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path LIMIT 25", - "returns": [ - "id", - "label", - "qualified_name", - "path" - ] - }, - { - "description": "Find a named class, function, method, variable, or constant definition.", - "name": "definition_context", - "parameters": [], - "query": "MATCH (d) WHERE d:Class OR d:Function OR d:Method OR d:Variable OR d:Constant RETURN d.id, d.label, d.kind, d.path LIMIT 50", - "returns": [ - "id", - "label", - "kind", - "path" - ] - }, - { - "description": "Find call expressions and resolved callable targets near a symbol.", - "name": "callgraph_neighborhood", - "parameters": [], - "query": "MATCH (c:CallExpression)-[:FROM_ResolvesTo]->(:ResolvesTo)-[:TO_ResolvesTo]->(target) RETURN c.id, c.path, target.id, target.label LIMIT 50", - "returns": [ - "call_id", - "path", - "target_id", - "target_label" - ] - }, - { - "description": "Inspect imports and dependencies.", - "name": "dependency_map", - "parameters": [], - "query": "MATCH (i:ImportDeclaration)-[:FROM_DependsOn]->(:DependsOn)-[:TO_DependsOn]->(d:Dependency) RETURN i.id, i.label, d.id, d.label LIMIT 100", - "returns": [ - "import_id", - "import_label", - "dependency_id", - "dependency_label" - ] - }, - { - "description": "Inspect routes, endpoints, executed queries, and secret use.", - "name": "runtime_surface", - "parameters": [], - "query": "MATCH (r:Route)-[:FROM_RoutesTo]->(:RoutesTo)-[:TO_RoutesTo]->(e:APIEndpoint) RETURN r.id, r.label, e.id, e.label LIMIT 100", - "returns": [ - "route_id", - "route_label", - "endpoint_id", - "endpoint_label" - ] - }, - { - "description": "Find documentation chunks connected to code nodes.", - "name": "documentation_context", - "parameters": [], - "query": "MATCH (d:DocumentationChunk)-[:FROM_Documents]->(:Documents)-[:TO_Documents]->(n) RETURN d.id, d.label, n.id, n.label LIMIT 50", - "returns": [ - "doc_id", - "doc_label", - "node_id", - "node_label" - ] - }, - { - "description": "Find references that have not been resolved to a semantic target.", - "name": "unresolved_references", - "parameters": [], - "query": "MATCH (r:Reference) WHERE NOT EXISTS { MATCH (r)-[:FROM_ResolvesTo]->(:ResolvesTo)-[:TO_ResolvesTo]->() } RETURN r.id, r.label, r.path, r.line_start LIMIT 100", - "returns": [ - "id", - "label", - "path", - "line_start" - ] - } - ], - "relation_types": [ - { - "constraints": [], - "description": "Structural containment between repository, files, scopes, declarations, and syntax-derived units.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Contains", - "source_types": [ - "Repository", - "SourceRoot", - "File", - "Module", - "Scope", - "Class", - "Function", - "Method", - "Component" - ], - "target_types": [ - "SourceRoot", - "File", - "Module", - "Scope", - "ImportDeclaration", - "ExportDeclaration", - "Symbol", - "Class", - "Function", - "Method", - "Parameter", - "ReturnType", - "TypeAnnotation", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Decorator", - "Assignment", - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef", - "CallExpression", - "Assignment", - "Reference", - "Literal", - "Expression", - "ControlFlowBlock", - "ExceptionFlow", - "Query", - "SecretRef", - "DocumentationSource", - "DocumentationChunk" - ] - }, - { - "constraints": [], - "description": "A file, module, scope, or component defines a semantic code node.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Defines", - "source_types": [ - "File", - "Module", - "Scope", - "Class", - "Function", - "Method", - "Component" - ], - "target_types": [ - "Symbol", - "Class", - "Function", - "Method", - "Parameter", - "ReturnType", - "TypeAnnotation", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Decorator", - "Assignment", - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef" - ] - }, - { - "constraints": [], - "description": "A source unit imports, includes, requires, or uses another unit.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Imports", - "source_types": [ - "File", - "Module", - "Scope" - ], - "target_types": [ - "ImportDeclaration", - "Dependency", - "Module", - "Symbol" - ] - }, - { - "constraints": [], - "description": "A source unit exports a declaration or public surface.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Exports", - "source_types": [ - "File", - "Module", - "Scope", - "Component" - ], - "target_types": [ - "ExportDeclaration", - "Symbol", - "Class", - "Function", - "Method", - "Parameter", - "ReturnType", - "TypeAnnotation", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Decorator", - "Assignment", - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef" - ] - }, - { - "constraints": [], - "description": "A declaration site introduces a named symbol or subordinate declaration.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Declares", - "source_types": [ - "Module", - "Scope", - "Class", - "Function", - "Method", - "Assignment" - ], - "target_types": [ - "Symbol", - "Class", - "Function", - "Method", - "Parameter", - "ReturnType", - "TypeAnnotation", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Decorator", - "Assignment", - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef" - ] - }, - { - "constraints": [], - "description": "Connects a node to the lexical or semantic scope used for resolution.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "HasScope", - "source_types": [ - "File", - "Module", - "Symbol", - "Class", - "Function", - "Method", - "Parameter", - "ReturnType", - "TypeAnnotation", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Decorator", - "Assignment", - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef", - "CallExpression", - "Assignment", - "Reference", - "Literal", - "Expression", - "ControlFlowBlock", - "ExceptionFlow", - "Query", - "SecretRef" - ], - "target_types": [ - "Scope" - ] - }, - { - "constraints": [], - "description": "Connects callables, endpoints, routes, or calls to their parameters or arguments.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "HasParameter", - "source_types": [ - "Function", - "Method", - "APIEndpoint", - "Route", - "CallExpression" - ], - "target_types": [ - "Parameter" - ] - }, - { - "constraints": [], - "description": "Connects callables or endpoints to their return type node.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "HasReturnType", - "source_types": [ - "Function", - "Method", - "APIEndpoint" - ], - "target_types": [ - "ReturnType" - ] - }, - { - "constraints": [], - "description": "Connects a typed code node to its annotation expression.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "HasTypeAnnotation", - "source_types": [ - "Symbol", - "Parameter", - "ReturnType", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute" - ], - "target_types": [ - "TypeAnnotation", - "Reference", - "Literal" - ] - }, - { - "constraints": [], - "description": "Connects an assignment site or assigned symbol to the target or assigned value.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Assigns", - "source_types": [ - "Assignment", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property" - ], - "target_types": [ - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Literal", - "Expression", - "CallExpression" - ] - }, - { - "constraints": [], - "description": "A source reference mentions another semantic node without necessarily resolving to it.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "References", - "source_types": [ - "Reference", - "Expression", - "CallExpression", - "Assignment", - "ControlFlowBlock", - "TypeAnnotation", - "Decorator", - "Query", - "SecretRef" - ], - "target_types": [ - "Symbol", - "Class", - "Function", - "Method", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Parameter", - "Module", - "Dependency" - ] - }, - { - "constraints": [], - "description": "A callable or call expression invokes another callable-like target.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Calls", - "source_types": [ - "Function", - "Method", - "CallExpression", - "Decorator", - "APIEndpoint", - "Route", - "Component" - ], - "target_types": [ - "CallExpression", - "Function", - "Method", - "Class", - "APIEndpoint" - ] - }, - { - "constraints": [], - "description": "A declaration is modified by a decorator, annotation, macro, or framework marker.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "DecoratedBy", - "source_types": [ - "Class", - "Function", - "Method", - "Property", - "APIEndpoint", - "Route", - "Component" - ], - "target_types": [ - "Decorator", - "CallExpression", - "Reference" - ] - }, - { - "constraints": [], - "description": "A resolver maps a syntactic reference to the best semantic target.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "ResolvesTo", - "source_types": [ - "Reference", - "ImportDeclaration", - "CallExpression", - "TypeAnnotation", - "Decorator" - ], - "target_types": [ - "Symbol", - "Module", - "Class", - "Function", - "Method", - "Variable", - "Constant", - "Dependency", - "Parameter" - ] - }, - { - "constraints": [], - "description": "A repository or code unit depends on an external or internal dependency.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "DependsOn", - "source_types": [ - "Repository", - "SourceRoot", - "File", - "Module", - "ImportDeclaration", - "Dependency", - "Component" - ], - "target_types": [ - "Dependency", - "Module", - "Component", - "SecretRef" - ] - }, - { - "constraints": [], - "description": "Documentation describes a repository, source unit, or semantic declaration.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Documents", - "source_types": [ - "DocumentationSource", - "DocumentationChunk", - "Literal" - ], - "target_types": [ - "Repository", - "File", - "Module", - "Symbol", - "Class", - "Function", - "Method", - "Parameter", - "ReturnType", - "TypeAnnotation", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Decorator", - "Assignment", - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef" - ] - }, - { - "constraints": [], - "description": "A route or component dispatches to an endpoint or handler.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "RoutesTo", - "source_types": [ - "Route", - "APIEndpoint", - "Component" - ], - "target_types": [ - "APIEndpoint", - "Function", - "Method", - "Component" - ] - }, - { - "constraints": [], - "description": "A source unit exposes a public runtime or module surface.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Exposes", - "source_types": [ - "Repository", - "Module", - "Component", - "APIEndpoint", - "Route" - ], - "target_types": [ - "APIEndpoint", - "Route", - "Function", - "Method", - "Component", - "ExportDeclaration" - ] - }, - { - "constraints": [], - "description": "A code path executes or constructs a query.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "ExecutesQuery", - "source_types": [ - "Function", - "Method", - "CallExpression", - "APIEndpoint", - "Component" - ], - "target_types": [ - "Query" - ] - }, - { - "constraints": [], - "description": "A code path or dependency uses a secret or sensitive configuration value.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "UsesSecret", - "source_types": [ - "Function", - "Method", - "CallExpression", - "Component", - "APIEndpoint", - "Dependency" - ], - "target_types": [ - "SecretRef" - ] - }, - { - "constraints": [], - "description": "A code path raises or throws an exception flow.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Raises", - "source_types": [ - "Function", - "Method", - "CallExpression", - "ControlFlowBlock" - ], - "target_types": [ - "ExceptionFlow" - ] - }, - { - "constraints": [], - "description": "A code path handles or catches an exception flow.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "Handles", - "source_types": [ - "Function", - "Method", - "ControlFlowBlock", - "ExceptionFlow" - ], - "target_types": [ - "ExceptionFlow" - ] - }, - { - "constraints": [], - "description": "Ordered grammar parent-child structure between retained named syntax nodes.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "Named grammar field represented by this child relationship.", - "name": "field_name", - "required": false, - "type": "string" - }, - { - "description": "Zero-based child position among retained named siblings.", - "name": "child_index", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "SyntaxChild", - "source_types": [ - "SyntaxCapture" - ], - "target_types": [ - "SyntaxCapture" - ] - }, - { - "constraints": [], - "description": "A semantic node was derived from a raw parser capture.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "DerivedFrom", - "source_types": [ - "Symbol", - "Class", - "Function", - "Method", - "Parameter", - "ReturnType", - "TypeAnnotation", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Decorator", - "Assignment", - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef", - "CallExpression", - "Assignment", - "Reference", - "Literal", - "Expression", - "ControlFlowBlock", - "ExceptionFlow", - "Query", - "SecretRef", - "DocumentationSource", - "DocumentationChunk", - "Module", - "ImportDeclaration", - "ExportDeclaration" - ], - "target_types": [ - "SyntaxCapture" - ] - }, - { - "constraints": [], - "description": "A semantic claim is supported by parser, file, or documentation evidence.", - "fields": [ - { - "description": "Stable unique relation identifier.", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Relation subtype or evidence role.", - "name": "kind", - "required": false, - "type": "string" - }, - { - "description": "Source node id.", - "name": "source_id", - "required": true, - "type": "string" - }, - { - "description": "Target node id.", - "name": "target_id", - "required": true, - "type": "string" - }, - { - "description": "Extractor confidence between 0 and 1.", - "name": "confidence", - "required": false, - "type": "number" - }, - { - "description": "One-based evidence start line.", - "name": "line_start", - "required": false, - "type": "integer" - }, - { - "description": "One-based evidence end line.", - "name": "line_end", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence start byte.", - "name": "byte_start", - "required": false, - "type": "integer" - }, - { - "description": "Zero-based evidence end byte.", - "name": "byte_end", - "required": false, - "type": "integer" - }, - { - "description": "Structured relation evidence and resolver details.", - "name": "metadata", - "required": false, - "type": "json" - } - ], - "name": "EvidencedBy", - "source_types": [ - "Repository", - "File", - "Module", - "Symbol", - "Class", - "Function", - "Method", - "Parameter", - "ReturnType", - "TypeAnnotation", - "TypeAlias", - "Variable", - "Constant", - "ClassAttribute", - "InstanceAttribute", - "Property", - "Decorator", - "Assignment", - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef", - "CallExpression", - "Assignment", - "Reference", - "Literal", - "Expression", - "ControlFlowBlock", - "ExceptionFlow", - "Query", - "SecretRef", - "Dependency", - "DocumentationSource", - "DocumentationChunk" - ], - "target_types": [ - "SyntaxCapture", - "File", - "DocumentationChunk" - ] - } - ], - "search_indexes": [ - { - "fields": [ - "label", - "qualified_name", - "summary" - ], - "layer": "semantic", - "name": "idx_code_symbols", - "node_types": [ - "Symbol", - "Class", - "Function", - "Method", - "Variable", - "Constant" - ] - }, - { - "fields": [ - "label", - "path", - "summary" - ], - "layer": "semantic", - "name": "idx_source_units", - "node_types": [ - "Repository", - "SourceRoot", - "File", - "Module" - ] - }, - { - "fields": [ - "label", - "qualified_name", - "summary" - ], - "layer": "semantic", - "name": "idx_dependencies", - "node_types": [ - "ImportDeclaration", - "Dependency" - ] - }, - { - "fields": [ - "label", - "qualified_name", - "summary" - ], - "layer": "semantic", - "name": "idx_runtime_surface", - "node_types": [ - "APIEndpoint", - "Component", - "Route", - "Query", - "SecretRef" - ] - }, - { - "fields": [ - "label", - "path", - "summary" - ], - "layer": "semantic", - "name": "idx_docs", - "node_types": [ - "DocumentationSource", - "DocumentationChunk" - ] - }, - { - "fields": [ - "label", - "tree_sitter_node_type", - "text" - ], - "layer": "syntax", - "name": "idx_syntax_captures", - "node_types": [ - "SyntaxCapture" - ] - } - ], - "version": "1.1.0" -} +{"context_profiles":{"brief":{"description":"Smallest useful context: matched nodes plus direct defining file/module.","max_depth":1,"relations":["Contains","Defines","EvidencedBy"]},"callgraph":{"description":"Callable neighborhood for callers, callees, and call expressions.","max_depth":2,"relations":["Calls","References","ResolvesTo"]},"change_impact":{"description":"Context for likely downstream impact of changing a symbol, including documentation and evidence.","max_depth":3,"relations":["Defines","References","Calls","RoutesTo","ExecutesQuery","UsesSecret","DependsOn","Documents","EvidencedBy"]},"definitions":{"description":"Definition-oriented context for symbols and scopes.","max_depth":2,"relations":["Defines","Declares","HasScope","HasParameter","HasReturnType","HasTypeAnnotation"]},"dependencies":{"description":"Import and dependency context.","max_depth":2,"relations":["Imports","DependsOn","References","ResolvesTo"]},"docs":{"description":"Documentation context connected to code artifacts.","max_depth":1,"relations":["Documents","EvidencedBy"]},"runtime":{"description":"Runtime surface context for routes, endpoints, queries, and secrets.","max_depth":2,"relations":["RoutesTo","Exposes","ExecutesQuery","UsesSecret"]}},"node_types":[{"constraints":[],"description":"A version-controlled repository or source tree boundary.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Repository","parser_node_types":[]},{"constraints":[],"description":"A configured root scanned for source, docs, manifests, and generated evidence.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"SourceRoot","parser_node_types":[]},{"constraints":[],"description":"A source, manifest, configuration, or documentation file.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"},{"description":"Hash of file content at extraction time.","name":"content_hash","required":false,"type":"string"},{"description":"File size in bytes at extraction time.","name":"size_bytes","required":false,"type":"integer"}],"name":"File","parser_node_types":[]},{"constraints":[],"description":"A language-level compilation or namespace unit derived from a source file.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Module","parser_node_types":["module","program","source_file","Module"]},{"constraints":[],"description":"An import/include/use/require declaration.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"},{"description":"Imported module, package, symbol, or path.","name":"imported_name","required":false,"type":"string"}],"name":"ImportDeclaration","parser_node_types":["import_statement","import_from_statement","import_declaration","Import","ImportFrom"]},{"constraints":[],"description":"An exported symbol or module boundary declaration.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"ExportDeclaration","parser_node_types":["export_statement","export_clause","export_declaration"]},{"constraints":[],"description":"A named code artifact when the exact semantic subtype is unresolved.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Symbol","parser_node_types":[]},{"constraints":[],"description":"A lexical or semantic boundary for name resolution.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Scope","parser_node_types":[]},{"constraints":[],"description":"A class, struct, trait, interface, enum class, or similar type container.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Class","parser_node_types":["class_definition","class_declaration","struct_item","ClassDef"]},{"constraints":[],"description":"A standalone function, lambda with stable name, or callable declaration.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Function","parser_node_types":["function_definition","function_declaration","FunctionDef"]},{"constraints":[],"description":"A function declared inside a class, trait, component, or object scope.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Method","parser_node_types":["method_definition","method_declaration","FunctionDef"]},{"constraints":[],"description":"A callable parameter.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Parameter","parser_node_types":["parameter","typed_parameter","arg"]},{"constraints":[],"description":"A callable return type annotation.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"ReturnType","parser_node_types":["return_type","returns"]},{"constraints":[],"description":"A type annotation attached to a symbol, parameter, assignment, or return value.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"TypeAnnotation","parser_node_types":["type","type_identifier","type_annotation","annotation"]},{"constraints":[],"description":"A named alias for a type expression.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"TypeAlias","parser_node_types":["type_alias","type_alias_declaration"]},{"constraints":[],"description":"A mutable or local named binding.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Variable","parser_node_types":["variable_declaration","Name"]},{"constraints":[],"description":"A named binding treated as stable or immutable by convention or syntax.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Constant","parser_node_types":[]},{"constraints":[],"description":"A class-level attribute or static field.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"ClassAttribute","parser_node_types":["AnnAssign","field_declaration"]},{"constraints":[],"description":"An instance-level attribute or field assignment.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"InstanceAttribute","parser_node_types":[]},{"constraints":[],"description":"A computed or decorated property exposed as an attribute.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Property","parser_node_types":[]},{"constraints":[],"description":"A decorator, annotation, macro, or attribute attached to a declaration.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Decorator","parser_node_types":[]},{"constraints":[],"description":"A call, constructor invocation, message send, or macro invocation.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"CallExpression","parser_node_types":["call","Call"]},{"constraints":[],"description":"An assignment, binding, or destructuring declaration.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Assignment","parser_node_types":["assignment","Assign","AnnAssign"]},{"constraints":[],"description":"A name or member reference that may resolve to another node.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Reference","parser_node_types":["identifier","Name"]},{"constraints":[],"description":"A literal value from source code.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Literal","parser_node_types":["string","integer","float","Constant"]},{"constraints":[],"description":"A non-literal expression worth preserving for context or reasoning.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Expression","parser_node_types":[]},{"constraints":[],"description":"A branch, loop, match, switch, or guard block.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"ControlFlowBlock","parser_node_types":[]},{"constraints":[],"description":"A raise, throw, try, catch, except, rescue, or finally flow unit.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"ExceptionFlow","parser_node_types":[]},{"constraints":[],"description":"A network, RPC, CLI, event, or message endpoint exposed by code.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"APIEndpoint","parser_node_types":[]},{"constraints":[],"description":"A UI, service, package, or runtime component represented in source.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Component","parser_node_types":[]},{"constraints":[],"description":"A route pattern, path binding, or router entry.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Route","parser_node_types":[]},{"constraints":[],"description":"A database, search, analytics, or graph query string/expression.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"Query","parser_node_types":[]},{"constraints":[],"description":"A reference to a secret, credential, token, key, or sensitive environment variable.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"SecretRef","parser_node_types":[]},{"constraints":[],"description":"An external package, library, framework, service, or runtime dependency.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"},{"description":"Declared version or version constraint.","name":"version","required":false,"type":"string"},{"description":"Dependency ecosystem such as pypi, npm, cargo, or go.","name":"ecosystem","required":false,"type":"string"}],"name":"Dependency","parser_node_types":[]},{"constraints":[],"description":"A documentation file or generated documentation artifact.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"DocumentationSource","parser_node_types":[]},{"constraints":[],"description":"A chunk or heading-level section of documentation.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"}],"name":"DocumentationChunk","parser_node_types":[]},{"constraints":[],"description":"Raw parser evidence preserving the concrete syntax node and capture name.","fields":[{"description":"Stable unique node identifier.","name":"id","required":true,"type":"string"},{"description":"Short human-readable node label.","name":"label","required":true,"type":"string"},{"description":"Ontology-specific subtype or parser-derived role.","name":"kind","required":false,"type":"string"},{"description":"Source language when the node is code-derived.","name":"language","required":false,"type":"string"},{"description":"Versioned grammar identity that produced this syntax node.","name":"grammar_version","required":false,"type":"string"},{"description":"Repository-relative source path.","name":"path","required":false,"type":"string"},{"description":"Best-effort language-neutral qualified name.","name":"qualified_name","required":false,"type":"string"},{"description":"Containing lexical or semantic scope id.","name":"scope_id","required":false,"type":"string"},{"description":"One-based start line in the source file.","name":"line_start","required":false,"type":"integer"},{"description":"One-based end line in the source file.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based start byte in the source file.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based end byte in the source file.","name":"byte_end","required":false,"type":"integer"},{"description":"Raw parser node type that produced this node.","name":"tree_sitter_node_type","required":false,"type":"string"},{"description":"Tree-sitter query capture name when available.","name":"capture_name","required":false,"type":"string"},{"description":"Compact text summary used for context assembly.","name":"summary","required":false,"type":"string"},{"description":"Structured extractor-specific details.","name":"metadata","required":false,"type":"json"},{"description":"Optional S-expression or compact parse-tree fragment.","name":"sexp","required":false,"type":"string"},{"description":"Optional source text captured for this syntax node.","name":"text","required":false,"type":"string"}],"name":"SyntaxCapture","parser_node_types":[]}],"ontology":"code_ontology_v1","parser_node_mappings":[{"captures":["module","source_file"],"context_rule":"","description":"Create one Module node per parser root or language namespace root.","name":"module","parser_node_types":["module","program","source_file","Module"],"relation_types":["Contains","Defines","DerivedFrom"],"target_node_types":["Module"]},{"captures":["import","reference.import","reference.include","reference.require","reference.use"],"context_rule":"","description":"Normalize import-like declarations across languages and attach imported names as metadata.","name":"imports","parser_node_types":["import_statement","import_from_statement","import_declaration","Import","ImportFrom"],"relation_types":["Imports","DependsOn","DerivedFrom"],"target_node_types":["ImportDeclaration"]},{"captures":["export","definition.export"],"context_rule":"","description":"Capture public export declarations and declarations marked as exported.","name":"exports","parser_node_types":["export_statement","export_clause","export_declaration"],"relation_types":["Exports","DerivedFrom"],"target_node_types":["ExportDeclaration"]},{"captures":["definition.class","definition.struct","definition.interface"],"context_rule":"","description":"Map class-like containers to Class nodes with a child Scope.","name":"classes","parser_node_types":["class_definition","class_declaration","struct_item","interface_declaration","ClassDef"],"relation_types":["Defines","Declares","HasScope","DecoratedBy","DerivedFrom"],"target_node_types":["Class"]},{"captures":["definition.function","definition.method"],"context_rule":"enclosing Class or Component changes the target node from Function to Method","description":"Create Function for module-level callables and Method when the callable is enclosed by Class or Component.","name":"functions_and_methods","parser_node_types":["function_definition","function_declaration","method_definition","method_declaration","FunctionDef"],"relation_types":["Defines","Declares","HasScope","HasParameter","HasReturnType","DecoratedBy","DerivedFrom"],"target_node_types":["Function","Method"]},{"captures":["definition.parameter","parameter"],"context_rule":"","description":"Create Parameter nodes for callable parameter declarations.","name":"parameters","parser_node_types":["parameter","typed_parameter","default_parameter","arg"],"relation_types":["HasParameter","HasTypeAnnotation","DerivedFrom"],"target_node_types":["Parameter"]},{"captures":["type.return","return_type"],"context_rule":"","description":"Capture explicit return type annotations.","name":"return_types","parser_node_types":["return_type","type","type_identifier","returns"],"relation_types":["HasReturnType","HasTypeAnnotation","References","DerivedFrom"],"target_node_types":["ReturnType"]},{"captures":["type","type.annotation","reference.type"],"context_rule":"","description":"Capture type annotation expressions attached to declarations.","name":"type_annotations","parser_node_types":["type","type_identifier","type_annotation","annotation","Name"],"relation_types":["HasTypeAnnotation","References","ResolvesTo","DerivedFrom"],"target_node_types":["TypeAnnotation"]},{"captures":["definition.type_alias"],"context_rule":"","description":"Capture named type aliases.","name":"type_aliases","parser_node_types":["type_alias","type_alias_declaration"],"relation_types":["Defines","HasTypeAnnotation","DerivedFrom"],"target_node_types":["TypeAlias"]},{"captures":["definition.variable","definition.constant","assignment"],"context_rule":"","description":"Normalize assignments; scope, naming convention, and receiver decide variable, constant, or attribute node type.","name":"assignments","parser_node_types":["assignment","assignment_expression","variable_declaration","Assign","AnnAssign"],"relation_types":["Defines","Declares","Assigns","HasTypeAnnotation","DerivedFrom"],"target_node_types":["Assignment","Variable","Constant","ClassAttribute","InstanceAttribute","Property"]},{"captures":["decorator","definition.decorator"],"context_rule":"","description":"Capture decorators, annotations, macros, or framework markers that modify declarations.","name":"decorators","parser_node_types":["decorator","attribute_item","annotation","Call","Name"],"relation_types":["DecoratedBy","Calls","References","DerivedFrom"],"target_node_types":["Decorator"]},{"captures":["reference.call","call"],"context_rule":"","description":"Create call-expression nodes and optionally resolve them to callable targets.","name":"calls","parser_node_types":["call","call_expression","invocation_expression","Call"],"relation_types":["Calls","References","ResolvesTo","DerivedFrom"],"target_node_types":["CallExpression"]},{"captures":["reference","reference.identifier","reference.member"],"context_rule":"","description":"Capture name and member references before or after semantic resolution.","name":"references","parser_node_types":["identifier","field_identifier","attribute","Name","Attribute"],"relation_types":["References","ResolvesTo","DerivedFrom"],"target_node_types":["Reference"]},{"captures":["literal","string","number"],"context_rule":"","description":"Capture literals that are useful for docs, routes, queries, secrets, or assignment values.","name":"literals","parser_node_types":["string","integer","float","true","false","null","none","Constant"],"relation_types":["Contains","References","DerivedFrom"],"target_node_types":["Literal"]},{"captures":["control_flow"],"context_rule":"","description":"Capture branch and loop blocks when they affect reasoning or dependency paths.","name":"control_flow","parser_node_types":["if_statement","for_statement","while_statement","match_statement","switch_statement"],"relation_types":["Contains","References","DerivedFrom"],"target_node_types":["ControlFlowBlock"]},{"captures":["exception","raises","handles"],"context_rule":"","description":"Capture exception raising and handling paths.","name":"exception_flow","parser_node_types":["try_statement","except_clause","catch_clause","raise_statement","throw_statement"],"relation_types":["Raises","Handles","DerivedFrom"],"target_node_types":["ExceptionFlow"]},{"captures":["entrypoint.api","route","endpoint"],"context_rule":"","description":"Create APIEndpoint and Route nodes from framework route declarations or decorated handlers.","name":"routes_and_endpoints","parser_node_types":["decorator","call","route_declaration","handler_definition"],"relation_types":["RoutesTo","Exposes","DecoratedBy","DerivedFrom"],"target_node_types":["APIEndpoint","Route"]},{"captures":["definition.component","component"],"context_rule":"","description":"Capture UI, service, runtime, or package components when extractor rules identify them.","name":"components","parser_node_types":["class_definition","function_definition","jsx_element","component_declaration"],"relation_types":["Defines","Contains","Exposes","DerivedFrom"],"target_node_types":["Component"]},{"captures":["query.sql","query.graph","query.search"],"context_rule":"","description":"Capture query strings or query builder expressions.","name":"queries","parser_node_types":["string","template_string","call","Call"],"relation_types":["ExecutesQuery","References","DerivedFrom"],"target_node_types":["Query"]},{"captures":["secret","secret.env","secret.ref"],"context_rule":"","description":"Capture secret-looking names, environment references, keys, and credential handles.","name":"secrets","parser_node_types":["identifier","string","attribute","Name","Constant"],"relation_types":["UsesSecret","References","DerivedFrom"],"target_node_types":["SecretRef"]},{"captures":["doc","doc.string","doc.comment"],"context_rule":"","description":"Capture documentation sources and chunks from docs, comments, and docstrings.","name":"documentation","parser_node_types":["comment","string","docstring","DocumentationSource","DocumentationChunk"],"relation_types":["Documents","EvidencedBy"],"target_node_types":["DocumentationSource","DocumentationChunk"]}],"query_helpers":[{"description":"List high-level source roots, files, modules, dependencies, and runtime surfaces.","name":"repository_overview","parameters":[],"query":"MATCH (n) WHERE n:SourceRoot OR n:File OR n:Module OR n:Dependency OR n:APIEndpoint OR n:Component RETURN n.id, n.label, n.path LIMIT 100","returns":["id","label","path"]},{"description":"Find concrete semantic definitions by label or qualified name.","name":"symbol_lookup","parameters":["name"],"query":"MATCH (s:Class) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Function) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Method) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Variable) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Constant) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:ClassAttribute) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:InstanceAttribute) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Property) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:Parameter) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path UNION ALL MATCH (s:TypeAlias) WHERE s.label = $name OR s.qualified_name = $name RETURN s.id, s.label, s.qualified_name, s.path LIMIT 25","returns":["id","label","qualified_name","path"]},{"description":"Find a named class, function, method, variable, or constant definition.","name":"definition_context","parameters":[],"query":"MATCH (d) WHERE d:Class OR d:Function OR d:Method OR d:Variable OR d:Constant RETURN d.id, d.label, d.kind, d.path LIMIT 50","returns":["id","label","kind","path"]},{"description":"Find call expressions and resolved callable targets near a symbol.","name":"callgraph_neighborhood","parameters":[],"query":"MATCH (c:CallExpression)-[:FROM_ResolvesTo]->(:ResolvesTo)-[:TO_ResolvesTo]->(target) RETURN c.id, c.path, target.id, target.label LIMIT 50","returns":["call_id","path","target_id","target_label"]},{"description":"Inspect imports and dependencies.","name":"dependency_map","parameters":[],"query":"MATCH (i:ImportDeclaration)-[:FROM_DependsOn]->(:DependsOn)-[:TO_DependsOn]->(d:Dependency) RETURN i.id, i.label, d.id, d.label LIMIT 100","returns":["import_id","import_label","dependency_id","dependency_label"]},{"description":"Inspect routes, endpoints, executed queries, and secret use.","name":"runtime_surface","parameters":[],"query":"MATCH (r:Route)-[:FROM_RoutesTo]->(:RoutesTo)-[:TO_RoutesTo]->(e:APIEndpoint) RETURN r.id, r.label, e.id, e.label LIMIT 100","returns":["route_id","route_label","endpoint_id","endpoint_label"]},{"description":"Find documentation chunks connected to code nodes.","name":"documentation_context","parameters":[],"query":"MATCH (d:DocumentationChunk)-[:FROM_Documents]->(:Documents)-[:TO_Documents]->(n) RETURN d.id, d.label, n.id, n.label LIMIT 50","returns":["doc_id","doc_label","node_id","node_label"]},{"description":"Find references that have not been resolved to a semantic target.","name":"unresolved_references","parameters":[],"query":"MATCH (r:Reference) WHERE NOT EXISTS { MATCH (r)-[:FROM_ResolvesTo]->(:ResolvesTo)-[:TO_ResolvesTo]->() } RETURN r.id, r.label, r.path, r.line_start LIMIT 100","returns":["id","label","path","line_start"]}],"relation_types":[{"constraints":[],"description":"Structural containment between repository, files, scopes, declarations, and syntax-derived units.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Contains","source_types":["Repository","SourceRoot","File","Module","Scope","Class","Function","Method","Component"],"target_types":["SourceRoot","File","Module","Scope","ImportDeclaration","ExportDeclaration","Symbol","Class","Function","Method","Parameter","ReturnType","TypeAnnotation","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Decorator","Assignment","APIEndpoint","Component","Route","Query","SecretRef","CallExpression","Assignment","Reference","Literal","Expression","ControlFlowBlock","ExceptionFlow","Query","SecretRef","DocumentationSource","DocumentationChunk"]},{"constraints":[],"description":"A file, module, scope, or component defines a semantic code node.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Defines","source_types":["File","Module","Scope","Class","Function","Method","Component"],"target_types":["Symbol","Class","Function","Method","Parameter","ReturnType","TypeAnnotation","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Decorator","Assignment","APIEndpoint","Component","Route","Query","SecretRef"]},{"constraints":[],"description":"A source unit imports, includes, requires, or uses another unit.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Imports","source_types":["File","Module","Scope"],"target_types":["ImportDeclaration","Dependency","Module","Symbol"]},{"constraints":[],"description":"A source unit exports a declaration or public surface.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Exports","source_types":["File","Module","Scope","Component"],"target_types":["ExportDeclaration","Symbol","Class","Function","Method","Parameter","ReturnType","TypeAnnotation","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Decorator","Assignment","APIEndpoint","Component","Route","Query","SecretRef"]},{"constraints":[],"description":"A declaration site introduces a named symbol or subordinate declaration.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Declares","source_types":["Module","Scope","Class","Function","Method","Assignment"],"target_types":["Symbol","Class","Function","Method","Parameter","ReturnType","TypeAnnotation","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Decorator","Assignment","APIEndpoint","Component","Route","Query","SecretRef"]},{"constraints":[],"description":"Connects a node to the lexical or semantic scope used for resolution.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"HasScope","source_types":["File","Module","Symbol","Class","Function","Method","Parameter","ReturnType","TypeAnnotation","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Decorator","Assignment","APIEndpoint","Component","Route","Query","SecretRef","CallExpression","Assignment","Reference","Literal","Expression","ControlFlowBlock","ExceptionFlow","Query","SecretRef"],"target_types":["Scope"]},{"constraints":[],"description":"Connects callables, endpoints, routes, or calls to their parameters or arguments.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"HasParameter","source_types":["Function","Method","APIEndpoint","Route","CallExpression"],"target_types":["Parameter"]},{"constraints":[],"description":"Connects callables or endpoints to their return type node.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"HasReturnType","source_types":["Function","Method","APIEndpoint"],"target_types":["ReturnType"]},{"constraints":[],"description":"Connects a typed code node to its annotation expression.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"HasTypeAnnotation","source_types":["Symbol","Parameter","ReturnType","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute"],"target_types":["TypeAnnotation","Reference","Literal"]},{"constraints":[],"description":"Connects an assignment site or assigned symbol to the target or assigned value.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Assigns","source_types":["Assignment","Variable","Constant","ClassAttribute","InstanceAttribute","Property"],"target_types":["Variable","Constant","ClassAttribute","InstanceAttribute","Property","Literal","Expression","CallExpression"]},{"constraints":[],"description":"A source reference mentions another semantic node without necessarily resolving to it.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"References","source_types":["Reference","Expression","CallExpression","Assignment","ControlFlowBlock","TypeAnnotation","Decorator","Query","SecretRef"],"target_types":["Symbol","Class","Function","Method","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Parameter","Module","Dependency"]},{"constraints":[],"description":"A callable or call expression invokes another callable-like target.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Calls","source_types":["Function","Method","CallExpression","Decorator","APIEndpoint","Route","Component"],"target_types":["CallExpression","Function","Method","Class","APIEndpoint"]},{"constraints":[],"description":"A declaration is modified by a decorator, annotation, macro, or framework marker.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"DecoratedBy","source_types":["Class","Function","Method","Property","APIEndpoint","Route","Component"],"target_types":["Decorator","CallExpression","Reference"]},{"constraints":[],"description":"A resolver maps a syntactic reference to the best semantic target.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"ResolvesTo","source_types":["Reference","ImportDeclaration","CallExpression","TypeAnnotation","Decorator"],"target_types":["Symbol","Module","Class","Function","Method","Variable","Constant","Dependency","Parameter"]},{"constraints":[],"description":"A repository or code unit depends on an external or internal dependency.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"DependsOn","source_types":["Repository","SourceRoot","File","Module","ImportDeclaration","Dependency","Component"],"target_types":["Dependency","Module","Component","SecretRef"]},{"constraints":[],"description":"Documentation describes a repository, source unit, or semantic declaration.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Documents","source_types":["DocumentationSource","DocumentationChunk","Literal"],"target_types":["Repository","File","Module","Symbol","Class","Function","Method","Parameter","ReturnType","TypeAnnotation","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Decorator","Assignment","APIEndpoint","Component","Route","Query","SecretRef"]},{"constraints":[],"description":"A route or component dispatches to an endpoint or handler.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"RoutesTo","source_types":["Route","APIEndpoint","Component"],"target_types":["APIEndpoint","Function","Method","Component"]},{"constraints":[],"description":"A source unit exposes a public runtime or module surface.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Exposes","source_types":["Repository","Module","Component","APIEndpoint","Route"],"target_types":["APIEndpoint","Route","Function","Method","Component","ExportDeclaration"]},{"constraints":[],"description":"A code path executes or constructs a query.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"ExecutesQuery","source_types":["Function","Method","CallExpression","APIEndpoint","Component"],"target_types":["Query"]},{"constraints":[],"description":"A code path or dependency uses a secret or sensitive configuration value.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"UsesSecret","source_types":["Function","Method","CallExpression","Component","APIEndpoint","Dependency"],"target_types":["SecretRef"]},{"constraints":[],"description":"A code path raises or throws an exception flow.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Raises","source_types":["Function","Method","CallExpression","ControlFlowBlock"],"target_types":["ExceptionFlow"]},{"constraints":[],"description":"A code path handles or catches an exception flow.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"Handles","source_types":["Function","Method","ControlFlowBlock","ExceptionFlow"],"target_types":["ExceptionFlow"]},{"constraints":[],"description":"Ordered grammar parent-child structure between retained named syntax nodes.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"Named grammar field represented by this child relationship.","name":"field_name","required":false,"type":"string"},{"description":"Zero-based child position among retained named siblings.","name":"child_index","required":false,"type":"integer"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"SyntaxChild","source_types":["SyntaxCapture"],"target_types":["SyntaxCapture"]},{"constraints":[],"description":"A semantic node was derived from a raw parser capture.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"DerivedFrom","source_types":["Symbol","Class","Function","Method","Parameter","ReturnType","TypeAnnotation","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Decorator","Assignment","APIEndpoint","Component","Route","Query","SecretRef","CallExpression","Assignment","Reference","Literal","Expression","ControlFlowBlock","ExceptionFlow","Query","SecretRef","DocumentationSource","DocumentationChunk","Module","ImportDeclaration","ExportDeclaration"],"target_types":["SyntaxCapture"]},{"constraints":[],"description":"A semantic claim is supported by parser, file, or documentation evidence.","fields":[{"description":"Stable unique relation identifier.","name":"id","required":true,"type":"string"},{"description":"Relation subtype or evidence role.","name":"kind","required":false,"type":"string"},{"description":"Source node id.","name":"source_id","required":true,"type":"string"},{"description":"Target node id.","name":"target_id","required":true,"type":"string"},{"description":"Extractor confidence between 0 and 1.","name":"confidence","required":false,"type":"number"},{"description":"One-based evidence start line.","name":"line_start","required":false,"type":"integer"},{"description":"One-based evidence end line.","name":"line_end","required":false,"type":"integer"},{"description":"Zero-based evidence start byte.","name":"byte_start","required":false,"type":"integer"},{"description":"Zero-based evidence end byte.","name":"byte_end","required":false,"type":"integer"},{"description":"Structured relation evidence and resolver details.","name":"metadata","required":false,"type":"json"}],"name":"EvidencedBy","source_types":["Repository","File","Module","Symbol","Class","Function","Method","Parameter","ReturnType","TypeAnnotation","TypeAlias","Variable","Constant","ClassAttribute","InstanceAttribute","Property","Decorator","Assignment","APIEndpoint","Component","Route","Query","SecretRef","CallExpression","Assignment","Reference","Literal","Expression","ControlFlowBlock","ExceptionFlow","Query","SecretRef","Dependency","DocumentationSource","DocumentationChunk"],"target_types":["SyntaxCapture","File","DocumentationChunk"]}],"search_indexes":[{"fields":["label","qualified_name","summary"],"layer":"semantic","name":"idx_code_symbols","node_types":["Symbol","Class","Function","Method","Variable","Constant"]},{"fields":["label","path","summary"],"layer":"semantic","name":"idx_source_units","node_types":["Repository","SourceRoot","File","Module"]},{"fields":["label","qualified_name","summary"],"layer":"semantic","name":"idx_dependencies","node_types":["ImportDeclaration","Dependency"]},{"fields":["label","qualified_name","summary"],"layer":"semantic","name":"idx_runtime_surface","node_types":["APIEndpoint","Component","Route","Query","SecretRef"]},{"fields":["label","path","summary"],"layer":"semantic","name":"idx_docs","node_types":["DocumentationSource","DocumentationChunk"]},{"fields":["label","tree_sitter_node_type","text"],"layer":"syntax","name":"idx_syntax_captures","node_types":["SyntaxCapture"]}],"version":"1.1.0"} diff --git a/assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz deleted file mode 100644 index 9f229c4c..00000000 Binary files a/assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz and /dev/null differ diff --git a/assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz deleted file mode 100644 index dd06f40d..00000000 Binary files a/assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz and /dev/null differ diff --git a/assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz deleted file mode 100644 index 0c7fb61c..00000000 Binary files a/assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz and /dev/null differ diff --git a/assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz deleted file mode 100644 index 85142639..00000000 Binary files a/assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz and /dev/null differ diff --git a/assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz deleted file mode 100644 index 7d746ecc..00000000 Binary files a/assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz and /dev/null differ diff --git a/assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz deleted file mode 100644 index 500e2ed6..00000000 Binary files a/assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz and /dev/null differ diff --git a/assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz index 9c0eafca..727844e7 100644 Binary files a/assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz and b/assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz differ diff --git a/assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz index e4ad47d8..8acf0d1d 100644 Binary files a/assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz and b/assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz differ diff --git a/assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz index d08f422d..a3255228 100644 Binary files a/assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz and b/assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz differ diff --git a/assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz b/assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz index 8af3ba95..750571aa 100644 Binary files a/assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz and b/assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz differ diff --git a/build.rs b/build.rs index 6372e4e2..5fad76c7 100644 --- a/build.rs +++ b/build.rs @@ -26,12 +26,6 @@ fn main() { println!("cargo:rustc-link-search=native={}", path.display()); } } - - if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") - && std::env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("x86_64") - { - link_macos_x86_compiler_runtime(); - } } fn build_tree_sitter_wat() { @@ -58,30 +52,3 @@ fn build_tree_sitter_wat() { source_dir.join("tree_sitter/parser.h").display() ); } - -fn link_macos_x86_compiler_runtime() { - println!("cargo:rerun-if-env-changed=DEVELOPER_DIR"); - let output = std::process::Command::new("xcrun") - .args(["clang", "--print-resource-dir"]) - .output() - .expect("macOS x86_64 builds require xcrun and clang"); - assert!( - output.status.success(), - "xcrun clang --print-resource-dir failed with {}", - output.status - ); - - let resource_dir = - String::from_utf8(output.stdout).expect("clang resource directory must be valid UTF-8"); - let library_dir = std::path::Path::new(resource_dir.trim()) - .join("lib") - .join("darwin"); - let runtime = library_dir.join("libclang_rt.osx.a"); - assert!( - runtime.is_file(), - "macOS compiler runtime not found at {}", - runtime.display() - ); - println!("cargo:rustc-link-search=native={}", library_dir.display()); - println!("cargo:rustc-link-lib=static=clang_rt.osx"); -} diff --git a/conda-forge/recipe/meta.yaml b/conda-forge/recipe/meta.yaml index 9773cb01..aac03ada 100644 --- a/conda-forge/recipe/meta.yaml +++ b/conda-forge/recipe/meta.yaml @@ -11,6 +11,7 @@ source: build: number: 0 + skip: true # [not ((osx and arm64) or (win64 and not arm64))] script: - cargo build --locked --release --bin codebase-graph - mkdir -p "${PREFIX}/bin" diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index 84cd2466..330237b3 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -13,31 +13,23 @@ use std::process::{Command, Stdio}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use yaml_serde::Value as YamlValue; +mod native_tests; mod release; const CONFIRMATIONS: &[&str] = &["release-environment", "private-vulnerability-reporting"]; const CRATES_IO_PACKAGE_LIMIT_BYTES: u64 = 10 * 1024 * 1024; -const NATIVE_TARGETS: [NativeTarget; 4] = [ - NativeTarget::LinuxX86_64, - NativeTarget::MacosArm64, - NativeTarget::MacosX86_64, - NativeTarget::WindowsX86_64, -]; +const NATIVE_TARGETS: [NativeTarget; 2] = [NativeTarget::MacosArm64, NativeTarget::WindowsX86_64]; #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] enum NativeTarget { - LinuxX86_64, MacosArm64, - MacosX86_64, WindowsX86_64, } impl NativeTarget { fn parse(value: &str) -> Result { match value { - "linux-x86_64" => Ok(Self::LinuxX86_64), "macos-arm64" => Ok(Self::MacosArm64), - "macos-x86_64" => Ok(Self::MacosX86_64), "windows-x86_64" => Ok(Self::WindowsX86_64), other => Err(format!("unsupported native target: {other}")), } @@ -45,9 +37,7 @@ impl NativeTarget { fn as_str(self) -> &'static str { match self { - Self::LinuxX86_64 => "linux-x86_64", Self::MacosArm64 => "macos-arm64", - Self::MacosX86_64 => "macos-x86_64", Self::WindowsX86_64 => "windows-x86_64", } } @@ -61,9 +51,7 @@ impl NativeTarget { fn host(self) -> (&'static str, &'static str) { match self { - Self::LinuxX86_64 => ("linux", "x86_64"), Self::MacosArm64 => ("macos", "aarch64"), - Self::MacosX86_64 => ("macos", "x86_64"), Self::WindowsX86_64 => ("windows", "x86_64"), } } @@ -160,22 +148,118 @@ fn validate_crate_size(bytes: u64) -> Result<(), String> { } fn native_test(args: Vec) -> Result<(), String> { - let options = parse_options(&args, &["--target"])?; - let target = NativeTarget::parse(required_option(&options, "--target")?)?; - ensure_native_host(target)?; - let command_args = native_test_command(target); - let mut command = Command::new("cargo"); - command.args(command_args); - run_command(&mut command, "native test") + native_tests::run(args) +} + +fn native_test_capture_command(target: NativeTarget) -> Vec<&'static str> { + let mut args = native_test_command(target); + args.extend(["--no-run", "--message-format=json-render-diagnostics"]); + args +} + +fn record_native_test_cli_artifact( + message: &Value, + manifest: &Path, + candidates: &mut BTreeSet, +) -> Result<(), String> { + if message["reason"] != "compiler-artifact" + || message["target"]["name"] != "codebase-graph" + || message["profile"]["test"] != false + || !message["target"]["kind"] + .as_array() + .is_some_and(|kinds| kinds.iter().any(|kind| kind == "bin")) + { + return Ok(()); + } + let actual_manifest = message["manifest_path"] + .as_str() + .ok_or("native-test CLI artifact has no source manifest")?; + if fs::canonicalize(actual_manifest).map_err(|error| error.to_string())? != manifest { + return Ok(()); + } + let executable = message["executable"] + .as_str() + .ok_or("native-test normal CLI artifact has no executable")?; + let executable = fs::canonicalize(executable).map_err(|error| error.to_string())?; + if !executable.is_file() { + return Err("native-test CLI artifact is not a file".into()); + } + candidates.insert(executable); + Ok(()) +} + +fn unique_native_test_cli(candidates: BTreeSet) -> Result { + if candidates.len() != 1 { + return Err(format!( + "expected one source-bound normal codebase-graph CLI compiler artifact, got {}", + candidates.len() + )); + } + Ok(candidates + .into_iter() + .next() + .expect("one captured executable")) +} + +fn capture_native_test_cli(target: NativeTarget) -> Result { + let manifest = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../Cargo.toml"); + let manifest = fs::canonicalize(manifest).map_err(|error| error.to_string())?; + let mut child = Command::new("cargo") + .args(native_test_capture_command(target)) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .map_err(|error| format!("failed to capture native-test CLI: {error}"))?; + let stdout = child.stdout.take().expect("capture stdout was piped"); + let mut candidates = BTreeSet::new(); + let mut failure = None; + for line in BufReader::new(stdout).lines() { + let line = match line { + Ok(line) => line, + Err(error) => { + failure = Some(error.to_string()); + break; + } + }; + match serde_json::from_str::(&line) { + Ok(message) => { + if let Some(rendered) = message["message"]["rendered"].as_str() { + eprint!("{rendered}"); + } + if let Err(error) = + record_native_test_cli_artifact(&message, &manifest, &mut candidates) + { + failure.get_or_insert(error); + } + } + Err(error) => { + failure.get_or_insert(format!("invalid native-test compiler message: {error}")); + } + } + } + let status = child.wait().map_err(|error| error.to_string())?; + if !status.success() { + return Err(format!( + "native-test artifact capture failed with status {status}" + )); + } + if let Some(error) = failure { + return Err(error); + } + unique_native_test_cli(candidates) } fn native_test_command(target: NativeTarget) -> Vec<&'static str> { - let mut args = vec!["test", "--workspace", "--locked"]; + let mut args = vec![ + "test", + "--workspace", + "--locked", + "--profile", + "native-tests", + ]; if target.uses_windows_extensions() { // Keep optimized fault-test binaries separate from shipped artifacts. args.extend([ - "--profile", - "native-tests", "--features", "codebase-graph/bundled-windows-extensions,k-wiki/bundled-windows-extensions", ]); @@ -1037,6 +1121,43 @@ fn check_workflow_policy( "FAIL: workflow-native-reuse-missing: CI native job must call native.yml.".to_string(), ); } + let supported: BTreeSet = NATIVE_TARGETS + .iter() + .map(|t| t.as_str().to_owned()) + .collect(); + let ci_targets = yaml_path(ci, &["jobs", "native", "strategy", "matrix", "include"]) + .and_then(YamlValue::as_sequence); + let actual: BTreeSet = ci_targets + .into_iter() + .flatten() + .filter_map(|t| { + t.get("target") + .and_then(YamlValue::as_str) + .map(str::to_owned) + }) + .collect(); + if actual != supported + || ci_targets.is_none_or(|targets| { + targets.len() != supported.len() + || targets + .iter() + .any(|t| t.get("run-tests").and_then(YamlValue::as_bool) != Some(true)) + }) + { + issues.push( + "FAIL: native-target-support: CI must test exactly macos-arm64 and windows-x86_64." + .into(), + ); + } + if yaml_path( + release, + &["jobs", "rebuild-artifacts", "strategy", "matrix", "target"], + ) + .and_then(yaml_string_set) + != Some(supported) + { + issues.push("FAIL: native-target-support: release rebuilds must contain exactly macos-arm64 and windows-x86_64.".into()); + } let required_needs = yaml_path(ci, &["jobs", "required", "needs"]) .and_then(yaml_string_set) .unwrap_or_default(); @@ -1089,6 +1210,18 @@ fn check_workflow_policy( .to_string(), ); } + if yaml_path(native, &["jobs", "tests", "if"]).and_then(YamlValue::as_str) != Some("inputs.run-tests") + || yaml_path(native, &["jobs", "tests", "strategy", "fail-fast"]).and_then(YamlValue::as_bool) != Some(false) + || yaml_path(native, &["jobs", "tests", "strategy", "matrix", "cohort"]).and_then(yaml_string_set) + != Some(BTreeSet::from(["unit".to_string(), "generation".to_string(), "integration".to_string()])) + || yaml_path(native, &["jobs", "tests", "strategy", "matrix", "shard"]).and_then(YamlValue::as_str) + != Some("${{ fromJSON(inputs.target == 'windows-x86_64' && '[1,2,3,4]' || '[0]') }}") + || yaml_path(native, &["jobs", "native", "needs"]).and_then(YamlValue::as_str) != Some("tests") + || yaml_path(native, &["jobs", "native", "if"]).and_then(YamlValue::as_str) + != Some("${{ always() && !cancelled() && (!inputs.run-tests || needs.tests.result == 'success') }}") + { + issues.push("FAIL: native-test-gate: require all three macOS cohorts and successful tests before artifact production; retain build-only callers.".into()); + } if yaml_path(release, &["on", "push"]).is_some() { issues.push( @@ -2228,6 +2361,8 @@ mod tests { validate_crate_size, validate_native_artifact_set, workflow_yaml_error, NativeProvenance, NativeTarget, YamlValue, CRATES_IO_PACKAGE_LIMIT_BYTES, NATIVE_TARGETS, }; + use serde_json::{json, Value}; + use std::collections::BTreeSet; use std::fs; use std::path::Path; @@ -2244,7 +2379,7 @@ mod tests { #[test] fn native_target_contract_maps_names_and_binaries() { assert_eq!( - NativeTarget::parse("linux-x86_64").unwrap().binary_names(), + NativeTarget::parse("macos-arm64").unwrap().binary_names(), ("codebase-graph", "k-wiki") ); assert_eq!( @@ -2254,6 +2389,8 @@ mod tests { ("codebase-graph.exe", "k-wiki.exe") ); assert!(NativeTarget::parse("linux-arm64").is_err()); + assert!(NativeTarget::parse("linux-x86_64").is_err()); + assert!(NativeTarget::parse("macos-x86_64").is_err()); } #[test] @@ -2267,16 +2404,64 @@ mod tests { &"codebase-graph/bundled-windows-extensions,k-wiki/bundled-windows-extensions" )); - for target in [ - NativeTarget::LinuxX86_64, - NativeTarget::MacosArm64, - NativeTarget::MacosX86_64, - ] { - let unix = native_test_command(target); - assert!(!unix.contains(&"--features")); - assert!(!unix.contains(&"--release")); - assert!(!unix.contains(&"--profile")); + let macos = native_test_command(NativeTarget::MacosArm64); + assert!(!macos.contains(&"--features")); + assert!(!macos.contains(&"--release")); + assert!(macos.contains(&"--profile")); + } + + #[test] + fn native_capture_reuses_the_exact_test_profile_and_features() { + for target in super::NATIVE_TARGETS { + let test = native_test_command(target); + let capture = super::native_test_capture_command(target); + assert_eq!(&capture[..test.len()], test); + assert_eq!( + &capture[test.len()..], + ["--no-run", "--message-format=json-render-diagnostics"] + ); + } + } + + #[test] + fn native_capture_requires_unique_source_bound_normal_cli_artifact() { + let root = super::unique_temp_dir("native_cli_capture").unwrap(); + let manifest = root.join("Cargo.toml"); + let unrelated = root.join("other.toml"); + let executable = root.join("normal-cli"); + let second = root.join("second-cli"); + for path in [&manifest, &unrelated, &executable, &second] { + fs::write(path, b"fixture").unwrap(); } + let manifest = fs::canonicalize(manifest).unwrap(); + let mut candidates = BTreeSet::new(); + let valid = json!({"reason":"compiler-artifact","manifest_path":manifest, + "target":{"name":"codebase-graph","kind":["bin"]}, + "profile":{"test":false},"executable":executable}); + let mut harness = valid.clone(); + harness["profile"]["test"] = json!(true); + let mut wrong = valid.clone(); + wrong["manifest_path"] = json!(unrelated); + for message in [&harness, &wrong, &json!({"reason":"build-finished"})] { + super::record_native_test_cli_artifact(message, &manifest, &mut candidates).unwrap(); + } + assert!(super::unique_native_test_cli(candidates.clone()).is_err()); + super::record_native_test_cli_artifact(&valid, &manifest, &mut candidates).unwrap(); + super::record_native_test_cli_artifact(&valid, &manifest, &mut candidates).unwrap(); + assert_eq!( + super::unique_native_test_cli(candidates.clone()).unwrap(), + fs::canonicalize(&executable).unwrap() + ); + let mut missing = valid.clone(); + missing["executable"] = Value::Null; + assert!( + super::record_native_test_cli_artifact(&missing, &manifest, &mut candidates).is_err() + ); + let mut ambiguous = valid; + ambiguous["executable"] = json!(second); + super::record_native_test_cli_artifact(&ambiguous, &manifest, &mut candidates).unwrap(); + assert!(super::unique_native_test_cli(candidates).is_err()); + fs::remove_dir_all(root).unwrap(); } #[test] @@ -2303,10 +2488,10 @@ mod tests { fs::write(package.join(name), name).unwrap(); } let archive = temp.join("artifact.tar.gz"); - create_native_archive(&package, &archive, NativeTarget::LinuxX86_64).unwrap(); + create_native_archive(&package, &archive, NativeTarget::MacosArm64).unwrap(); let extracted = temp.join("extracted"); let error = - extract_and_validate_native_archive(&archive, NativeTarget::LinuxX86_64, &extracted) + extract_and_validate_native_archive(&archive, NativeTarget::MacosArm64, &extracted) .unwrap_err(); assert!(error.contains("checksums.txt") || error.contains("SHA256")); fs::remove_dir_all(temp).unwrap(); @@ -2322,7 +2507,7 @@ mod tests { } validate_native_artifact_set(&temp, version, commit_sha).unwrap(); - let provenance_path = temp.join("native-linux-x86_64/provenance.json"); + let provenance_path = temp.join("native-macos-arm64/provenance.json"); let mut provenance: NativeProvenance = serde_json::from_slice(&fs::read(&provenance_path).unwrap()).unwrap(); provenance.commit_sha = "ffffffffffffffffffffffffffffffffffffffff".to_string(); @@ -2380,17 +2565,91 @@ mod tests { } fn valid_ci_workflow() -> YamlValue { - yaml_serde::from_str( - "on:\n pull_request: {branches: [main]}\n push: {branches: [main]}\njobs:\n native: {uses: './.github/workflows/native.yml'}\n publish-dry-run:\n steps: [{run: 'cargo package --locked --no-verify && cargo run -p xtask -- verify-crate-size package.crate'}]\n required:\n if: '${{ always() }}'\n needs: [fmt, clippy, supply-chain, publish-dry-run, native]\n", + yaml_serde::from_str(include_str!("../../../.github/workflows/ci.yml")).unwrap() + } + + #[test] + fn workflows_reject_removed_platform_targets() { + for removed in ["linux-x86_64", "macos-x86_64"] { + let mut ci = valid_ci_workflow(); + ci["jobs"]["native"]["strategy"]["matrix"]["include"] + .as_sequence_mut() + .unwrap() + .push( + yaml_serde::from_str(&format!("target: {removed}\nrun-tests: true")).unwrap(), + ); + let mut issues = Vec::new(); + check_workflow_policy( + &ci, + &valid_native_workflow(), + &yaml_serde::from_str(&valid_release_workflow_text()).unwrap(), + &mut issues, + ); + assert!( + issues.iter().any(|s| s.contains("native-target-support")), + "{issues:?}" + ); + } + } + + #[cfg(unix)] + #[test] + fn installer_rejects_removed_hosts_before_reading_archives_or_writing_destinations() { + let root = super::unique_temp_dir("unsupported_installer_hosts").unwrap(); + let bin = root.join("bin"); + fs::create_dir(&bin).unwrap(); + let uname = bin.join("uname"); + let target = root.join("installed"); + let path = std::env::join_paths( + std::iter::once(bin.clone()) + .chain(std::env::split_paths(&std::env::var_os("PATH").unwrap())), ) - .unwrap() + .unwrap(); + let installer = + Path::new(env!("CARGO_MANIFEST_DIR")).join("../../release/install/install.sh"); + for system in ["Linux", "Darwin"] { + fs::write( + &uname, + format!("#!/bin/sh\ncase \"$1\" in -s) echo {system} ;; -m) echo x86_64 ;; esac\n"), + ) + .unwrap(); + super::set_executable(&uname).unwrap(); + let output = std::process::Command::new("bash") + .arg(&installer) + .arg("--bin-dir") + .arg(&target) + .arg("--dry-run") + .env("PATH", &path) + .output() + .unwrap(); + assert!(!output.status.success()); + assert!(String::from_utf8_lossy(&output.stderr).contains("Supported platforms")); + assert!(!target.exists()); + } + fs::remove_dir_all(root).unwrap(); } fn valid_native_workflow() -> YamlValue { - yaml_serde::from_str( - "on:\n workflow_call:\n inputs:\n target: {}\n source-sha: {}\n run-tests: {}\n upload-artifact: {}\n artifact-retention-days: {}\njobs:\n native:\n permissions: {contents: read}\n", - ) - .unwrap() + yaml_serde::from_str(include_str!("../../../.github/workflows/native.yml")).unwrap() + } + + #[test] + fn native_test_failure_cannot_authorize_artifact_production() { + for replacement in ["${{ always() }}", "${{ !inputs.run-tests }}"] { + let mut native = valid_native_workflow(); + native["jobs"]["native"]["if"] = YamlValue::String(replacement.into()); + let mut issues = Vec::new(); + check_workflow_policy( + &valid_ci_workflow(), + &native, + &yaml_serde::from_str(&valid_release_workflow_text()).unwrap(), + &mut issues, + ); + assert!( + issues.iter().any(|i| i.contains("native-test-gate")), + "{issues:?}" + ); + } } fn valid_release_workflow_text() -> String { diff --git a/crates/xtask/src/native_tests.rs b/crates/xtask/src/native_tests.rs new file mode 100644 index 00000000..5d0b2112 --- /dev/null +++ b/crates/xtask/src/native_tests.rs @@ -0,0 +1,426 @@ +use super::*; +use std::time::Instant; + +mod shards; +use shards::Shard; + +struct CapturedCli { + directory: PathBuf, + executable: PathBuf, +} +impl CapturedCli { + fn copy(source: &Path, name: &str) -> Result { + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| e.to_string())? + .as_nanos(); + let directory = + env::temp_dir().join(format!("native-test-cli-{}-{nanos}", std::process::id())); + fs::create_dir(&directory).map_err(|e| e.to_string())?; + let captured = Self { + executable: directory.join(name), + directory, + }; + fs::copy(source, &captured.executable).map_err(|e| e.to_string())?; + if sha256_file(source)? != sha256_file(&captured.executable)? { + return Err("captured native CLI copy differs from the compiler artifact".into()); + } + Ok(captured) + } +} +impl Drop for CapturedCli { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.directory); + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Cohort { + All, + Unit, + Generation, + Integration, +} +impl Cohort { + fn parse(value: &str) -> Result { + match value { + "all" => Ok(Self::All), + "unit" => Ok(Self::Unit), + "generation" => Ok(Self::Generation), + "integration" => Ok(Self::Integration), + _ => Err(format!("unsupported native test cohort: {value}")), + } + } + fn name(self) -> &'static str { + match self { + Self::All => "all", + Self::Unit => "unit", + Self::Generation => "generation", + Self::Integration => "integration", + } + } +} + +fn cohort(package: &str, kind: &str, name: &str) -> Option { + match kind { + "lib" | "bin" | "example" => Some(Cohort::Unit), + "test" if package == "codebase-graph" && name == "generation_storage" => { + Some(Cohort::Generation) + } + "test" | "doctest" => Some(Cohort::Integration), + _ => None, + } +} + +fn inventory(metadata: &Value) -> Result, String> { + let members = metadata["workspace_members"] + .as_array() + .ok_or("missing workspace members")?; + let mut inventory = Vec::new(); + for package in metadata["packages"].as_array().ok_or("missing packages")? { + if !members.contains(&package["id"]) { + continue; + } + let package_name = package["name"].as_str().ok_or("missing package name")?; + for target in package["targets"].as_array().ok_or("missing targets")? { + let name = target["name"].as_str().ok_or("missing target name")?; + for kind in target["kind"].as_array().ok_or("missing target kind")? { + let kind = kind.as_str().ok_or("invalid target kind")?; + let mut kinds = Vec::new(); + if target["test"] == true { + kinds.push(kind); + } + if target["doctest"] == true { + kinds.push("doctest"); + } + for kind in kinds { + if let Some(cohort) = cohort(package_name, kind, name) { + inventory.push(json!({"package":package_name,"target":name, + "kind":kind,"cohort":cohort.name()})); + } + } + } + } + } + Ok(inventory) +} + +fn windows_preflight(base: &[String]) -> Vec { + let mut regressions = base.to_vec(); + regressions.extend( + [ + "--lib", + "--", + "storage::locks::", + "maintenance_legacy_bootstrap::", + "dc_workspace_root_removal_restarts", + "managed_read_recovery_does_not_recreate", + "dc_generation_actual_", + "dc_generation_engine_actual_", + "maintenance_projection::tests::", + "busy_writer_is_reported", + "dc_s2_current_v2_receipt", + "dc_s5_canonical_only_real_producer_spare_key", + "dc_s5_artifact_use_lease_defers_obsolete_key_until_release", + "dc_s5_real_copied_orphan_staging_requires_confirmation_then_retires_without_source_edit", + "dc_s5_real_reader_lease_protects_predecessor_until_release", + ] + .map(str::to_owned), + ); + regressions +} + +fn commands(target: NativeTarget, selected: Cohort, inventory: &[Value]) -> Vec> { + let base: Vec = native_test_command(target) + .into_iter() + .map(str::to_owned) + .collect(); + if selected == Cohort::All { + let mut commands = Vec::new(); + if target == NativeTarget::WindowsX86_64 { + commands.push(windows_preflight(&base)); + } + let mut complete = base; + complete.push("--no-fail-fast".into()); + commands.push(complete); + return commands; + } + let mut commands = Vec::new(); + if selected == Cohort::Unit { + if target == NativeTarget::WindowsX86_64 { + commands.push(windows_preflight(&base)); + } + let mut command = base.clone(); + command.extend(["--lib", "--bins", "--examples"].map(str::to_owned)); + commands.push(command); + } else { + let mut packages: BTreeMap> = BTreeMap::new(); + for entry in inventory + .iter() + .filter(|e| e["cohort"] == selected.name() && e["kind"] == "test") + { + packages + .entry(entry["package"].as_str().unwrap().into()) + .or_default() + .insert(entry["target"].as_str().unwrap().into()); + } + for (package, tests) in packages { + let mut command: Vec = base + .iter() + .filter(|s| *s != "--workspace") + .cloned() + .collect(); + if target == NativeTarget::WindowsX86_64 { + let features = command.iter().position(|s| s == "--features").unwrap() + 1; + command[features] = format!("{package}/bundled-windows-extensions"); + } + command.extend(["--package".into(), package]); + for test in tests { + command.extend(["--test".into(), test]); + } + commands.push(command); + } + if selected == Cohort::Integration { + let mut command = base; + command.push("--doc".into()); + commands.push(command); + } + } + commands +} + +pub(super) fn run(args: Vec) -> Result<(), String> { + let options = parse_options(&args, &["--target", "--cohort", "--shard"])?; + let target = NativeTarget::parse(required_option(&options, "--target")?)?; + let selected = Cohort::parse(options.get("--cohort").map(String::as_str).unwrap_or("all"))?; + let shard = options + .get("--shard") + .map(|s| Shard::parse(s)) + .transpose()?; + if shard.is_some() && selected == Cohort::All { + return Err("--shard requires the unit, generation or integration cohort".into()); + } + ensure_native_host(target)?; + let suffix = shard + .map(|s| format!("-{}of{}", s.part, s.total)) + .unwrap_or_default(); + let output = PathBuf::from("diagnostics/tests").join(format!( + "{}-{}{suffix}", + target.as_str(), + selected.name() + )); + fs::create_dir_all(&output).map_err(|e| e.to_string())?; + let metadata = Command::new("cargo") + .args(["metadata", "--no-deps", "--locked", "--format-version", "1"]) + .output() + .map_err(|e| e.to_string())?; + if !metadata.status.success() { + return Err(String::from_utf8_lossy(&metadata.stderr).into_owned()); + } + let metadata: Value = serde_json::from_slice(&metadata.stdout).map_err(|e| e.to_string())?; + let inventory = inventory(&metadata)?; + let unsharded = commands(target, selected, &inventory); + let started = Instant::now(); + let compiler_artifact = capture_native_test_cli(target)?; + let captured = CapturedCli::copy(&compiler_artifact, target.binary_names().0)?; + let executable = captured.executable.as_path(); + let hash = sha256_file(executable)?; + println!( + "native-test CLI compiler artifact: {} sha256={hash}", + executable.display() + ); + let commands = if let Some(shard) = shard { + let mut planned = Vec::new(); + for (index, args) in unsharded.iter().enumerate() { + // Keep the existing focused preflight once, and all doctests once. + if args.iter().any(|a| a == "--" || a == "--doc") { + if shard.part == 1 { + planned.push(args.clone()); + } + continue; + } + let listed = list_tests(args, &["--list"], executable)?; + fs::write( + output.join(format!("source-{index}-inventory.txt")), + &listed, + ) + .map_err(|e| e.to_string())?; + fs::write( + output.join(format!("source-{index}-ignored.txt")), + list_tests(args, &["--list", "--ignored"], executable)?, + ) + .map_err(|e| e.to_string())?; + let listing = std::str::from_utf8(&listed).map_err(|e| e.to_string())?; + let (cases, chunks) = shard.commands(args, listing)?; + fs::write( + output.join(format!("source-{index}-selection.json")), + serde_json::to_vec_pretty(&json!({"part":shard.part,"total":shard.total, + "selected_cases":cases,"commands":chunks})) + .unwrap(), + ) + .map_err(|e| e.to_string())?; + planned.extend(chunks); + } + planned + } else { + unsharded.clone() + }; + let mut report = json!({"source_sha":env::var("SOURCE_SHA").ok(),"target":target.as_str(), + "cohort":selected.name(),"cli":executable,"compiler_artifact":compiler_artifact,"cli_sha256":hash,"inventory":inventory, + "shard":shard.map(|s| json!({"part":s.part,"total":s.total})),"unsharded_commands":unsharded, + "commands":commands,"executions":[],"capture_seconds":started.elapsed().as_secs_f64()}); + let save = |report: &Value| { + fs::write( + output.join("report.json"), + serde_json::to_vec_pretty(report).unwrap(), + ) + .map_err(|e| e.to_string()) + }; + save(&report)?; + let mut failures = Vec::new(); + for (index, args) in commands.iter().enumerate() { + for (label, extra) in [ + ("inventory", vec!["--list"]), + ("ignored", vec!["--list", "--ignored"]), + ] { + fs::write( + output.join(format!("{index}-{label}.txt")), + list_tests(args, &extra, executable)?, + ) + .map_err(|e| e.to_string())?; + } + println!( + "native test command {}: cargo {}", + index + 1, + args.join(" ") + ); + let now = Instant::now(); + let result = run_command( + Command::new("cargo") + .args(args) + .env("CARGO_BIN_EXE_codebase-graph", executable), + "native test", + ); + let seconds = now.elapsed().as_secs_f64(); + println!( + "native test command {} finished in {seconds:.3}s: {result:?}", + index + 1 + ); + report["executions"] + .as_array_mut() + .unwrap() + .push(json!({"command":index,"seconds":seconds,"error":result.as_ref().err()})); + save(&report)?; + if let Err(error) = result { + failures.push(error); + if target == NativeTarget::WindowsX86_64 + && matches!(selected, Cohort::All | Cohort::Unit) + && index == 0 + && unsharded.first() == Some(args) + { + break; + } + } + } + report["total_seconds"] = json!(started.elapsed().as_secs_f64()); + report["cli_unchanged"] = json!(sha256_file(executable).is_ok_and(|h| h == hash)); + save(&report)?; + if report["cli_unchanged"] != true { + return Err("captured native-test CLI changed during execution".into()); + } + if failures.is_empty() { + Ok(()) + } else { + Err(failures.join("\n")) + } +} + +fn list_tests(args: &[String], extra: &[&str], executable: &Path) -> Result, String> { + let mut command = Command::new("cargo"); + command.args(args); + if !args.iter().any(|a| a == "--") { + command.arg("--"); + } + let listing = command + .args(extra) + .env("CARGO_BIN_EXE_codebase-graph", executable) + .output() + .map_err(|e| e.to_string())?; + if !listing.status.success() { + return Err(format!( + "native test listing failed: {}", + String::from_utf8_lossy(&listing.stderr) + )); + } + Ok(listing.stdout) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn captured_cli_survives_cargo_replacing_its_output() { + let root = unique_temp_dir("native-cli-copy-test").unwrap(); + let source = root.join("original"); + fs::write(&source, b"compiler artifact").unwrap(); + let captured = CapturedCli::copy(&source, "captured").unwrap(); + let directory = captured.directory.clone(); + fs::write(source, b"later Cargo output").unwrap(); + assert_eq!( + fs::read(&captured.executable).unwrap(), + b"compiler artifact" + ); + drop(captured); + assert!(!directory.exists()); + fs::remove_dir_all(root).unwrap(); + } + #[test] + fn windows_regressions_precede_the_complete_unfiltered_suite() { + let commands = commands(NativeTarget::WindowsX86_64, Cohort::All, &[]); + assert_eq!(commands.len(), 2); + assert!(commands[0].contains(&"--lib".into())); + assert!(commands[0].contains(&"--workspace".into())); + assert!(!commands[0].contains(&"--package".into())); + assert!(commands[0].contains(&"--".into())); + assert!(commands[1].contains(&"--workspace".into())); + assert!(!commands[1].contains(&"--".into())); + assert!(commands[1].contains(&"--no-fail-fast".into())); + let unit = super::commands(NativeTarget::WindowsX86_64, Cohort::Unit, &[]); + assert_eq!(unit.len(), 2); + assert!(unit[1].contains(&"--lib".into())); + let inventory = vec![ + json!({"package":"codebase-graph","target":"generation_storage","kind":"test","cohort":"generation"}), + ]; + let generation = + super::commands(NativeTarget::WindowsX86_64, Cohort::Generation, &inventory); + assert!(generation[0].contains(&"codebase-graph/bundled-windows-extensions".into())); + assert!(!generation[0].iter().any(|s| s.contains("k-wiki/bundled"))); + } + #[test] + fn partitions_every_test_target_and_keeps_new_integration_targets() { + let metadata = json!({"workspace_members":["root","wiki"],"packages":[ + {"id":"root","name":"codebase-graph","targets":[ + {"name":"codebase_graph","kind":["lib"],"test":true,"doctest":true}, + {"name":"codebase-graph","kind":["bin"],"test":true,"doctest":false}, + {"name":"generation_storage","kind":["test"],"test":true,"doctest":false}, + {"name":"future_test","kind":["test"],"test":true,"doctest":false}]}, + {"id":"wiki","name":"k-wiki","targets":[ + {"name":"generation_storage","kind":["test"],"test":true,"doctest":false}]}]}); + let inventory = inventory(&metadata).unwrap(); + assert_eq!(inventory.len(), 6); + assert_eq!( + inventory + .iter() + .filter(|x| x["cohort"] == "generation") + .count(), + 1 + ); + let selected = commands(NativeTarget::MacosArm64, Cohort::Integration, &inventory); + assert_eq!(selected.len(), 3); + assert!(selected[0].contains(&"future_test".into())); + assert!(!selected[0].contains(&"generation_storage".into())); + assert!(selected[1].contains(&"generation_storage".into())); + assert!(selected[2].contains(&"--doc".into())); + assert!(Cohort::parse("invalid").is_err()); + } +} diff --git a/crates/xtask/src/native_tests/shards.rs b/crates/xtask/src/native_tests/shards.rs new file mode 100644 index 00000000..98a12119 --- /dev/null +++ b/crates/xtask/src/native_tests/shards.rs @@ -0,0 +1,155 @@ +use super::*; + +// Keep Cargo plus the forwarded harness arguments well below Windows' 32 KiB +// command-line ceiling, including Cargo's executable/path/environment options. +const ARGUMENT_UNITS: usize = 8 * 1024; + +#[derive(Clone, Copy)] +pub(super) struct Shard { + pub(super) part: usize, + pub(super) total: usize, +} +impl Shard { + pub(super) fn parse(value: &str) -> Result { + let invalid = || format!("invalid test shard {value}; expected I/N with 1 <= I <= N <= 16"); + let (part, total) = value.split_once('/').ok_or_else(invalid)?; + let part: usize = part.parse().map_err(|_| invalid())?; + let total: usize = total.parse().map_err(|_| invalid())?; + if part == 0 || part > total || total > 16 { + return Err(invalid()); + } + Ok(Self { part, total }) + } + pub(super) fn commands( + self, + base: &[String], + listing: &str, + ) -> Result<(Vec, Vec>), String> { + let all: BTreeSet<_> = listing + .lines() + .filter_map(|l| l.strip_suffix(": test")) + .map(str::to_owned) + .collect(); + // Spread contiguous module families, including the expensive collector + // and lifecycle fixtures, evenly across the required serial runners. + let names: Vec<_> = all + .into_iter() + .enumerate() + .filter_map(|(index, name)| (index % self.total + 1 == self.part).then_some(name)) + .collect(); + let mut prefix = base.to_vec(); + prefix.extend(["--no-fail-fast", "--", "--exact"].map(str::to_owned)); + let initial: usize = prefix.iter().map(|s| s.encode_utf16().count() + 3).sum(); + let mut units = initial; + let mut command = prefix.clone(); + let mut commands = Vec::new(); + for name in &names { + let next = name.encode_utf16().count() + 3; + if initial + next > ARGUMENT_UNITS { + return Err("native test name exceeds the command argument budget".into()); + } + if units + next > ARGUMENT_UNITS { + commands.push(command); + command = prefix.clone(); + units = initial; + } + command.push(name.clone()); + units += next; + } + // An empty selection must execute nothing; an empty harness filter + // would silently run the entire suite on every shard. + if command.len() > prefix.len() { + commands.push(command); + } + Ok((names, commands)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn selected_exact_filters_execute_the_expected_real_harness_cases() { + let cases = [ + "tests::native_target_contract_maps_names_and_binaries", + "tests::release_identity_validation_is_strict", + ]; + let listing = cases + .iter() + .map(|s| format!("{s}: test\n")) + .collect::(); + let (_, commands) = Shard::parse("1/1") + .unwrap() + .commands(&["test".into()], &listing) + .unwrap(); + let command = &commands[0]; + let split = command.iter().position(|a| a == "--").unwrap(); + let result = Command::new(env::current_exe().unwrap()) + .args(&command[split + 1..]) + .output() + .unwrap(); + let stdout = String::from_utf8_lossy(&result.stdout); + assert!( + result.status.success(), + "{stdout}\n{}", + String::from_utf8_lossy(&result.stderr) + ); + assert!(stdout.contains("2 passed; 0 failed"), "{stdout}"); + } + #[test] + fn shards_cover_every_listed_case_once_and_bound_windows_arguments() { + let names: BTreeSet<_> = (0..900) + .map(|i| { + format!( + "storage::maintenance_{}::a_long_fixture_name_{i:04}", + "case".repeat(12) + ) + }) + .collect(); + let listing = names + .iter() + .map(|n| format!("{n}: test\n")) + .collect::(); + let base = vec!["test".into(), "--workspace".into(), "--lib".into()]; + let mut seen = BTreeSet::new(); + for part in 1..=3 { + let (selected, commands) = Shard { part, total: 3 }.commands(&base, &listing).unwrap(); + assert!(commands.len() > 1); + let actual: Vec<_> = commands + .iter() + .flat_map(|c| { + c[c.iter().position(|s| s == "--exact").unwrap() + 1..] + .iter() + .cloned() + }) + .collect(); + assert_eq!(actual, selected); + for command in commands { + assert!( + command + .iter() + .map(|s| s.encode_utf16().count() + 3) + .sum::() + <= ARGUMENT_UNITS + ); + } + for name in selected { + assert!(seen.insert(name)); + } + } + assert_eq!(seen, names); + assert!(Shard::parse("0/3").is_err()); + assert!(Shard::parse("4/3").is_err()); + assert!(Shard::parse("1/17").is_err()); + assert!(Shard::parse("1/3/4").is_err()); + } + #[test] + fn empty_shard_never_runs_an_unfiltered_suite() { + let (selected, commands) = Shard::parse("1/3") + .unwrap() + .commands(&["test".into()], "0 tests, 0 benchmarks\n") + .unwrap(); + assert!(selected.is_empty()); + assert!(commands.is_empty()); + } +} diff --git a/docs/mcp.md b/docs/mcp.md index 03256740..5a5a3448 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -56,11 +56,10 @@ changing the MCP URL. The service manager is selected by platform: -- macOS: a user `launchd` service owns the configured loopback listener and +- macOS ARM64: a user `launchd` service owns the configured loopback listener and starts or restarts the daemon when a client connects, including in on-demand-only user sessions. -- Linux: a `systemd --user` unit manages the repository daemon. -- Windows: Task Scheduler manages the repository daemon. +- Windows x86_64: Task Scheduler manages the repository daemon. A repository lock prevents a second daemon from starting for the same graph state. If the service manager is unavailable, setup fails closed rather than diff --git a/docs/release.md b/docs/release.md index b8e1adf3..9bd55258 100644 --- a/docs/release.md +++ b/docs/release.md @@ -2,7 +2,7 @@ Release-please manages version pull requests and changelogs. Publication is bound to the successful `main` CI run for the exact release-PR merge commit. Subsequent merges do not invalidate that release while its commit remains -on `main`. The workflow validates all four retained native artifacts before creating its tag and GitHub Release, +on `main`. The workflow validates both retained native artifacts before creating its tag and GitHub Release, then publishes the crate from the same source SHA. Release-please itself always runs with tag creation disabled. Failed CI, ordinary commits, ambiguous release PRs, and commits removed from main history cannot authorize publication. @@ -34,7 +34,7 @@ asset and crate publication. Never copy a local CLI login token into repository Pull requests targeting `main` and pushes to `main` run: - `cargo fmt --check` -- platform-aligned workspace tests on Linux, macOS ARM, and Windows +- platform-aligned workspace tests on macOS ARM64 and Windows x86_64 - `cargo clippy --workspace --all-targets --all-features --locked -- -D warnings` - Rust advisory scanning with `cargo audit` - `cargo publish --dry-run --locked` @@ -42,11 +42,16 @@ Pull requests targeting `main` and pushes to `main` run: - `cargo package -p k-wiki --locked --no-verify` and the isolated Knowledge Wiki smoke. The unpublished wiki binary uses the in-tree codebase-graph registrar, while the preceding root package check verifies that publishable shared API in isolation. -- Release-ready package builds on Linux x86_64, macOS ARM/x86_64, and Windows x86_64. Pull requests build and smoke - them without retention; `main` retains all four artifacts for 90 days. +- Release-ready package builds on macOS ARM64 and Windows x86_64. Pull requests build and smoke + them without retention; `main` retains both artifacts for 90 days. - A stable `required` aggregate check that succeeds only when every mandatory job succeeds. Repository rules should require this check after it has appeared on `main` once. +macOS ARM64 runs unit, generation-storage, and remaining integration/doctest cohorts independently. Windows uses four +serial groups per cohort, including generation-storage, so each case runs once. Groups are discovered from +Cargo's test inventories, with complete and selected inventories retained in diagnostic artifacts. Each runner executes +tests serially, and a target's release artifact is built only after all its test groups succeed. + ## Release Flow 1. Merge ordinary PRs into `main`. Once current-main CI succeeds, release-please creates or updates the release PR. @@ -54,7 +59,7 @@ Pull requests targeting `main` and pushes to `main` run: 3. Release verifies the run's repository, workflow path, event, branch, status, SHA, and exact trusted release PR identity. The merge SHA must still be in main history; it need not be the latest main commit. 4. Immutable metadata must agree across the root package, wiki package/dependency, release manifest, and changelog. - Production checks and all four native artifacts are validated before any tag or release is created. + Production checks and both native artifacts are validated before any tag or release is created. 5. The single publisher creates only `vX.Y.Z` at that SHA, uploads the validated archives, and publishes the crate. Existing matching tags/releases can be resumed. Conflicting tags fail without being moved. Delayed older versions do not replace newer versions as GitHub's latest release. @@ -135,7 +140,7 @@ Dispatch `Release` on `main` with exactly one target: archive/checksum/provenance set without creating tags, releases, labels, proposals, or publishing crates. After checking the successful dry-run, dispatch the same target with `dry-run: false` to publish. -For existing-tag recovery, `artifact-source: rebuild-if-missing` rebuilds **all four** targets with the shared native +For existing-tag recovery, `artifact-source: rebuild-if-missing` rebuilds **both** targets with the shared native workflow if any retained artifact is unavailable. It never mixes promoted and rebuilt artifacts. Automatic and CI-run recovery never substitute a run or rebuild missing artifacts. @@ -164,4 +169,4 @@ conda-forge: 2. Verify the Rust toolchain requirements are available on conda-forge. 3. Copy `conda-forge/recipe/meta.yaml` into a new `recipes/codebase-graph/` directory in a fork of `conda-forge/staged-recipes`. 4. Replace `version` and `sha256` placeholders with release-specific values. -5. Open the staged-recipes pull request and let conda-forge CI validate Linux, macOS, and Windows builds. +5. Open the staged-recipes pull request and let conda-forge CI validate macOS ARM64 and Windows x86_64 builds. diff --git a/docs/specs/CBG-COPY-01/approval.md b/docs/specs/CBG-COPY-01/approval.md new file mode 100644 index 00000000..a70f8d6f --- /dev/null +++ b/docs/specs/CBG-COPY-01/approval.md @@ -0,0 +1,13 @@ +# CBG-COPY-01 — Scope A approval + +- Canonical specification: [spec.md](spec.md), logical revision 0.2. +- Approved specification full Git SHA: `c5b62ba77d2f94b71714b5319f3b3fa215983f9f`. +- Approved file SHA-256: `1a0ea5f860ce2764e1666bd6345faf9bb24929f35795255772c04380839b4dd2`. +- User decision, this conversation: **“i approve the implementation of scope A. push the fix to PR #153”** (2026-10-26). +- Approved scope: Scope A only, including all-mutating-phase fail-closed behavior, removal of native-error substring suppression and write retry, initial configured request pool, equal existing spill/final-file limits, bounded diagnostic ingestion/reporting and the specified verification. Scope B automatic candidate reconstruction and Ladybug upgrade remain excluded. +- Target: existing [PR #153](https://github.com/rabii-chaarani/codebaseGraph/pull/153), branch `codex/automatic-graph-disk-cleanup`, baseline `5839055d9279f4475f893a54abeaa542344c1b02`. The user's explicit PR target supersedes the draft's provisional clean-main recommendation; the worktree starts clean from that PR head and preserves its lifecycle/maintenance and supported-platform policy. +- Independent proposal review and focused verification: [resolution](review-resolution.md), [final verification](proposal-review-verification.md). Reviewer verification is not the user decision above. +- Closure owner remains the repository maintainer. Approval, implementation, local verification, supported-platform qualification and rollout remain separate states. +- Linear connection remains pending verification; no delivery issue or successful synchronization is claimed. + +The specification snapshot is retained unchanged. This record attaches the explicit same-scope user approval to its committed bytes; no additional scope choice is inferred. diff --git a/docs/specs/CBG-COPY-01/baseline-review.md b/docs/specs/CBG-COPY-01/baseline-review.md new file mode 100644 index 00000000..53b50a22 --- /dev/null +++ b/docs/specs/CBG-COPY-01/baseline-review.md @@ -0,0 +1,42 @@ +# CBG-COPY-01 — PR #153 baseline-adaptation review + +## GREEN — proceed with approved Scope A + +**No new decision-blocking premise or scope blocker was found in the actual PR #153 baseline.** Scope A can be implemented on `5839055d9279f4475f893a54abeaa542344c1b02` without replacing its newer maintenance/containment design or reopening the settled PR-head choice. This is a proposal/baseline verdict, not an implementation, test or release approval. + +### Reviewed identity and boundary + +- Fresh read-only review under the mandated GPT-6.1 Sol / Max, fresh/default assignment; used `adversarial-proposal-review-engineer/SKILL.md` and the governing original-root `AGENTS.md`. +- Actual worktree: `/private/tmp/codebasegraph-copy153-scope-a`. Inspected HEAD `92f3c14a7f61cf01c8e598aea8e6c3c73ccdf274`: approval-record commit above spec commit `c5b62ba77d2f94b71714b5319f3b3fa215983f9f`, above the selected PR baseline. +- Canonical specification SHA-256 verified: `1a0ea5f860ce2764e1666bd6345faf9bb24929f35795255772c04380839b4dd2`. Read `approval.md`; the exact user decision approves Scope A and PR #153. Its explicit baseline decision supersedes the draft's provisional main recommendation. +- Read the prior focused verification. APR-001–003 remain resolved at specification level; no broad reinvestigation or Scope B design was performed. +- Source discovery used the registered original-root graph first, then explicit semantic `graph_context(run_isolated_phase, runtime)`. Graph health identified `/Users/rabii/Projects/Repositories/codebaseGraph`; its older indexed line ranges are **not** evidence of current PR source. All current-source conclusions below come from direct worktree reads and comparison. A read-only Git diff confirmed the inspected writer/phase/run/accumulator/writer/xtask files have no changes relative to the selected PR baseline. + +### Focused premise checks + +| Approved requirement | Actual PR-baseline evidence | Assessment | +| --- | --- | --- | +| R1/R2: one write attempt, configured request pool, no native-error suppression | `src/db_writer/write.rs:25–36,218–249,379–396` retains the known fallback retry, isolated pool loop and suppression. Its only delta from the registered original writer is the database-use lease at lines 28–30. `src/db_writer/phase.rs:497–568,711–746` retains the corresponding native query/checkpoint/suppression boundaries. | The approved fix targets the actual exposure. Removing retry/suppression does not require a new recovery owner or a changed pool ceiling. Preserve the newer lease across the whole invocation. | +| R4: preserve first-chunk/index ordering | `write.rs:119–159,264–291` already completes the first successful repeated-table COPY before its hash index and processes connectors only after node indexes. | No new sequencing premise or missing detector. Keep this ordering; stop on failure rather than replay or repair. | +| R3: reuse equal existing spill/final limits | `src/execution/run.rs:60–75` still passes `usize::MAX` through `COPY_FILE_CHUNK_BYTES`. `src/staging_writer/accumulator.rs:739–829,869–979` already rotates sequential JSON/CSV files and accounts for complete JSON framing / connector headers when checking a single row. | Passing the existing spill bound to both limits fits the approved design. No native list/glob support, new setting or table-wide buffering is needed. | +| R5/R6: preserve newer ownership, containment and retention while bounding diagnostics | `phase.rs:121–188` holds a database-use lease, resolves absolute DB paths and chooses inherited versus standalone guarded execution. Full stderr reads remain at lines 247 and 318. Guarded teardown checks are at 287–340; registered-root cleanup restrictions and ownership are at 344–369,578–708. `run.rs:25–47` holds artifact/database use leases. The accumulator keeps the legacy staging guard through its result (`26–40,65–91,420–433`), uses owned output/finalization (`803–826,942–975`) and defers registered spool disposal (`1002–1037`). | Both diagnostic routes require the approved bounded-reader/assembled-context change; they must not be collapsed into the old path. Existing cleanup authorization, typed RSS failure, ownership/provenance wrappers and guard lifetime remain intact. The spec already requires this; no new architectural decision is needed. | +| Unchanged supported-platform gates | `crates/xtask/src/main.rs:21–35,52–56` defines exactly `MacosArm64` and `WindowsX86_64` and rejects other target names. | Qualification remains macOS arm64 and Windows x64 only. Older wiki/memory statements mentioning Linux or macOS Intel are historical, not authority to broaden this delivery. | + +The attempted falsifier was that the newer maintenance-owned phase/output paths make the approved local retry/diagnostic/chunk changes depend on a different cleanup or retry architecture. The source does not support that concern: Scope A changes the load policy and bounded I/O at existing boundaries while retaining the current owners. No revised scope or user decision is required before implementation. + +### Limits and remaining gates + +- The existing normal-profile RSS, control-list scaling, actual isolated mutation/reopen, PK/endpoint/search, preservation/cleanup, bounded-reader and supported-platform acceptance gates remain mandatory. None is claimed to pass here. +- Scryer orientation/rules and existing storage/recovery wiki contracts were read through their registered original-root services. Those services expose original-checkout state, not a fresh PR-head model reconciliation; implementation reconciliation remains with the coordinator. Linear is verification-pending; no tracking synchronization is claimed. +- No builds, tests, native probes, dependency changes, Git writes, repository edits, model/tracking writes or daemon actions ran. Only this report was written. **No constants changed.** The only baseline assumption used is the explicit approved PR-head target, independently checked against the inspected source files. +- Reusable knowledge assessed: this is a narrow source-adaptation verdict, not a new verified runtime fact. The existing uncertain-COPY-outcome memory already covers the durable lesson; no memory write is warranted. + +Independent fresh final code review remains a separate gate after implementation and executed evidence. + +### Inspected source SHA-256 + +- `src/db_writer/write.rs`: `d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d` +- `src/db_writer/phase.rs`: `a0afc7c8be36c10ae7dcc839ccb9abd821dd3a2966b93564434e0d6473902b97` +- `src/execution/run.rs`: `899acb0f8bf2cdeb55a1f232694ae6d5d4ef4aee61db33235eebfcd1d5dc3470` +- `src/staging_writer/accumulator.rs`: `359385201e8af617957ee854be2069092b1cdebfc96c5b738962c874ce445481` +- `src/staging_writer/writer.rs`: `10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0` diff --git a/docs/specs/CBG-COPY-01/evidence/acceptance-map.json b/docs/specs/CBG-COPY-01/evidence/acceptance-map.json new file mode 100644 index 00000000..2eec43ed --- /dev/null +++ b/docs/specs/CBG-COPY-01/evidence/acceptance-map.json @@ -0,0 +1,51 @@ +{ + "A1": { + "method": "actual isolated scheduler injected phase errors + native result propagation", + "passing": [ + "db_writer::write::tests::isolated_phase_schedule_stops_after_first_error_without_replay", + "both duplicate-DDL helpers" + ], + "limits": "native engine disk-full rollback is not claimed" + }, + "A2": { + "method": "normalCLI realCOPY+checkpoint success then debug acknowledgement fault", + "result": "native-profile-copy-final2/2passed ManagedandDirect", + "proof": "schema+firstcopytrace; originalcause; no laterphases; activebundleunchanged" + }, + "A3": { + "method": "normalCLI1MiBsequentialchunks/readOnlyreopen/exact1400Functionlabels/allFunctionPK +allIDsrepeatedtables+actualPKplan+ModuleDefinesFunctionendpoints", + "result": "optimizednative2/2passed", + "limits": "smallfixtureoracle notgeneralnativeintegrityvalidator" + }, + "A4": { + "method": "exactframing/header/UTF8sinktests+all1MiBproducedfilebytesfromrealCLItrace", + "result": "focusedunitpassed/optimizednativepassed" + }, + "A5": { + "method": "realCOPYackfailureManagedDirect priorbundlehashes+query/search; schedulerunit stopsindex/postcopy; existingAPIfailurecleanup and native lifecycle cases", + "result": "COPYcasepassed; lifecycleexecutionpending", + "limits": "not a single native in-flightcheckpoint/index/full-disk faultmatrix; layeredpolicy/owneroracles stated separately" + }, + "A6": { + "method": "separatelaternormalCLIinvocation afterfailedManagedDirect; existingownedcleanup/churn/lifecycle tests", + "result": "laterinvocationpassed; aggregatecleanup pendingfullsuite", + "limits": "PR153boundedmaintenance notinstantrecursivecleanup; no unsafe deletedirs" + }, + "A7": { + "method": "128/256/384phase-pass-through tests15/15, realnormal384poolchunkbuildsuccessful, existingnativeactualRSSbudgetfault", + "result": "normal+matrixpassed; RSSfaultpending", + "limits": "sampledcharges notmathematicalpeaks; notperpoolmeasuredbenchmark" + }, + "A8": { + "method": "boundedreadinstrumentation+oversizedUTF8actualerrorhelpers/reader/assemblerqueryVscheckpoint+cleanupsecondarytest", + "result": "final15/15andfullsuitecleanupsecondarypassed" + }, + "A9": { + "method": "same-sourcefreshbuildspill1vs32 allsortednode/stringJSON+connectorendpoints+base/lastsearch parity; existingcachedclean/churn", + "result": "optimized2/2passed; fullchurnpending" + }, + "A10": { + "method": "fmt,strictclippy,fullworkspace,doctests,optimizednative/lifecycle,currentplatformCI", + "result": "fmt+clippy+doctests+nativepassed; full/lifecycleinprogress; WindowsCIpending" + } +} diff --git a/docs/specs/CBG-COPY-01/evidence/diagnostic-final-01.junit.xml b/docs/specs/CBG-COPY-01/evidence/diagnostic-final-01.junit.xml new file mode 100644 index 00000000..3c898a68 --- /dev/null +++ b/docs/specs/CBG-COPY-01/evidence/diagnostic-final-01.junit.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/specs/CBG-COPY-01/evidence/focused-unit-02.junit.xml b/docs/specs/CBG-COPY-01/evidence/focused-unit-02.junit.xml new file mode 100644 index 00000000..edecdff5 --- /dev/null +++ b/docs/specs/CBG-COPY-01/evidence/focused-unit-02.junit.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/specs/CBG-COPY-01/evidence/intent-traceability.json b/docs/specs/CBG-COPY-01/evidence/intent-traceability.json new file mode 100644 index 00000000..1e02f4c8 --- /dev/null +++ b/docs/specs/CBG-COPY-01/evidence/intent-traceability.json @@ -0,0 +1,66 @@ +{ + "work": "CBG-COPY-01", + "approved_scope": "A", + "approved_spec_commit": "c5b62ba77d2f94b71714b5319f3b3fa215983f9f", + "base": "5839055d9279f4475f893a54abeaa542344c1b02", + "existing_claims": { + "resp-110": { + "contract": "fresh schema/staged-row database load", + "implementation": [ + "src/db_writer/write.rs::write_database_with_metrics", + "src/db_writer/write.rs::write_database_isolated_with", + "src/db_writer/phase.rs::execute_phase" + ], + "tests": [ + "db_writer::write::tests::isolated_phase_schedule_stops_after_first_error_without_replay", + "copy_phase_process::native_copy_acknowledgement_failure_preserves_managed_and_direct_bundles" + ] + }, + "resp-273": { + "contract": "stage within configured budget", + "implementation": [ + "src/execution/run.rs::execute_scanned_materialization", + "src/staging_writer/accumulator.rs::JsonTableSink", + "src/staging_writer/accumulator.rs::ConnectorSink" + ], + "tests": [ + "staging_writer::accumulator::output_tests::final_output_limits_include_json_framing_and_csv_header", + "copy_phase_process::bounded_production_chunks_reopen_with_primary_keys_connectors_and_search" + ] + }, + "resp-108": { + "contract": "publish complete graph generations", + "implementation": [ + "src/api/materialization.rs::execute_materialization_request (unchanged owner)" + ], + "tests": [ + "copy_phase_process::native_copy_acknowledgement_failure_preserves_managed_and_direct_bundles", + "tests/generation_storage.rs (existing suite)" + ] + }, + "resp-264": { + "contract": "report build/resource failures", + "implementation": [ + "src/db_writer/phase.rs::isolated_phase_error", + "src/db_writer/phase.rs::read_error_tail_from" + ], + "tests": [ + "db_writer::phase::tests::oversized_native_error_preserves_query_vs_explicit_checkpoint_boundary", + "db_writer::phase::tests::error_tail_reader_requests_no_more_than_the_limit" + ] + }, + "resp-281": { + "contract": "release graph-load phase resources", + "implementation": [ + "src/db_writer/phase.rs::run_isolated_phase", + "src/db_writer/phase.rs::run_guarded_phase (existing containment retained)" + ], + "tests": [ + "tests/refresh_recovery_process.rs lifecycle cases (execution pending)" + ] + } + }, + "model_reconciliation": "Registered model points to original checkout. New worktree has no model; no matching-fingerprint test verdict ingestion or implemented-claim fold is claimed.", + "no_new_claims": "This fix restores existing fresh-load/budget/publication responsibilities; no new nodes, links or responsibilities required.", + "linear": "pending connection verification; no synchronization claimed" +} diff --git a/docs/specs/CBG-COPY-01/evidence/local-checks-in-progress.json b/docs/specs/CBG-COPY-01/evidence/local-checks-in-progress.json new file mode 100644 index 00000000..f9fb2381 --- /dev/null +++ b/docs/specs/CBG-COPY-01/evidence/local-checks-in-progress.json @@ -0,0 +1,89 @@ +{ + "approved_spec_commit": "c5b62ba77d2f94b71714b5319f3b3fa215983f9f", + "implementation_base": "5839055d9279f4475f893a54abeaa542344c1b02", + "static_release_archive": { + "version": "0.19.0", + "sha256": "6bee40babdf837bb6aa4d271047dfe5ed3dacf022f16af84952ca4c22d034d62" + }, + "cli": { + "path": "local frozen compiler artifact", + "sha256": "5acca8f98ee5108261f3eae4e96d7467ea2cddc8c75ccbd0e90050975b14cde2" + }, + "executed": [ + { + "command": "cargo test -p xtask --locked", + "result": "42 passed,0failed", + "log_sha256": "4ff84539c916d0877d2a13522aa60f0d48ddb0586d412571e11ec649bd55fce0" + }, + { + "command": "cargo nextest run ... --lib selected writer/staging/execution/search", + "result": "72 passed,0failed", + "log_sha256": "00ac3c11e8320ae12b3346515ddb8243b3acacb580526a9aab6aecceb4f03ab9" + }, + { + "command": "cargo test -p codebase-graph --test copy_phase_process --locked -- --nocapture", + "result": "2 passed,0failed", + "log_sha256": "092a729c080a0574d7d6c6db6aaa3d76f3a876e1af80bb823e3f2d99a1e0b3b9" + }, + { + "command": "cargo clippy --workspace --all-targets --all-features --locked -- -D warnings", + "result": "passed after moving helper before test module", + "log_sha256": "5240928b35f279dd113668b6b32cae99570fd6b60f96b66c2b99bb6207067e8c" + }, + { + "command": "cargo nextest ... final writer/phase +pool matrix", + "result": "15 passed,0failed", + "log_sha256": "5f36785be519557ab15540c26d1f193e59109cc34dc41dba1e1c87cf8d1af514" + }, + { + "command": "cargo test --profile native-tests --test copy_phase_process --locked -- --nocapture", + "result": "2 passed,0failed including same-source clean control", + "log_sha256": "21cc9f39b7851d4a0cb2ff0643f2e95e2bf802a4af02614e392958871c9dfb00" + }, + { + "command": "cargo clippy --workspace --all-targets --all-features --locked -- -D warnings (final)", + "result": "passed", + "log_sha256": "71cb61d12721c357b0ddb1ddc49f44b95471dbe4f4eb3ef26ce25755991dcbb3" + }, + { + "command": "cargo test --workspace --doc --locked", + "result": "3 passed,0failed", + "log_sha256": "d85e2712ea9002c60b12a7faea49a771cf95d0e132f2d36bdb34159ebaf7786f" + } + ], + "initial_failures": [ + { + "kind": "compile", + "reason": "test helper missing PathBuf import, fixed before execution" + }, + { + "kind": "test oracle", + "reason": "fixture produced Defines not HasReturnType edges; corrected to actual expected Module\u2192Defines\u2192Function endpoints, native02passed" + }, + { + "kind": "Clippy", + "reason": "helper followed test module; moved without warning suppression" + } + ], + "pending": [ + "fullworkspace1344serialrun currentlyrunning", + "optimizedexistinglifecycle16case run currentlyrunning", + "Windowsx86_64 existing PRCI", + "finalaggregatehandoff notmerge/deploy" + ], + "no_claims": [ + "not original incident diagnosis", + "no engine upgrade", + "no deployment/daemon changes", + "no Scryer currentworktree verdict ingestion" + ], + "final_source_sha256": { + "src/db_writer/write.rs": "d1625f1bd5239eea6eb7dd2b85244ce93f2df13d1503007b6cbe37dddd782542", + "src/db_writer/phase.rs": "1500b94723ada235301c44f6e9c27ffcddeb3f13f03862de84a54b100ca0be7e", + "src/execution/run.rs": "0c2adc995840d52503545e7918e76e785ad41705320eb03c36844fb229489752", + "src/staging_writer/accumulator.rs": "13737811c1dd9cb827573bf4ca7233265c393c3dbdc02214ceca9a3814199d4e", + "src/staging_writer/tests.rs": "39360ae193bd29bc6a2e72ab9b188569a952944afa1b0f686224d9a96a8dc942", + "src/staging_writer/writer.rs": "08d8cfbb0ec534f09e5ad7c13e5e90cb1ab397b80b51aae1356cb397431e2a55", + "tests/copy_phase_process.rs": "ca61337099747e3356c4308bfcba7c6b73f8d2a604bef6808569509465567c3e" + } +} diff --git a/docs/specs/CBG-COPY-01/evidence/writer-pool-matrix-final.junit.xml b/docs/specs/CBG-COPY-01/evidence/writer-pool-matrix-final.junit.xml new file mode 100644 index 00000000..f663ac5a --- /dev/null +++ b/docs/specs/CBG-COPY-01/evidence/writer-pool-matrix-final.junit.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/specs/CBG-COPY-01/execution.md b/docs/specs/CBG-COPY-01/execution.md new file mode 100644 index 00000000..e0ba1186 --- /dev/null +++ b/docs/specs/CBG-COPY-01/execution.md @@ -0,0 +1,21 @@ +# CBG-COPY-01 — Execution status + +## Authority and snapshot + +Approved [Scope A](spec.md), revision 0.2 at full specification commit `c5b62ba77d2f94b71714b5319f3b3fa215983f9f`; [approval](approval.md) quotes the user's explicit implementation/PR request. Implementation worktree starts at existing PR153 head `5839055d9279f4475f893a54abeaa542344c1b02` and preserves its supported-platform policy (macOS ARM64, Windows x86_64). [Independent baseline adaptation review](baseline-review.md) is green; a fresh final code review is still required. + +Scryer original planning ledger `chg-5qnevq` and execution ownership/brief `chg-v5863h` record the same-scope user sign-off. No new architecture claim or responsibility was invented for this existing-claim safety fix. The registered Scryer/CodebaseGraph services refer to the original checkout. A Scryer read scoped to this new worktree reports no model; do not ingest current test reports under old source fingerprints or claim global drift was reconciled. Linear remains verification-pending with no dispatchable issue tools; synchronization is pending. + +## Executed baseline preparation + +- Toolchain: Rust/Cargo 1.96.0, macOS arm64. +- Official Ladybug 0.19.0 static archive: SHA-256 `6bee40babdf837bb6aa4d271047dfe5ed3dacf022f16af84952ca4c22d034d62`, matching official release metadata; paired headers/static library provided explicitly through `LBUG_LIBRARY_DIR`/`LBUG_INCLUDE_DIR`, with `LBUG_VERSION=0.19.0`. No dependency upgrade or Cargo.lock update. +- Baseline `cargo test -p codebase-graph --lib --tests --locked --no-run`: passed. This compiled existing targets only; it did not execute acceptance tests. + +## Implementation and verification + +In progress. Local focused tests passed (72/72), the real CLI COPY regressions passed (2/2), release-recipe tests passed (42/42), and strict workspace/all-target/all-feature Clippy passed after correcting one item-order violation. The complete serial workspace nextest run has started with 1,344 cases; it is not yet a pass. See [local check index](evidence/local-checks-in-progress.json) for provenance and early failed compile/test-oracle/lint attempts; failures were corrected rather than hidden. + +The native regression writes through a source-bound normal CLI with isolated phase children. It covers Managed and Direct unchanged database/manifest/search bundles after actual COPY+checkpoint success followed by an injected acknowledgement error, then a separate successful invocation. The chunk fixture uses the configured 1 MiB spill/final bound, checks early indexes, reopens in a new process, compares all 1,400 expected Function labels and keyed lookups, probes every ID in repeated node/edge-node tables, confirms the native `PRIMARY_KEY_SCAN_NODE_TABLE` operator, verifies exact Module→Defines→Function endpoints, and searches across chunked output. Observed database phase charge was 130,073,800 bytes in that fixture; this is a local sample, not a general peak bound or platform guarantee. + +The initial connector oracle expected `HasReturnType`, which this Rust fixture does not emit. Read-only scratch queries established the actual `Defines` edges; the corrected test passed. No product behavior was changed to satisfy that mistaken oracle. The full suite, final review, native profile and Windows CI remain separate gates. No merge, deployment, daemon restart or live graph rewrite is authorized. diff --git a/docs/specs/CBG-COPY-01/implementation-review-resolution.md b/docs/specs/CBG-COPY-01/implementation-review-resolution.md new file mode 100644 index 00000000..3c809dd5 --- /dev/null +++ b/docs/specs/CBG-COPY-01/implementation-review-resolution.md @@ -0,0 +1,12 @@ +# CBG-COPY-01 — Implementation review resolution + +Independent reviewer: fresh/default-equivalent `openai-codex/gpt-6.1-sol`, Max effort. Baseline `5839055d9279f4475f893a54abeaa542344c1b02`. [Initial review and focused source addendum](implementation-review.md). Reviewer did not implement or execute project tests. + +| ID | Finding | Correction | Executed evidence / status | +| --- | --- | --- | --- | +| FR-001 (P2) | Oversized stderr tail erased COPY-query versus explicit-CHECKPOINT boundary. | Actual child error-format helpers append a short operation suffix outside native text; bounded reader/assembler retain it without a larger budget. | `writer-pool-matrix-final.log`: 15/15 pass, including oversized native UTF-8 through actual helpers, reader and assembler. Source inspected independently. Final source inspected independently; no unresolved confirmed finding. | +| FR-002 (P3) | Helper after the test module failed strict Clippy. | Move unchanged helper before the test module; no suppression. | `clippy-02.log` passed. Latest-source `clippy-final.log` passed after FR-001 and the pool-matrix test. | + +No other confirmed production defect survived the independent source review. This does not waive acceptance or platform qualification. The same-source fresh-candidate spill32/control comparison is independently reviewed and passed in the optimized native-tests profile (2/2 cases). The deliberate RSS fixture operates at the existing pre-open readiness barrier and is not labelled an in-flight COPY recovery proof. The original incident's first cause remains unproven. + +Full serial workspace tests are running on a frozen earlier compiler snapshot. Final affected checks must cover later diagnostic and control-oracle edits; results will be recorded separately. Windows x86_64 execution remains with native CI. No merge, rollout or daemon change is authorized. diff --git a/docs/specs/CBG-COPY-01/implementation-review.md b/docs/specs/CBG-COPY-01/implementation-review.md new file mode 100644 index 00000000..b062e682 --- /dev/null +++ b/docs/specs/CBG-COPY-01/implementation-review.md @@ -0,0 +1,112 @@ +# CBG-COPY-01 — independent final code review 01 + +## Outcome: initial snapshot needs corrections; final qualification remains pending + +Two confirmed findings apply to the supplied snapshot: **FR-001 (P2)** loses the COPY-query versus explicit-checkpoint boundary when truncating a large diagnostic, and **FR-002 (P3)** fails strict Clippy. During this review the coordinator corrected both. I independently checked those focused source changes: FR-002 also has a passing strict-Clippy rerun; FR-001's new regression has not yet been executed. No other confirmed P0–P3 production defect survived the reviewed source traces. This is not final acceptance, user approval, supported-platform qualification, or permission to merge/deploy. + +## Identity, authority and scope + +- Reviewer: fresh independent RLM child; coordinator-confirmed actual model **`openai-codex/gpt-6.1-sol`**, **`thinking=max`**, default-equivalent child, bounded task prompt only, no implementing history or forked turns. Read the required `adversarial-review-engineer/SKILL.md`, its engineering lenses, and the original repository-root `AGENTS.md`. +- Worktree: `/private/tmp/codebasegraph-copy153-scope-a`; HEAD `92f3c14a7f61cf01c8e598aea8e6c3c73ccdf274`; implementation is an uncommitted diff against PR #153 baseline `5839055d9279f4475f893a54abeaa542344c1b02`. +- Approved contract: `docs/specs/CBG-COPY-01/spec.md`, revision 0.2 at `c5b62ba77d2f94b71714b5319f3b3fa215983f9f`; independently verified file SHA-256 `1a0ea5f860ce2764e1666bd6345faf9bb24929f35795255772c04380839b4dd2`. Read `approval.md`, including the explicit Scope A / PR #153 decision. The frozen spec's planning language is qualified by that separate approval; it is not a scope reopening. +- Initial source identity: `final-review-snapshot-01.json`, SHA-256 `3f657549d8b974455eb99392c412ae8fea945a1c2fe3f7fe7fa1cbaad611c038`; all seven initial source hashes matched when read. +- Scope: writer/phase one-attempt policy, configured pool, sequential final staging, diagnostics, new subprocess regression oracles, and consequential caller/ownership/search paths. No Scope B replay, native upgrade, ownership redesign, threshold increase, or new public setting was reviewed or authorized. +- Graph-first discovery used the parent's full bounded handoff `review-discovery-gates.json`: write `Module:c1e2a855e3f4de7303ea`, phase `Module:b30b0f66c988e531c46d`, run `Module:e8798576a9ba6ba106cb`, accumulator `Module:d41558c55db44da4a833`, plus explicit change-impact/runtime gates and indexed dependencies. The registered graph is the older original checkout; actual conclusions use the PR worktree source/diff, not its obsolete line ranges. Followed relevant source imports after discovery. Read registered wiki storage/recovery contracts, recalled the verified uncertain-COPY-outcome memory, and fetched Scryer rules/orientation. Their original-checkout state is not a current-worktree fingerprint or passing verdict. Linear remains pending verification. + +## Confirmed findings and focused resolution + +### FR-001 — P2: truncation erases the operation boundary required for a COPY failure + +**Initial locations:** `src/db_writer/phase.rs:635–651,832–835,851–916`; both parent routes call the bounded reader at `248` and `335`. + +**Trigger / consequence:** when the native error itself exceeds 64 KiB, a failed COPY query and a failed explicit `CHECKPOINT` can produce the same returned diagnostic. This fails Scope A §3.4 / R6 / A8's requirement to distinguish those operation boundaries; it does not establish database corruption or change the fail-closed outcome. + +**Confirmed by complete static trace:** the child prepends `COPY statement ... failed:` or `checkpoint after ... failed:` to the native error. `execute_phase_file` returns that string through `bootstrap.rs:54–62`; `src/bin/codebase-graph.rs:1–4` prints it. The parent reader seeks to only the last 64 KiB, discarding either prefix. `isolated_phase_error` restores `phase=copy`, ordinal, target and limits, but the `LadybugWritePhase::Copy` request cannot identify which internal operation failed. Equal sufficiently long native tails therefore lose the distinction in both guarded and inherited routes. The byte cap and UTF-8 truncation themselves are bounded and correct. The old baseline also lost this boundary: this is an incomplete approved diagnostic fix, not a newly introduced replay defect. + +**Minimum remedy / verification:** keep a short stable boundary independently of the native prefix, for example append `operation=COPY-query` or `operation=explicit-CHECKPOINT` after the native cause. Preserve the actual native tail and the existing 64 KiB cap; COPY-query includes possible engine-internal checkpoint uncertainty and must not imply rollback. Exercise the actual error-formatting helpers, bounded reader and assembler with the same oversized UTF-8 native cause, asserting distinct boundaries, preserved metadata, marker and cap. + +**Focused source recheck:** coordinator changed actual query/checkpoint mappings to `copy_query_failure` / `checkpoint_failure`, appending exactly those suffixes. New `oversized_native_error_preserves_query_vs_explicit_checkpoint_boundary` runs both helpers through a cursor, bounded reader and assembled error with a >64 KiB cause. At phase SHA-256 `1500b94723ada235301c44f6e9c27ffcddeb3f13f03862de84a54b100ca0be7e`, the original causal loss is corrected by source inspection: the final printed error and both truncators preserve its suffix. **Affected execution/Clippy/final-snapshot verification is still pending.** + +### FR-002 — P3: strict workspace Clippy rejects the helper after the test module + +**Initial location:** `src/db_writer/write.rs:348–463`. + +The new `run_schema_query` follows `#[cfg(test)] mod tests`. `clippy-01.log` records `clippy::items_after_test_module`, fatal under the required `-D warnings`. This is an introduced, bounded build-gate failure, not a database correctness defect. + +**Resolution verified:** coordinator moved the identical helper above the test module, without an allow or semantic change. I compared the actual old/new text; the only change is relocation and surrounding whitespace. Current write SHA-256 `09598352b80e42f2c242e992b6377f57eee82d0acd3a0e308ddcf601f04d16c0`. `clippy-02.log` ends successfully for strict workspace Clippy. This resolves FR-002 on that helper-move snapshot; the subsequent FR-001 phase edit still needs its affected checks. + +## Safeguards and attempted falsifiers + +- **No same-candidate replay:** isolated `run_phase` makes one call with `request.buffer_pool_bytes`; fallback invokes `write_database_once` once. All schema/COPY/index/post-COPY results propagate with `?`; no buffer-pool classifier or mutating retry remains. Native query helpers now accept only actual `Ok`; generic `Err` mapping cannot silently suppress any of the former existing-object substrings. The first error stops scheduling; no candidate deletion/repair was added here. +- **Ordering:** first successful repeated-target COPY/checkpoint precedes its early hash-index/checkpoint. Other node indexes finish before connector COPY; a failing phase returns before the next mutation. This preserves the baseline sequence instead of replacing its detector. In-process and isolated paths use the same early-table logic. +- **Resources:** direct request pool and threads pass through unchanged; production writer retains one native thread and its existing calculator. Both failure routes now seek before reading, consume at most 64 KiB, decode within constant bounded storage, and cap assembled metadata plus UTF-8-safe native tail. Successful exits do not ingest stderr. Typed guardian/inherited RSS error branches and containment barriers are retained. No claim is made that a bounded file caps native index memory or that pool allowance equals resident RSS. +- **Staging:** the production call now passes the same existing spill bytes to both limits. Existing JSON/CSV sinks account for framing/header, reject an oversized framed row with `MemoryBudgetExceeded`, rotate deterministically and produce one COPY per file. The search reader already enumerates contiguous base/suffixed JSON files. Retained/cloned COPY-plan scaling is pre-existing and explicitly requires resource qualification; no evidence supports inventing a new streaming subsystem in this review. +- **Ownership/publication:** actual materialization caller aborts on pipeline error before finalization/publication (`src/api/materialization.rs:316–328`). Storage execution owns cleanup and appends cleanup failures after the primary error (`505–525`); Direct cleanup consults the existing phase containment registry (`1449–1450`). Existing newer lease, workspace, guardian and unknown-containment boundaries remain. Source inspection does not substitute for the pending native ownership matrix. +- **Real subprocess coverage:** `Fixture::build` invokes the normal Cargo-provided `codebase-graph build` executable, not a library fallback. Bootstrap registers the same executable; distinct phase PIDs and the post-success trace prove actual helper execution. The debug fault hook runs only after `execute_phase` returns successfully, including explicit checkpoint and handle drop, before reporting success. The failed trace contains schema then first COPY only. The follow-up build is an independent invocation, not Scope B replay. + +## Executed evidence read; remaining evidence limits + +I ran **no builds, tests or native probes**; executions below belong to the coordinator and their retained logs. Read-only Git inspection and source/hash checks were performed. + +| Evidence | Supported conclusion / limitation | +| --- | --- | +| `focused-unit-02.log` and JUnit | 72/72 pass, including actual duplicate-DDL native result propagation in both helpers, phase stop ordering, bounded reader instrumentation, UTF-8/context cap, framing/oversize/rotation, staging determinism and existing search chunks. These precede the FR-001 fix; the runner schedule is not native fault injection. | +| `xtask-tests.log` | 42/42 pass. Does not qualify native execution or a platform by itself. | +| `native-copy-01.log` | One preservation case passed; the other failed an invalid `HasReturnType` fixture expectation before that oracle was corrected. Retain as a failed execution, not a pass or a proven product regression. | +| `native-copy-02.log` | Corrected suite **2/2 pass** through normal CLI/phase subprocesses. Reopen checks all 1,400 expected Function labels, per-ID PK lookup, scan/PK parity for repeated targets, and genuine pinned-native `PRIMARY_KEY_SCAN_NODE_TABLE` evidence; exact Module–Defines–Function endpoint rows, first/last searchable terms, and observed per-COPY files ≤1 MiB. Managed and Direct real-COPY acknowledgement failures preserve DB/manifest/search hashes and prior content; later independent builds succeed. | +| Native02 resource output | Successful normal configured pool is demonstrated, separately from budget failure: database-write sampled high water `130073800`, parse `23124382`, search `1048544`, staging `2087283` bytes for the 1,400-function fixture. This does not measure a fully resident 384 MiB pool, all control-state costs, per-child phase overhead, or every representative workload. | +| `clippy-01.log` / `clippy-02.log` | Initial strict lint failure; helper-only fix rerun passes. New diagnostic fix requires fresh affected checks. | + +**Acceptance evidence not yet complete at this report:** + +1. The reviewed native oracle does not yet compare all same-source chunked results with an unchunked clean-control build (A9), independently expected IDs/JSON metadata for every node/edge target (A3), or prove every named repeated edge-node condition solely from its assertions. It does have substantial scan/PK/endpoint evidence; do not relabel it a count-only test. Coordinator plans a clean-control extension; that new source needs focused review and execution. +2. The new two-case suite does not establish the whole explicit-checkpoint/index/cancellation/RSS/secondary-cleanup preservation matrix in A5/A8 or no residual failed-run/extra generation condition in A6. Existing lifecycle/resource/churn cases can supply relevant evidence once traced and executed; their setup/barrier coverage must not be overstated as a failed in-flight COPY. The existing RSS fixture allocates before native database opening and must stay distinct from the normal successful profile. +3. Source passes configured pool directly, but the current new policy test uses only one tiny sentinel pool; A7's below/equal/above-256 request matrix and its measured resource/control-plan/phase evidence require explicit reconciliation. Safe typed budget abort alone does not satisfy normal workload success. +4. Full workspace, doctests, final format/strict Clippy after fixes, native-tests profile, and Windows x64 qualification remain pending. A full run executing an earlier source snapshot cannot silently cover later edits. The ordinary native-tests profile's debug fault coverage is required; release-only test exclusion is not a pass. +5. Scryer worktree evidence reconciliation, durable evidence/handoff, Linear synchronization, and owner acceptance remain with the coordinator. The original-root service cannot truthfully fingerprint this uncommitted worktree. No model/tracking mutation was done by this reviewer. + +These are qualification limits/open evidence items, not additional scored product defects. No original incident root cause, stronger native durability guarantee, exhaustive integrity validation, or platform success is inferred. + +## Snapshot hashes + +Initial snapshot hashes below remain the identity of the main review. The two focused corrections above are separately identified; all other source files still matched their initial hashes at focused recheck. + +| File | Initial SHA-256 | +| --- | --- | +| `src/db_writer/write.rs` | `e185526cfd2a31115a53117b9b089734b4d2eab41a02710c18266b8741900ab7` | +| `src/db_writer/phase.rs` | `0220a1a8cd6c301d4ad27f1c800d6ed014a32a111584ceb6d7a28ebc91e8fa13` | +| `src/execution/run.rs` | `0c2adc995840d52503545e7918e76e785ad41705320eb03c36844fb229489752` | +| `src/staging_writer/accumulator.rs` | `13737811c1dd9cb827573bf4ca7233265c393c3dbdc02214ceca9a3814199d4e` | +| `src/staging_writer/tests.rs` | `39360ae193bd29bc6a2e72ab9b188569a952944afa1b0f686224d9a96a8dc942` | +| `src/staging_writer/writer.rs` | `08d8cfbb0ec534f09e5ad7c13e5e90cb1ab397b80b51aae1356cb397431e2a55` | +| `tests/copy_phase_process.rs` | `a17912256764f14a4723e1611f1dd37fe9bc6625f6984ffb2ad2e478f3f8aef9` | + +**Baseline comparison / assumptions / side effects:** the baseline-adaptation GREEN verdict remains valid: no new owner, topology, platform, or Scope B premise is needed. The actual integrated implementation exposed the two findings above; this report does not retroactively turn the proposal review into runtime approval. Assumed only the explicitly approved PR baseline and pinned native provenance supplied in `native-inputs.json`; read source and execution rather than treating recalled historical Linux/older-checkout claims as current support. **No constants changed by this reviewer.** No repository source, Git state, dependency, service, model, memory, tracking, or daemon writes were performed; the sole output is this requested report. Reusable knowledge was assessed: existing uncertain-COPY-outcome memory already covers the durable lesson; pending implementation qualification warrants no filler memory. + +**Next review gate:** execute the focused diagnostic fix checks, reconcile named acceptance gaps, and provide the exact final source/evidence snapshot for same-context focused verification. Maintain the user's approval and release/platform decisions separately. + + +## Focused final-source addendum — snapshot 03 + +After the initial review, I read and independently compared the new clean-control regression and verified every current source hash against the coordinator's snapshot-03 manifest. Only the two already-reviewed production corrections and the test-oracle extension differ from snapshot 01: + +- `src/db_writer/write.rs`: `09598352b80e42f2c242e992b6377f57eee82d0acd3a0e308ddcf601f04d16c0` — identical helper relocated above tests; FR-002 resolved as described above. +- `src/db_writer/phase.rs`: `1500b94723ada235301c44f6e9c27ffcddeb3f13f03862de84a54b100ca0be7e` — actual COPY-query/checkpoint format helpers append distinct stable suffixes and the new oversized-tail regression exercises both through the bounded reader/assembler. **FR-001 source correction passes focused independent inspection.** Its execution remains pending, not passing. +- `tests/copy_phase_process.rs`: `ca61337099747e3356c4308bfcba7c6b73f8d2a604bef6808569509465567c3e` — the chunked build's read-only logical snapshot is now compared to a second normal CLI build from the same paths/content, fresh candidate, using the existing 32 MiB spill value. It compares all sorted node labels/IDs/native string representations (intended to include JSON properties), all sorted connector labels/endpoints, and both sampled search result vectors. Original independent 1,400-label/PK/endpoint expectations remain intact. Handles close before the second build; it is an independent fresh-candidate invocation, not replay of a failed phase. + +The extension directly addresses the missing same-source control comparison in evidence item 1. It neither weakens an assertion nor changes production behavior. **Its native `CAST(n AS STRING)` binding/representation and successful comparison are unexecuted at this addendum**; schema-wide introspection, every-property semantics, and all intended JSON content must not be claimed merely from reading the query. A binding failure would be a fixture failure to correct and rerun, not evidence that the product loses graph data. Native02's existing 2/2 pass does not cover this later source. + +**Current conclusion:** no unresolved confirmed source defect remains after the focused fixes, but final review/qualification is **pending the affected executed checks and the named acceptance evidence**, including the coordinator's full/native/resource/platform gates. No further source change is requested by this focused inspection. This addendum supersedes the initial report's statement that the control extension is only planned; all other limitations remain. No additional build/test/probe or repository/service write was performed by this reviewer. + + +## Focused execution addendum — findings resolved, final qualification still pending + +Read `diagnostic-final-01.log` and its JUnit: **14/14 pass**, including `oversized_native_error_preserves_query_vs_explicit_checkpoint_boundary`, bounded reader/cap tests, native query-result propagation, and no-replay schedule. Read `workspace-doctest.log`: **3/3 pass**. Read `clippy-final.log`: strict workspace check finishes successfully after the suffix/control edits. **FR-001 and FR-002 are now resolved by focused source inspection and affected passing execution.** These executions are coordinator-run; I started none. + +A later test-only addition changes `src/db_writer/write.rs` to SHA-256 `d1625f1bd5239eea6eb7dd2b85244ce93f2df13d1503007b6cbe37dddd782542`. I compared it with the previously reviewed write source: the sole additional change is `phase_requests_preserve_low_and_high_configured_pools`, checking phase-request serialization for **128/256/384 MiB**, 768 MiB worker budget and one thread. This is a proportionate test of the already-reviewed direct pass-through; no production semantics changed. Its passing execution is **not** in the 14-case diagnostic log and remains pending at this addendum. The earlier snapshot-03 manifest does not silently identify that later file; the current test/control/phase hashes otherwise remain as recorded above. + +**Pool-matrix follow-up verified:** `writer-pool-matrix-final.log` and JUnit record **15/15 pass**, explicitly including `phase_requests_preserve_low_and_high_configured_pools` (case 14), against the same current write SHA-256 `d1625f1bd5239eea6eb7dd2b85244ce93f2df13d1503007b6cbe37dddd782542`. This closes the narrow pool-test execution gap above; it is serialization/pass-through evidence, not measured RSS at each pool. + +**Optimized native clean-control follow-up verified:** independently read completed `native-profile-copy-final.log`: **2/2 pass** in the optimized `native-tests` profile, zero ignores/filtered cases, elapsed **220.65 s**. The current test source hash remains `ca61337099747e3356c4308bfcba7c6b73f8d2a604bef6808569509465567c3e`; all seven current source hashes match the reviewed corrections/pool-test snapshot. The same-source chunked-versus-32-MiB-control logical node/ID/native property-string comparison, all connector endpoints and sampled search-vector comparison execute successfully. Native `CAST(n AS STRING)` binds and produces equal compared results. The original expected 1,400 Function labels, all PK checks, genuine PK operator, early index sequence, connector expectations, file-byte bounds, and Managed/Direct post-COPY preservation/fresh-invocation checks remain in the passing suite. This **closes acceptance-evidence gap 1 at the fixture level**; it does not add production schema introspection or establish a general every-property integrity validator. Sampled chunked database-write high water is `129582232` bytes, distinct from the configured pool or deliberate RSS-abort evidence. + +**Final reviewer state at handoff:** no unresolved confirmed P0–P3 source finding. Full workspace, existing lifecycle/resource evidence and supported-platform qualification are still pending. A running run is not passing; the original full-suite snapshot cannot cover later source edits by assumption. Proceed with those checks and evidence reconciliation within approved Scope A; do not declare full acceptance/closure from this review alone. No constants changed or other writes were made by this reviewer. diff --git a/docs/specs/CBG-COPY-01/investigation-evidence.md b/docs/specs/CBG-COPY-01/investigation-evidence.md new file mode 100644 index 00000000..9bbba980 --- /dev/null +++ b/docs/specs/CBG-COPY-01/investigation-evidence.md @@ -0,0 +1,54 @@ +# CBG-COPY-01 — Prior investigation evidence + +This is a compact provenance record for the proposed fix, not executed verification of an implementation. The planning task ran no builds, native fault experiments, production writes or tests. Scope and approval remain in [spec.md](spec.md). + +## Decision-relevant observations already executed + +- Inspected CodebaseGraph source: `6aa5ba8bd99c3071e106a8bfc7a408652a8e4bb5`. Its writer/staging/pool calculation also compared equal to local `origin/main` `ca115690b226384164cf364458a0e3255b8908d3`; this does not establish lifecycle equivalence or identify the original incident executable. +- Host: macOS arm64. Temporary databases and isolated project copy only; official released native archives were SHA-verified. +- With official Ladybug 0.22.0, a Rust project copy changed only to `lbug = "=0.22.0"` and the corresponding lockfile passed Cargo check including tests and two focused native writer tests. This is integration evidence on one host, not approval to upgrade or a full-platform qualification. +- 0.22 retained the exact second unindexed COPY guard. Separate sequential COPY files with an early primary-key hash index worked. A JSON file-list COPY silently loaded only the first file in the evaluated fixture; do not substitute list/glob syntax for per-file COPY. +- In 0.22, suppressing automatic checkpoints let COPY report success but lose copied rows after abrupt `_exit` before close. Keeping the default automatic checkpoint retained the fixture's rows. This is one tested boundary, not an exhaustive durability proof. +- Official 0.22.0 checkpoint integrity probe twice reproduced [upstream #1406](https://github.com/LadybugDB/ladybug/issues/1406): a fixture child used `RLIMIT_FSIZE` of current file size + 256 KiB to force a CHECKPOINT I/O failure, reset the limit, then CHECKPOINT returned success. A primary-key lookup found `id=7` before close. A new read-only process saw 20 rows and a forced scan found `id=7`, while primary-key lookup returned zero. This supports lookup-vs-scan regression oracles; it does not prove the 0.19 incident's first failure or require disk-full tests in routine CI. +- The same synthetic failure workload under 0.19 prevented read-only reopen with a checkpoint-in-progress diagnostic. This is not evidence that 0.19 recovery is safe or that 0.22's failure is a new regression. The proposed fix avoids treating either failure as permission to retry a mutating statement. +- A toy 0.19 database opened read-only in 0.22 unchanged; after a 0.22 write/checkpoint, 0.19 could not reopen the upgraded copy. A legacy non-ASCII STRING primary-key fixture required rebuilding stale hashes. Migration/rebuild/rollback and matching extensions remain separate upgrade decisions. + +## Exact checkpoint observation excerpts + +```text +CHECKPOINT -> STATE:0 +IO exception: Cannot write to file ... Error: File too large +LIMIT_RESET +CHECKPOINT -> STATE:1 +before-close MATCH (p:Person {id:7}) RETURN count(*) -> 1 +``` + +```text +fresh-process readonly: +MATCH (p:Person) RETURN count(*) -> 20 +MATCH (p:Person) WITH p.id AS id WHERE id=7 RETURN count(*) -> 1 +MATCH (p:Person {id:7}) RETURN count(*) -> 0 +``` + +The child-only fault used synthetic scratch data. No live graph, daemon, production resource limit, project dependency or repository code changed during that evaluation. + +## Source and scope limits + +- Bulk COPY/checkpoint distinction: [PR1204](https://github.com/LadybugDB/ladybug/pull/1204) and [PR1361](https://github.com/LadybugDB/ladybug/pull/1361). PR1361 was unmerged at evaluation; no fix from an open PR is claimed as released. +- Evaluated latest stable: [v0.22.0](https://github.com/LadybugDB/ladybug/releases/tag/v0.22.0), published 2026-10-25, commit `4ca22377b85a33a10247410fe3944a5224b1169d`. +- Original DocumentationSource incident's first-attempt logs, exact binary and COPY plan were unavailable. Same-candidate replay is an established exposure, not an established incident diagnosis. +- Cross-platform qualification, representative workload limits and the proposed fail-closed implementation have not been executed by this planning work. + +## Local raw artifacts + +The compact observations above are portable. Local paths below are diagnostic pointers and hashes, **not** a substitute for retained final implementation JUnit/native-platform evidence. They may disappear with temporary storage. + +Original evaluation directory: `/private/tmp/ladybug-022-evaluation-dp_8zy5d`; final research assessment `evaluation.md`; native/crate inputs `provenance.json`; detailed engine/integration reports `engine-evaluation.md` and `integration-evaluation.md`. + +| Existing raw file | SHA-256 at planning readback | +| --- | --- | +| `checkpoint-integrity-observations.json` | `3b9ef4730ca17b2501ff3995b7fb25f166b98abc279717dd67c7752d49cd9684` | +| `failed-checkpoint-confirm.log` | `1a4000b2f68bbf88dfa50de8666adfd4e556ef6efc821336c24b0042931fd1de` | +| `cargo-focused-tests.log` | `306fe8982b34bf22cc6c44416904908b89d9dc15c1140cc84eac77d46f1ad38f` | +| `cargo-check.log` | `54a4d41815e508e67691e7280f4bd47365c4be51864e6d33b3f873f5cf9e8a91` | +| `no-close-observations.json` | `bd49c9cedd051e41ea68cab99426dc61f6a8f40c9ef61e8d6e5a77d7f6286cdf` | diff --git a/docs/specs/CBG-COPY-01/proposal-review-verification.md b/docs/specs/CBG-COPY-01/proposal-review-verification.md new file mode 100644 index 00000000..60a4ac97 --- /dev/null +++ b/docs/specs/CBG-COPY-01/proposal-review-verification.md @@ -0,0 +1,41 @@ +# CBG-COPY-01 — Focused proposal revision verification + +## Recommendation: proceed to the user's plan-approval decision + +**APR-001, APR-002 and APR-003 are resolved at the specification level. No unresolved material finding remains from this focused review.** Revision 0.2 supports the recommended Scope A without adding automatic Scope B retries, a production full-graph integrity validator, or a streaming-plan subsystem. + +This is a reviewer recommendation about **the plan only**, not user approval or permission to implement. Implementation behavior, tests, resource feasibility and platform qualification remain unexecuted gates. + +## Reviewed snapshot and method + +- Final frozen approval candidate: `spec-r0.2-final-candidate.md`, SHA-256 **`1a0ea5f860ce2764e1666bd6345faf9bb24929f35795255772c04380839b4dd2`**. +- Canonical `docs/specs/CBG-COPY-01/spec.md` was byte-for-byte equal to that final candidate and matched that exact hash during verification. +- Compared revision 0.2 with the frozen revision 0.1 and the original independent report. Canonical `docs/specs/CBG-COPY-01/proposal-review.md` preserves that original report unchanged. +- Verified the final candidate against the earlier revision 0.2 freeze (`dd0bc0dae859355529328b497dc613f1513bb891a11be0f5a3ca5b67945d7c22`). Only three changes occurred: review status now refers to the separate resolution record, the requested decision explicitly names error suppression and bounded diagnostics, and a stray space was removed. None changes the requirements or acceptance assessed below. This report's verdict is bound to the **final candidate** hash above. +- Reused the exact source evidence established in the original review. No new broad research or implementation verification was performed. Source locators below describe previously inspected code, not a claim that a fix now exists. +- Same independent reviewer context and mandated GPT-6.1 Sol / Max assignment; focused follow-up, not a substitute fresh final implementation review. + +## Finding resolution + +| Stable ID | Status | Revision evidence and assessment | +| --- | --- | --- | +| **APR-001** — native-error suppression | **Resolved for plan approval** | Scope A.1 (line 51), R1 (81), implementation step 3 (98) and A1 (108) explicitly remove the blanket native `Err` → success mapping for all three former substrings in both writer paths. Successful idempotent SQL/results remain successful. A1 now tests at the query-result boundary, so a runner-level error injection cannot conceal suppression inside a phase. The behavior change is exposed for sign-off (57). This directly covers the prior `write.rs:376–393` and `phase.rs:305–322` concern without inventing a general SQL parser or declaring every idempotent operation unsafe. | +| **APR-002** — bounded diagnostic ingestion | **Resolved for plan approval** | Scope A.4 (54), R6 (86), implementation step 3 (98) and A8 (115) bound ingestion before allocation and the assembled isolated-phase diagnostic under the **existing 64 KiB** budget. They explicitly cover selected-main guarded/inherited routes and avoid unnecessary successful-exit stderr reads. Metadata and truncation framing share the output budget. A8 checks bounded read requests/bytes or allocations plus an oversized file, not only returned string length. The scope correctly excludes later API envelopes, keeps typed resource errors and cleanup ordering, and adds no retention policy or increased constant. This addresses the prior full-file reads at old `phase.rs:135` and inspected main `phase.rs:210,281`. | +| **APR-003** — COPY-control-list scaling | **Resolved** | The risk table (127) states that retained/cloned statement lists grow with chunk count and that per-file bounds do not bound all control state independently of cardinality. A7 (114) records retained/control cost, statement/file count and high-water marks under unchanged limits. It distinguishes normal successful publication from a separate deliberate resource-abort test. R3 (83) reuses Graph Writer `node-81` / `resp-273`; no new architectural claim is invented. A streaming-plan subsystem remains conditional on measured necessity and separate approval. | + +## Affected assumptions and counterevidence rechecked + +- **Normal-build usability is a real gate.** A7 now requires the existing representative previously successful/default profile to publish correctly under unchanged limits. Safe termination of an intentionally over-budget fixture cannot pass that requirement. A failure returns to the owner rather than authorizing a higher ceiling or an implicit retry design. +- **Configured pool is not measured resident memory.** A7 explicitly avoids that inference and distinguishes the materialization calculator's admitted range from a plain low-level write request. The prior review's virtual-reservation and allocator-release counterevidence still defeats a claim that starting at the full configured pool is already proven infeasible. Actual RSS and workload results remain pending. +- **File bounds have precise semantics.** R3 now requires equal existing record/output limits and counts framing/header bytes, including the case where an unframed row fits but its complete file does not. This is compatible with the previously inspected sinks; it is not a new chunk-size setting or a native-memory-fit guarantee. +- **Search needs regression coverage, not a rewrite.** The new source-evidence row (37) acknowledges the existing contiguous-chunk iterator. A9 (116) places searchable terms in base and later files, reopens in a separate process, and checks genuine PK-index results as well as connectors/search. This matches the prior `search_index/build.rs:97–101,197–235` evidence and does not expand into sparse-staging repair. +- **No new correctness overclaim.** The revision retains uncertainty about the original incident and distinguishes preserved prior native observations from planning activity. It does not infer rollback from a native error, claim Ladybug 0.19 safe, or require routine host-disk-full experiments. + +## Remaining gates, not unresolved review defects + +1. The user selects Scope A and its availability/pool behavior, or requests a different plan. Removing hidden native-error suppression is explicitly part of the proposed behavior. **Scope B remains excluded** (59 onward). +2. Select and recheck the clean then-current-main baseline, preserving lifecycle ownership and containment safeguards. Unknown teardown still blocks destructive cleanup/new mutation. +3. Freeze/commit the agreed specification and record the actual user decision, exact scope/full SHA and required Scryer sign-off. Linear synchronization remains pending availability. Neither this verification nor the original review is approval. +4. After authorization, implement and execute the planned query-level, bounded-reader, actual-isolated-fault, reopen PK/connector/search, successful-resource-profile, deliberate budget-abort and normal platform/final-review gates. None has been executed by this review. + +Only this report was written. No specification/repository/service state, constants, Git state, database, daemon, dependency, model or tracking record was changed; no builds, tests or probes ran. The existing uncertainty memory already covers the reusable lesson, so no new memory entry is warranted. diff --git a/docs/specs/CBG-COPY-01/proposal-review.md b/docs/specs/CBG-COPY-01/proposal-review.md new file mode 100644 index 00000000..e54ff207 --- /dev/null +++ b/docs/specs/CBG-COPY-01/proposal-review.md @@ -0,0 +1,89 @@ +# CBG-COPY-01 — Independent adversarial proposal review + +## Recommendation: proceed with specified conditions — PLAN ONLY + +Scope A is a supportable minimum correctness fix. Remove same-candidate mutation replay, retain existing ownership/publication boundaries, and use the already implemented bounded sequential-file path. **Before freezing the approval candidate, resolve APR-001 and APR-002 below and incorporate the supplied APR-003 caveat.** These are narrow contract/acceptance changes, not grounds for requiring Scope B, an engine upgrade, a streaming-plan framework, or a full production integrity validator. + +This review is not user approval, implementation permission, executed qualification, or rollout approval. No plan-level architectural blocker was established. The repository owner must still choose the scope and clean-main baseline and approve the exact committed specification/full SHA. Scope B remains excluded. + +## Reviewed decision and evidence boundary + +- Frozen proposal: `spec-r0.1.md`, revision 0.1, SHA-256 **`54d51d60096d1038485f3d335fc25911bac4d092afbfaee034fff3e2697c64a2`**. Canonical `docs/specs/CBG-COPY-01/spec.md` matched that hash during review. +- Requested commitment: approval of the Scope A delivery plan, including all-mutating-phase fail-closed behavior, one configured request pool, and final JSON/CSV file bounds. Owner: repository maintainer/user. No approval record or implementation date is supplied. +- Source baseline: supplied HEAD `6aa5ba8bd99c3071e106a8bfc7a408652a8e4bb5`. The coordinator reports unchanged writer/staging/run/resource files against local `origin/main` `ca115690b226384164cf364458a0e3255b8908d3`. I additionally read that local main's phase file with read-only `git show`; its lifecycle differs, and both narrow phase concerns below remain present there. Then-current implementation main is deliberately not selected. +- Supporting artifacts read: `context.json`, `authority-and-contracts.json`, `loader-analysis.md`, `publication-analysis.md`, `proposed-review-amendment.md`, and the additive portable `docs/specs/CBG-COPY-01/investigation-evidence.md`. The latter two are **not** part of the frozen proposal hash. +- Required review skill and technical-feasibility, architecture/integration, operating-model, and staged-risk lenses were applied, including quantitative checks. Reviewer assignment is the required fresh/default GPT-6.1 Sol / Max configuration; no substitute reviewer was used. + +## Findings + +### APR-001 — State how native “already exists” errors fit the all-mutation failure rule + +**Priority:** Material; resolve before approval freeze. **Evidence type:** Established coverage/contract gap; safety consequence remains conditional, not an observed engine failure. + +**Proposal claim:** §3 Scope A.1 (line 46), R1 (line 76), §5 step 3 (line 93): any schema/hash/post-COPY/checkpoint error stops the candidate. A1 (line 103) injects an error at the phase-runner boundary. + +**Verified evidence:** `src/db_writer/write.rs:55–67,376–393` and `src/db_writer/phase.rs:213–216,254–260,305–322` pass schema/post-COPY SQL through `query_ignoring_existing`. That helper converts *any* native error containing `already exists`, `exists already`, or `already installed` into `Ok`. Local main preserves this at `phase.rs:457,502,563–581`. Consequently, merely deleting the retry loop can still let later statements/checkpoints/phases run after such a native `Err`; a runner-level injected error does not exercise this conversion. + +**Strongest counterevidence:** Idempotent catalog/extension setup is an existing intentional path. This review did not demonstrate a real mutating fault carrying those strings or show that an ordinary already-present catalog object is unsafe. Official v0.19.0 `install_extension.cpp:23–29` also reports the ordinary already-installed case as a successful output message, not necessarily an exception. Therefore do not label every harmless no-op as corruption or claim this caused the incident. + +**Remedy / resolution evidence:** Explicitly say whether Scope A removes/narrows this blanket error-to-success mapping or retains a precisely justified no-op exception. Successful idempotent SQL/result messages can remain successful. Do not treat a raw mutating error as proven harmless solely because a substring matches. Add a small query-result-level regression for schema/post-COPY error propagation in both isolated and fallback paths, plus the intended successful idempotent setup case. No general SQL parser or full-graph runtime validator is required. If an error exception changes the selected behavior, obtain the owner's decision rather than silently weakening R1. + +### APR-002 — Bound diagnostic ingestion, not only the returned stderr tail + +**Priority:** Material; resolve before approval freeze. **Evidence type:** Established implementation gap and incomplete planned resource oracle; no stderr-induced OOM was executed or observed. + +**Proposal claim:** §2 diagnostic evidence row (line 34), Scope A.4 (line 49), R6 (line 81), §5 step 3, and A8 (line 110) promise bounded original context under the existing child-error budget. + +**Verified evidence:** Old `phase.rs:135` calls `fs::read` on the complete stderr file; only `bounded_child_error` at `340–343` trims the last 64 KiB. Local main still does complete reads at `phase.rs:210,281`, then trims at `598–601`. These reads occur after child supervision finishes, including successful exits. Thus a short final error string does not imply bounded diagnostic allocation. With sufficiently long native stderr, ingestion can exhaust parent memory or obscure the very failure Scope A intends to preserve. A8 can pass a returned-length assertion while missing this mechanism. + +**Strongest counterevidence:** The displayed native tail is already bounded; normal logs may be small. The proposal adds no indefinite raw-log retention, and main's containment-controlled cleanup must remain intact. This finding does not require a new log-retention policy or claim a current incident exploited stderr size. + +**Remedy / resolution evidence:** Require a bounded file-tail/window read using the existing budget before allocating the diagnostic, on both relevant main execution routes. Bound the assembled returned context as well; reserve room for parent-known phase/target/pool metadata rather than allowing added prefixes to defeat the budget. Extend A8 to assert bounded ingestion and preserved primary/secondary ordering for oversized stderr, not just short displayed output. Keep native log retention and deletion under the existing owner/containment contract. A small helper is sufficient; no logging subsystem or increased constant is needed. + +### APR-003 — Incorporate the supplied COPY-control-list caveat + +**Priority:** Improvement; apply before final handoff. **Evidence type:** Established scaling fact; resource failure remains conditional. + +**Proposal locator:** Scope A.3 (line 48), R3 (line 78), and §7 chunk-overhead risk (line 121). Supplied amendment: `proposed-review-amendment.md`. + +**Verified evidence:** `accumulator.rs:695,798,825,942` retains one string per output file; `400–406` joins those lists; `staging_writer/writer.rs:30` clones the list. File/COPY count therefore grows with output volume divided by chunk size. The new file bound is not a cardinality-independent bound on Rust/control state. + +**Counterevidence and remedy:** The frozen draft already disclaims native-memory fit, keeps resource failure terminal, and anticipates more child/checkpoint overhead. There is no measured control-list blocker. Apply the supplied amendment and include statement/file count and retained/control RSS in the existing resource qualification. Keep the existing list design unless evidence demonstrates a blocker and the user approves wider scope. For R3's budget evidence, reuse existing Graph Writer `node-81` / `resp-273` (“Stage graph rows within the configured memory budget”), returned by scoped Scryer orientation; no invented claim is needed. + +## Owner choices and execution gates — not defects or executed passes + +| Item | What remains to decide/prove | Consequence | +| --- | --- | --- | +| Availability policy | User explicitly accepts one attempt for **all** mutating phases and fallback writes. An immediate build may fail where old adaptive execution succeeded. Existing pending-refresh scheduling remains unchanged; a later ordinary invocation is not Scope B. | Required scope decision before implementation. No evidence requires automatic candidate reconstruction now. | +| Pool policy | Select one configured pool from the start, or the explicitly described 256 MiB-start fail-closed alternative. A failed resource qualification must return to the owner; it must not silently raise limits or implement Scope B. | Required behavior choice, not an inferred approval. | +| Baseline and ownership | Choose clean then-current main, preserve its lifecycle fixes, reread source anchors and relevant Scryer drift. Distinguish Managed generation identity from Direct's recreated candidate at its established destination-scoped spelling; “fresh” need not mean a new Direct filename. | Old-checkout source does not establish current teardown safety. Unknown containment blocks destructive cleanup and new mutation. No loader-level arbitrary-path reset. | +| Actual isolated fault path | A2 must reach an authenticated fixture barrier **after real scratch mutation/checkpoint**, then lose/fail acknowledgement. Record launched helper identity, mutation evidence, one invocation, no later mutation/publication, and prior-active preservation. | A pre-open readiness failure or in-process fallback does not pass this criterion. A deterministic engine disk-full reproduction is not required. | +| Reopen correctness | A3/A9 must reopen in a separate process and compare the expected fixture's complete ID set, genuine PK-index lookups, exact connector endpoints, and cross-chunk search results. Establish that the selected query really uses the intended lookup path for the pinned native version. | Counts alone, or query syntax without a valid lookup oracle, do not establish the required regression proof. This remains a targeted fixture, not a production full-ID scan. | +| RSS and supported workload | Run at least the existing representative successful/default profile under unchanged limits, as well as A7's deliberate budget-abort case. Record effective pool, parent/child high-water, statement/file count, and phase overhead. No invented performance SLA is needed. | “Correctly aborts over budget” alone does not establish that normal builds remain usable. Results are pending execution, not a reason to reject a bounded implementation plan now. | +| Approval and qualification | Resolve findings, freeze/commit spec, record real user decision/full SHA and Scryer sign-off; synchronize Linear when dispatch becomes available. Execute normal affected/native/platform checks and independent final review. | Drafts, this recommendation, attached tests, local passes, and merges do not replace these distinct gates. | + +### RSS arithmetic and counterevidence + +Default configuration is worker **768 MiB**, Rust **384 MiB**, spill **32 MiB** (`src/api/context.rs:251–253`). The request calculator selects `min(768−384,384) = 384 MiB` (`protocol.rs:169–190`). In the inherited phase branch the 16 MiB supervisor headroom makes its threshold **752 MiB** (`phase.rs:104–106`; local main `171–173`). If the parent actually retained 384 MiB and native pages used another 384 MiB, their 768 MiB sum would exceed that threshold by 16 MiB before other overhead. The guarded main branch instead passes the existing worker ceiling to the guardian (`main phase.rs:238–247`); qualify the actual selected path. + +**That calculation does not prove the minimum fix infeasible or imply an instant 384 MiB RSS allocation.** Rust's configured allowance is not measured retained RSS. The writer releases unused allocator pages before the first child (`write.rs:91–94`). Official **v0.19.0** `BufferManager` creates VM regions and tracks claimed memory (`buffer_manager.cpp:77–85,336–368`); `VMRegion` uses `MEM_RESERVE` on Windows or anonymous `mmap` on Unix (`vm_region.cpp:30–76`), with physical pages claimed as used. A higher configured pool can change resident usage and metadata, but is not itself proof of a fully resident pool at startup. The draft's warning and stop condition are appropriate; measure rather than declaring either safety or infeasibility from configured ceilings. Sampled RSS enforcement is not a mathematical instantaneous-peak bound. + +## Supported strengths and rejected challenges + +- The strongest causal case survives: a reported mutation/checkpoint failure does not establish rollback. Stopping the complete candidate and returning to its storage owner avoids replay without introducing a new retry state machine. The existing owner already separates pipeline failure from publication (`materialization.rs:311–319,482–503,903–929`). +- The early hash-index step already exists after a successful first chunk, in both writer paths (`write.rs:103–157,248–320`). This is not a missing repeated-table detector. Failing COPY must not be followed by compensating index creation. +- Equal record/output spill limits reuse existing exact framing/header checks (`accumulator.rs:721–755,851–899`). Sequential files avoid depending on the evaluated JSON file-list behavior. The error for a single framed row that cannot fit is explicit, not silent truncation. +- Search is not a single-file blocker: independently read `search_index/build.rs:97–101,197–235`; it enumerates the base JSON file and contiguous suffixed chunks. Preserve all-chunks search parity in A9; no reader rewrite is presently justified. +- The fallback fresh-path check generally prevents a second native load after candidate creation (`write.rs:170–176`), so its retry wrapper must not be described as proven duplicate COPY execution. It can replace the original failure with “candidate database path already exists”; removing write-operation replay still has a sound purpose. +- Scope B correctly treats teardown, candidate/sidecar rebinding, immutable staged inputs and publication as owner-level concerns. Existing publication recovery is not a promise to reconstruct failed builds. The draft correctly avoids conflating Direct Prepared recovery with prepublication abandonment. +- PK/connector regressions are warranted. A new full runtime integrity validator is not required by the verified safety mechanism, and this review does not request one. + +## Research performed, limits, and reusable knowledge + +Read repository `AGENTS.md`, the planning/review skills and applicable lenses; discovered k-wiki bundles, recalled the existing uncertain-COPY memory, retrieved storage lifecycle context, and used scoped Scryer rules/orientation. Semantic graph discovery and explicit context preceded repository source reads. Linear was confirmed pending and had no dispatchable delivery tools; no tracking state was changed. No broad architectural-model dump was used. + +On **2026-10-26**, inspected primary upstream records [issue #1406](https://github.com/LadybugDB/ladybug/issues/1406), [PR #1204](https://github.com/LadybugDB/ladybug/pull/1204), and [PR #1361](https://github.com/LadybugDB/ladybug/pull/1361), plus versioned v0.19.0 [buffer manager](https://github.com/LadybugDB/ladybug/blob/v0.19.0/src/storage/buffer_manager/buffer_manager.cpp), [VM region](https://github.com/LadybugDB/ladybug/blob/v0.19.0/src/storage/buffer_manager/vm_region.cpp), and [extension installer](https://github.com/LadybugDB/ladybug/blob/v0.19.0/src/processor/operator/simple/install_extension.cpp). #1406 remains open with an open linked fix; #1361 is unmerged/draft. Neither is a released safety guarantee. PR1204 explicitly identifies bulk COPY row payloads as absent from WAL. These sources support uncertainty and the regression oracle, not a diagnosis of the original 0.19 incident. + +Read the preserved official 0.22.0 arm64 observation record and rechecked two raw-file hashes against the supplied portable provenance: `checkpoint-integrity-observations.json` = `3b9ef4730ca17b2501ff3995b7fb25f166b98abc279717dd67c7752d49cd9684`; `failed-checkpoint-confirm.log` = `1a4000b2f68bbf88dfa50de8666adfd4e556ef6efc821336c24b0042931fd1de`. The reopened scan/PK disagreement is prior executed evidence, **not a test performed in this review**. It neither proves 0.19 safe nor proves the original incident's first failure. Portable evidence retention is a useful addition to the final approval snapshot. + +No builds, tests, benchmarks, fault probes, database operations, Git mutations, dependency installs, daemon changes, repository edits, model/tracking writes, or constants changes were performed by this reviewer. The report is the only file written. Resource feasibility and actual new implementation behavior remain unexecuted gates. The existing uncertainty memory already captures the reusable lesson; this read-only review warrants no new memory/model entry. diff --git a/docs/specs/CBG-COPY-01/resource-ceilings-amendment.md b/docs/specs/CBG-COPY-01/resource-ceilings-amendment.md new file mode 100644 index 00000000..fbc9f878 --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-ceilings-amendment.md @@ -0,0 +1,44 @@ +# CBG-COPY-01 — Resource-ceiling removal amendment + +## Decision and authority + +Revision R1, 2026-10-26. Baseline: PR153 head `8e47d2b3c74b72b56a0b6b212d1bf939c8088ca7`. This additive amendment preserves the historical [Scope A spec](spec.md) and [approval](approval.md). + +The user requested removal of the resource ceiling because it was an optimization rather than desired production behavior. After the assistant offered removal of hard budget enforcement and its options while retaining streaming/chunking, diagnostics and ownership/containment, the user selected: **“remove only the enforcement and options”**. Implementation and update of existing PR153 are authorized for this scope. No merge/deployment is authorized. + +Scryer change `chg-vhbvd3` rewords existing `resp-263`, `resp-271`, `resp-273`, `resp-274` to remove configured-hard-memory promises while retaining failed-candidate abandonment, bounded execution window, deterministic spill/merge and staged search. Same-scope user sign-off recorded. No new topology or directives. Linear remains verification-pending. + +## Requirements + +| ID | Observable contract | +| --- | --- | +| CBG-COPY-01/RM1 | Remove `worker_memory_mib` and `rust_memory_mib` from supported CLI/config/API/MCP/native materialization requests, including defaults, validation, propagation and health claims. Do not add an “unlimited” replacement or an enormous sentinel limit. Existing stored fields follow ordinary unknown-field deserialization; no compatibility shim/migration. Old CLI flags are unsupported. | +| CBG-COPY-01/RM2 | Remove application RSS-triggered termination and artificial source/partition/artifact estimate rejection, plus the derived 128–384 MiB Ladybug pool policy. Use native-engine buffer-pool defaults instead of another application ceiling. Keep observation where useful, normal cancellation/owner-loss/unexpected-exit teardown, and fail-closed publication on real errors. | +| CBG-COPY-01/RM3 | Keep `spill_chunk_mib`, `max_parallelism` and single-thread controls as optimization choices. Continue sequential COPY, early indexes, external merge, spill and bounded queues. Chunk targets trigger flush/rotation, not admission failure: an individually large valid record can occupy its own chunk. Normal allocation/I/O errors and true encoding/format limits still apply. Do not restore whole-table buffering. | +| CBG-COPY-01/RM4 | Preserve Scope A: no same-candidate mutation replay, no existing-object error suppression, original error primary, bounded diagnostic ingestion/context and COPY-query versus explicit-checkpoint distinction; preserve prior published graph on failed candidate. | +| CBG-COPY-01/RM5 | Preserve containment/ownership/leases, cancellation, unknown-teardown evidence, corruption/path/format checks, protocol/framing/diagnostic bounds, read-format validation and bounded disk-cleanup pass policy. No retention/platform policy redesign or dependency upgrade. | + +Natural OS/native OOM and full-disk errors remain possible; this is removal of application workload enforcement, not a guarantee of unlimited physical resources. The public worker/Rust budget knobs are removed, while batching/concurrency tuning remains. Automatic candidate reconstruction remains excluded. + +## Implementation sequence and ownership + +1. Public propagation: remove worker/Rust fields from request/config types, CLI/help/MCP schemas, refresh/daemon options and health output; update real callers and fixtures rather than leaving dead fields. Shared contracts have one writer. +2. Runtime: remove RSS-kill policy from worker/native/guardian paths and budget estimates from source, partition and artifact load. Use native pool defaults. Retain metrics when practical and exact cleanup/cancellation ownership. +3. Streaming: keep spill/merge/file rotation but flush an oversized valid record into its own run/chunk. Keep security/corruption/read-format and maintenance-pass limits; do not create a general resource framework or a fake maximum. +4. Verify: replace obsolete budget-rejection tests with no-ceiling behavior, retain real CLI COPY/reopen/PK/connector/search and Managed/Direct preservation, and rerun ownership/cancellation tests. Update curated wiki through k-wiki. Require independent proposal and final review with GPT-6.1 Sol/Max; backend workers GPT-6 Luna/High. Detailed briefs/prerequisites/file ownership live in Scryer. + +## Acceptance + +| ID | Testable pass condition | +| --- | --- | +| CBG-COPY-01/RA1 → RM1 | CLI parsing/help, config, typed requests and MCP schema expose no worker/Rust memory option. Old stored fields cannot continue enforcing a limit; ordinary builds need no replacement ceiling. Test actual entrypoints, not magic-string-only checks. | +| CBG-COPY-01/RA2 → RM2,RM5 | Controlled fixtures exceeding a deliberately small former budget are not rejected by application estimates or RSS policy. No live-system exhaustion experiment. Source/partition/artifact and worker/phase paths contain no artificial budget admission/kill; cancellation/owner-loss still stop owned work and preserve evidence. | +| CBG-COPY-01/RA3 → RM3 | Small spill targets still rotate; an oversized valid JSON/CSV/spill/search record succeeds as an individual chunk rather than failing solely for target size. Logical rows, IDs, JSON, endpoints and search match a control after separate-process reopen. No whole-table fallback. | +| CBG-COPY-01/RA4 → RM4,RM5 | Existing real COPY acknowledgement-failure, early-hash/chunk/reopen, scan-versus-PK, connector/search, Managed/Direct prior-bundle and independent rebuild tests pass. Existing unknown-containment/cancellation/owner-loss behavior remains; no retry or unsafe cleanup is added. | +| CBG-COPY-01/RA5 | Formatting/strict Clippy, affected/full suites, native-test profile and ordinary macOS ARM64/Windows x86_64 CI pass or are explicitly pending. Keep exact source/binary/native provenance and failed attempts. No hidden replacement ceiling, production fallback for tests, weakened unrelated assertions or silent platform expansion. | + +## Risks and qualification + +Removing enforcement can increase memory/disk use and expose real OS/native allocation or I/O failure. The user selected that behavior. Spill targets are not maximum accepted record sizes; test framing/rotation and merge behavior to avoid infinite rotation or an unbounded batch. Public-field removal touches many consumers, so compile all targets/features and inspect serialized schemas. Cleanup's bounded per-pass budget and security/protocol framing remain outside this workload-ceiling removal. + +Earlier long-running tests use frozen pre-amendment binaries; their eventual results are labelled as such and do not qualify this changed source. No R1 implementation edits exist at creation. Commit this amendment and bind the explicit user approval before dependent implementation; independent review and actual qualification remain separate gates. diff --git a/docs/specs/CBG-COPY-01/resource-ceilings-approval.md b/docs/specs/CBG-COPY-01/resource-ceilings-approval.md new file mode 100644 index 00000000..74b8e9ad --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-ceilings-approval.md @@ -0,0 +1,10 @@ +# CBG-COPY-01 — Resource-ceiling amendment approval + +- Approved canonical amendment: [resource-ceilings-amendment.md](resource-ceilings-amendment.md), revision R1. +- Full specification commit: `c8dd4ea9ca2a6f992756a2a41828396a27eabb6d`. +- File SHA-256: `40fa2007539bf26e13a88a3c894428e3cee9dc46230d647e4be6bc1c2ca9b696`. +- User decision, this conversation, 2026-10-26: **“remove only the enforcement and options”**, answering whether to remove hard resource enforcement/options while retaining streaming/chunking, bounded diagnostics and ownership/containment. +- Implementation applies after PR153 head `8e47d2b3c74b72b56a0b6b212d1bf939c8088ca7`; update existing PR153, do not merge/deploy. Earlier Scope A approval stays intact except the hard-workload-budget requirements explicitly amended here. +- Scryer `chg-vhbvd3` records changed existing intent and the real same-scope user sign-off. Linear remains unavailable pending verification; no synchronization is claimed. + +Independent review and executed qualification remain separate from this actual user approval. Removing application workload enforcement does not remove physical OS/native limits or correctness/security/cleanup bounds. diff --git a/docs/specs/CBG-COPY-01/resource-ceilings-execution.md b/docs/specs/CBG-COPY-01/resource-ceilings-execution.md new file mode 100644 index 00000000..d8057ec8 --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-ceilings-execution.md @@ -0,0 +1,25 @@ +# CBG-COPY-01 R1 — Resource-ceiling removal execution + +[Approved amendment](resource-ceilings-amendment.md) and [exact user approval](resource-ceilings-approval.md) govern this follow-up after `8e47d2b`. Historical Scope A reports are retained, not relabelled as post-removal evidence. + +## Implemented scope + +Removed worker/Rust memory options from CLI, config, API, native requests and refresh health; removed RSS-triggered worker/phase termination, artificial source/partition/artifact admission estimates and the application-derived native pool. Native database opens use the actual Ladybug defaults with independent read/write thread tuning preserved. Removed the obsolete application `MemoryBudgetExceeded` error contract rather than passing enormous sentinel limits. + +Retained spill/chunk and concurrency options as optimizations. Normal rows rotate at the target; individually larger valid records form their own chunks/runs. Merge opens at most the existing fan-in per level and progresses through multiple jumbo runs. Search documents retain the existing read-format bound; targets no longer reject a valid document solely for exceeding a batch size. + +Unchanged: no same-candidate mutation replay, native result error propagation, original-error primacy, bounded diagnostic ingestion/context, COPY-query versus explicit-checkpoint labels, Managed/Direct publication and previous-bundle preservation, containment/ownership/cancellation, corruption/path/format checks, protocol bounds, cleanup per-pass policy, dependency/native version and supported platforms. + +## Executed evidence + +[Machine-readable checks and limitations](resource-removal-evidence/checks.json) identify commands/log hashes and failures corrected during integration. Local macOS arm64: 134 focused cases, 42 config/API/ownership cases, 2 real CLI COPY/reopen/control/preservation cases, the legacy stored-budget case, the owned-allocation case, mixed/jumbo merge cases, actual MCP schema case and 3 doctests pass. Strict all-workspace/all-target/all-feature Clippy passes. Native-test profile compiles. The corrected jumbo native JSON oracle passes (1/1); [independent final review](resource-ceilings-final-review.md) reports no confirmed P0–P3 source defects and verifies the corrected oracle and its passing log. Final formatting, all-target/all-feature workspace compilation and strict Clippy pass on the reviewed hashes. + +The first jumbo native test incorrectly expected the syntax `text` preview to hold a complete 2 MiB comment. The parser intentionally keeps 160 characters there. Scratch read-only native inspection found the full value in JSON metadata (2,097,217 encoded bytes). The corrected oracle reads/parses that field; no product truncation fix or weakened logical expectation was introduced. + +Natural allocation, engine sizing and I/O limits remain; removing application enforcement does not promise unlimited resources. The existing search read-format guard, security/protocol bounds and maintenance per-pass quotas remain deliberately unchanged. + +## Qualification and handoff + +Scryer change `chg-vhbvd3` records the approved four existing-claim amendments; `chg-67epjd` records implementation ownership. Registered model/graph tools still reference the original checkout; no passing test verdict is falsely attached to old source fingerprints. Linear remains verification-pending. Canonical wiki explanations were updated through k-wiki, then transferred with PR-specific lifecycle qualifications preserved. Validation/links/site build passed; stale old memory-budget operational recipe was quarantined without erasing history. + +Additional post-removal cancellation and unknown-containment process cases each passed (1/1). These execute the post-removal source-bound binary, not an earlier budget-enforcing binary. Full post-removal workspace/native-platform qualification remains with the new PR153 CI. Earlier pre-amendment native lifecycle16/16 pass is retained only as historical evidence; its interrupted long full-workspace run is not a passing result. No merge, deployment, daemon restart or live graph rewrite is performed. diff --git a/docs/specs/CBG-COPY-01/resource-ceilings-final-review.md b/docs/specs/CBG-COPY-01/resource-ceilings-final-review.md new file mode 100644 index 00000000..94416b1a --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-ceilings-final-review.md @@ -0,0 +1,54 @@ +# CBG-COPY-01 R1 — independent final code review + +## Verdict: source GREEN; qualification remains pending + +**No confirmed P0, P1, P2 or P3 production defect survived review.** The reviewed implementation follows the approved removal of enforcement/options without replacing the ceilings with sentinel limits. The corrected native-jumbo reopen test and actual MCP schema check now pass. This is not an unconditional acceptance/release pass: final affected/full-suite and platform checks remain separate gates. No merge/deployment approval is implied. + +### Identity and authority + +- Actual reviewer: `openai-codex/gpt-6.1-sol`, thinking `max`, default/fresh independent context, as recorded in `review-gates-and-model.json`; reviewer did not implement changes. Required repository `AGENTS.md`, adversarial-review skill and engineering lenses were read. +- Actual source: `/private/tmp/codebasegraph-copy153-scope-a`; baseline `8e47d2b3c74b72b56a0b6b212d1bf939c8088ca7`; documentation HEAD `82077d051efb3ac5c38f5aba0a0ec88f677c1e36` plus uncommitted integrated source/test changes. +- Governing amendment: `docs/specs/CBG-COPY-01/resource-ceilings-amendment.md` R1, approved commit `c8dd4ea9ca2a6f992756a2a41828396a27eabb6d`, verified file SHA-256 `40fa2007539bf26e13a88a3c894428e3cee9dc46230d647e4be6bc1c2ca9b696`, approval companion and historical Scope A. Live read-only Scryer rules/orientation and `chg-vhbvd3` four reworded claims were checked; wiki/memory recalled. No model/tracking synchronization is claimed. +- Reviewed 54-file source/test snapshot: `final-source-hashes-02.json`, with the following later **test-only** additions/final formatting inspected (current source hashes in `final-source-hashes-03.json`; its three wiki changes are outside this code-review boundary): + - `src/adapters/cli/tests/mcp.rs`: `22cd4cef51368a0f3050641e7e437d7fbc4d798e6337b87c3bf1b446cf3081bc` + - `src/staging_writer/spill.rs`: `a1870eac16d7299ef937178b9520e68ff7603a308b7a5f59ed1b63c248035efd` + - `tests/copy_phase_process.rs`: `dbff1132733d5557a7108033f9bb41b8e5968357614c1b1bb8a4344fc046424b` +- Original-root graph discovery/context preceded actual-worktree source inspection; newer modules were followed through imports. The registered graph is not a fresh index of this worktree. Source and baseline diff, not stale graph coordinates, supply the conclusions below. + +## Confirmed findings + +**None (P0–P3).** The first native-jumbo run used an invalid full-text oracle; its corrected metadata oracle now passes without production changes. The failed attempt remains recorded, not counted as passing evidence. + +## Challenged contracts and counterevidence + +1. **Public removal and error contract (RM1/RA1).** Fields/defaults/validation/forwarding were deleted through public `MaterializationRequest`, native request, config, refresh overrides/status, CLI and MCP startup. Spill/concurrency/parallel controls remain. Config uses ordinary Serde unknown-field behavior; old CLI flags fail parsing. `src/api/core.rs:1281–1283` now maps ordinary materialization errors without the obsolete memory-specific branch; removal of its narrowly corresponding test does not remove unrelated error mapping. `MemoryBudgetExceeded` and pool-bearing constructors/exports are gone, consistently with the approved contract, not a compatibility shim. MCP tools derive from the registered descriptor schemas (`src/adapters/mcp/tools.rs:10–43`, `src/api/core.rs:139–261,584–644`); materialization is not an MCP tool and no registered schema carries these knobs. The new actual tools/list negative assertions are at `src/adapters/cli/tests/mcp.rs:208–212`. + +2. **No replacement workload ceiling (RM2/RA2).** Source, partition and artifact estimates are now observations, not admission decisions (`src/execution/parallel.rs:216–318`, `src/artifact_store.rs:508–545`). Snapshot length/hash, artifact envelope/key/payload/raw-row validation remain. Native opens use `SystemConfig::default()` with only independent read-only/thread settings (`src/db_writer/access.rs:114–149`, `src/db_writer/phase.rs:552–555`), not an oversized application constant. Exact cached lbug 0.19 Rust SDK defaults pool size to zero; its native constructor resolves zero by physical-memory auto-sizing. Native/OS/address-space limits still exist and are not promises of unlimited capacity. No dependency change is needed for the approved behavior. + +3. **Streaming and jumbo merge progress (RM3/RA3).** `SortedSpool::push` flushes existing rows before overshoot and a jumbo singleton immediately afterward (`src/staging_writer/spill.rs:153–194`). Merge levels group at most 32 runs and strictly reduce any count above 32 (`:197–236`); heap-budget rejection was removed, not replaced by whole-table buffering. JSON/CSV sinks rotate only nonempty chunks, avoiding endless empty rotation on a jumbo row (`src/staging_writer/accumulator.rs:715–759,826–875`). u32 spill framing remains. Search occurrence spill remains bounded-fan-in; chunk-derived document admission is gone, while the existing independent 1 MiB reader-format limit is now shared with the writer (`src/search_index/io.rs:57–75`, `model.rs:3`). This format restriction is approved RM5 scope, not a concealed chunk ceiling. + +4. **Failure/ownership preservation (RM4/RM5).** The diff removes RSS termination predicates and budget-result plumbing but retains guardian cancellation, owner-loss, teardown proof, receipts and unknown-containment handling (`src/process_lifecycle/mod.rs:400–547`, `src/materialization_worker.rs:183–296`). RSS high-water observation remains. Database leases and phase ownership remain. COPY remains sequential with early hash indexes, no same-candidate replay or existing-object suppression; the scheduling/error tests and real Managed/Direct preservation test support this. Maintenance's own pass budgets/`ResourceLimit`, metadata/diagnostic framing and corruption checks were not removed. + +5. **Private process protocol compatibility.** Removed fields do not invalidate old receipts/new readers: ordinary unknown fields are ignored and the baseline's removed receipt fields are optional. Bootstrap registers the current executable for worker/phase launches (`src/bootstrap.rs:12–19`); guard/phase paths launch that executable. Same-version intended operation is coherent. This does **not** establish mixed old/new executable support after replacement-in-place: unchanged v2 is not a cross-release negotiation mechanism. Keep the existing coordinated daemon/helper restart precondition; no new migration/framework is justified by the inspected scope. + +## Executed evidence inspected; not executed by reviewer + +- `check-04.log`: all-targets/features compile success before later minor core/test/lint edits. +- `focused-unit-02.log`: **134/134 passed**. Earlier focused01 had a CLI-test message mismatch; parent changed only the assertion. Focused02 predates the final dead-parser/import cleanup, which subsequent strict Clippy compiled. +- `clippy-02.log`: strict Clippy success; `normal-cli-build-final.log`: ordinary CLI build success without warnings. Final test-only additions postdate these runs and need affected rechecks. +- `public-settings-tests.log`: **42/42 passed**; `doctests.log`: **3/3 passed**. +- `native-copy-01.log`: **2/2 passed**, including small-target chunk/control parity, reopen/PK operator/connector/search and COPY-acknowledgement failure with Managed/Direct prior-bundle preservation and independent later rebuild. +- `legacy-budget-config-process.log`: **1/1 passed**; a 4 MiB source refresh succeeds with ignored stored worker/Rust values 2/1 MiB. +- `ownership-no-ceiling-process.log`: **1/1 passed**; fixture-owned 8 MiB loader allocation stays alive and recovers. This is not an actual exceedance of the old 512 MiB worker setting. +- `final-added-unit-cases.log`: **2/2 passed**, 1,027 jumbo runs through multiple levels and mixed small/jumbo sorted-value parity. +- `actual-mcp-schema-test.log`: **1/1 passed**, actual stdio tools/list schema has no retired memory fields. +- `native-jumbo-record-02.log`: **1/1 passed** after a test-only oracle correction. Real CLI spill target 1 MiB accepts a >1 MiB staged COPY, and a new read-only database open in the independent test process parses metadata JSON and confirms the full 2 MiB payload; search also succeeds. See `tests/copy_phase_process.rs:282–319`. +- `native-profile-compile.log`: optimized `native-tests` profile compiles the COPY and refresh-recovery integration binaries; compilation alone is not execution. + +## Remaining conditions and limitations + +- **RA3 failed-attempt resolution:** `native-jumbo-record.log` failed because it expected the complete comment in `SyntaxCapture.text`, which is a preview field. The parent diagnosed a 160-character preview and >2 MiB `metadata.canonical_key`; the corrected test reads `CAST(n.metadata AS STRING)`, parses the JSON and checks full padding. I inspected that corrected assertion and the passing `native-jumbo-record-02.log`. No production change was made or required. The existing native control case separately proves logical rows/IDs/endpoints/search parity; the new jumbo case proves full large-value persistence/reopen and search, but does not independently compare every jumbo graph row to a different spill-target control. +- Complete final affected compile/format/strict-Clippy checks against the final hashes, any remaining ownership/cancellation/owner-loss regressions, broader/full suites, optimized native-test execution and ordinary macOS ARM64/Windows x86_64 CI, or explicitly retain their pending qualification labels. Actual MCP schema, mixed-jumbo and native-jumbo checks have now passed. Pre-amendment binaries are not R1 qualification. +- In-place upgrades with old running daemons/helpers remain conditional on the existing coordinated restart rule, not verified mixed-version support. Increased native/OS memory use and real OOM/full-disk failure are the user's approved tradeoff, not a defect to reintroduce ceilings for. + +**Side effects/assumptions:** only this report was written; no implementation, constants, Git/model/tracking/service state or dependencies changed by reviewer; no builds/tests/probes launched. Scope is the approved enforcement/options amendment, not an audit or redesign of all graph execution. No newly established durable lesson warrants a memory write under the read-only task. diff --git a/docs/specs/CBG-COPY-01/resource-ceilings-proposal-review.md b/docs/specs/CBG-COPY-01/resource-ceilings-proposal-review.md new file mode 100644 index 00000000..c223e832 --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-ceilings-proposal-review.md @@ -0,0 +1,21 @@ +# CBG-COPY-01 R1 — independent proposal review + +## GREEN — proceed to scoped implementation + +**No unresolved material or decision-blocking findings.** The frozen amendment and supplied shared contracts support the already approved removal of enforcement/options. This clears the proposal-review gate only; implementation correctness, platform qualification, merge and deployment are separate. + +**Reviewed:** 2026-10-26; fresh independent context; required adversarial-proposal-review skill and repository `AGENTS.md`. Requested configuration: GPT-6.1 Sol / Max, default, fresh context. Frozen R1 SHA-256: `40fa2007539bf26e13a88a3c894428e3cee9dc46230d647e4be6bc1c2ca9b696`. Actual source: `/private/tmp/codebasegraph-copy153-scope-a`, named baseline `8e47d2b3c74b72b56a0b6b212d1bf939c8088ca7`. Also read `discovery.json`, both inventories, `proposed-shared-contracts.json`, historical Scope A spec/approval, live read-only Scryer amendment/rules/orientation and relevant wiki/memory. Original-root graph discovery/context preceded actual-worktree source reads. Linear remains verification-pending. + +## Findings and design assessment + +- **Scope is coherent.** Delete only worker/Rust budget options, fields, defaults, validation, propagation and health claims; keep spill/chunk and concurrency tuning. Remove source/partition/artifact estimates and RSS-triggered termination. Keep bounded queues, sequential COPY, early indexes, ownership/leases, cancellation, unknown-containment evidence, corruption/path/format checks, diagnostics and cleanup-pass bounds. New user authorization overrides historical budget-preservation requirements, not unchanged Scope A. Evidence: `src/protocol.rs`, `src/execution/parallel.rs`, `src/execution/run.rs`, `src/artifact_store.rs`, `src/materialization_worker.rs`, `src/db_writer/phase.rs`, `src/process_lifecycle/mod.rs`. + +- **Native defaults are feasible, not “unlimited.”** Exact cached `lbug-0.19.0/src/database.rs` supplies `SystemConfig::default()` with buffer-pool size zero; bundled `lbug-src/src/main/database.cpp` resolves zero through native auto-sizing. Use that path while retaining the independent single write thread/read-only setting. Remove application pool constants and pool-bearing write/phase contracts and the old `open_ladybug_database_with_limits` API; do not pass huge sentinel values. Native sizing/format/address-space bounds and OS/allocation/I/O failures remain. Evidence: `src/db_writer/access.rs`, `src/db_writer/request.rs`, `src/db_writer/phase.rs`, `src/staging_writer/writer.rs`. + +- **Search-limit ambiguity is resolved.** The runtime inventory's broad retention wording must not preserve chunk-derived write admission: remove the chunk-size rejection in `src/search_index/io.rs` and occurrence admission in `src/search_index/build.rs`. Keep `u32` framing, 256-byte term behavior and the independent 1 MiB document read guard in `src/search_index/read.rs`. “Valid jumbo” means above a small target but within retained format/read-security limits; it does not promise arbitrary-sized search documents. R1 RM3/RA3 and RM5 permit this without another scope decision. + +- **Jumbo support must survive merging.** Singleton spill/output chunks alone are insufficient: current grouping/heap checks also reject target-sized overshoot. The supplied contracts correctly require bounded fan-in and guaranteed progress, permitting current record heads above the tuning target without whole-table buffering or opening every run. RA3 verification must include **two or more jumbo runs, mixed small/jumbo records, and more than 32 runs requiring multi-level merge**, with sorted-row parity and reopen/PK/connector/search correctness. Evidence: `src/staging_writer/spill.rs`, `src/staging_writer/accumulator.rs`. This is an existing acceptance obligation, not a new pre-code blocker. + +Retain R1 RA1–RA5 as written: real entrypoint/config removal, controlled no-ceiling fixtures, ownership/failure preservation, jumbo/rotation correctness and exact post-change test provenance. Pre-amendment binary results do not qualify R1. No additional question or proposal revision is required before implementation within this boundary. + +**Limits/side effects:** source/SDK trace only; no builds, tests, probes, implementation, Git/service/model/tracking writes, database creation or daemon changes. Only this report was written; no constants changed. Assumed supplied baseline/user-approval identities are accurate and subsequent commits are documentation-only. No new verified runtime fact warrants a memory entry. diff --git a/docs/specs/CBG-COPY-01/resource-ceilings-review-resolution.md b/docs/specs/CBG-COPY-01/resource-ceilings-review-resolution.md new file mode 100644 index 00000000..7c76646c --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-ceilings-review-resolution.md @@ -0,0 +1,9 @@ +# CBG-COPY-01 R1 — Review and qualification status + +[Independent final code review](resource-ceilings-final-review.md) used a fresh `openai-codex/gpt-6.1-sol` reviewer at Max effort. It found no confirmed P0–P3 production defect. The exact reviewed source hashes are retained in [checks.json](resource-removal-evidence/checks.json). The approved amendment and user decision remain in their immutable specification commit and approval record. + +The initial jumbo test incorrectly expected the complete comment in a 160-character syntax preview. The real native COPY had succeeded with a >1 MiB record. The corrected oracle reads raw native JSON metadata after a separate-process write, parses it and checks the full 2 MiB canonical key. Its rerun passed and the reviewer verified the result. This is a corrected test premise, not a production truncation repair. + +The actual MCP tools/list negative assertions, mixed small/jumbo and multi-level jumbo merge checks passed. Final format, all-target/all-feature workspace check and strict Clippy passed after the reviewed changes. The additional retained cancellation and unknown-containment process cases each passed after removal (1/1). Full platform CI remains pending. Compilation is not execution; pre-amendment results do not qualify this removal. + +No dependency, supported platform, native format/security bound, cleanup-pass quota, ownership protocol, daemon deployment or automatic COPY retry was added or removed outside the approved scope. Scryer intent changes are signed off; registered-source evidence synchronization remains pending because the registered model uses the original checkout. Linear remains unavailable pending connection verification. No merge or rollout approval is inferred. diff --git a/docs/specs/CBG-COPY-01/resource-removal-evidence/actual-mcp-schema-test.junit.xml b/docs/specs/CBG-COPY-01/resource-removal-evidence/actual-mcp-schema-test.junit.xml new file mode 100644 index 00000000..9b6ce62d --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-removal-evidence/actual-mcp-schema-test.junit.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/specs/CBG-COPY-01/resource-removal-evidence/checks.json b/docs/specs/CBG-COPY-01/resource-removal-evidence/checks.json new file mode 100644 index 00000000..c14ee2fe --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-removal-evidence/checks.json @@ -0,0 +1,236 @@ +{ + "work": "CBG-COPY-01/R1", + "source_base": "8e47d2b3c74b72b56a0b6b212d1bf939c8088ca7", + "approved_amendment_commit": "c8dd4ea9ca2a6f992756a2a41828396a27eabb6d", + "approved_amendment_sha256": "40fa2007539bf26e13a88a3c894428e3cee9dc46230d647e4be6bc1c2ca9b696", + "platform": "macOSarm64", + "rust": "1.96.0", + "native_version": "0.19.0", + "native_archive_sha256": "6bee40babdf837bb6aa4d271047dfe5ed3dacf022f16af84952ca4c22d034d62", + "final_normal_cli_sha256": "98a3f277d202ef550e7a588167fdc180752bec7e88313ce11d15349a808c8a22", + "checks": [ + { + "name": "allworkspacealltargetsallfeaturescheck", + "result": "pass", + "log": "check-04.log", + "sha256": "6100154c937d1124fe9668a6fb6b9a921b74f17cd12dc1012308559a4b8333a4" + }, + { + "name": "focusedmaterializationwriterstreamartifactprotocol", + "result": "134/134pass", + "log": "focused-unit-02.log", + "sha256": "f771ce70d3a62f4f9796a59bb9a02abe6a46ff3aa981dda2b08e83493e4624a0" + }, + { + "name": "publicconfigAPIandownership", + "result": "42/42pass", + "log": "public-settings-tests.log", + "sha256": "72ac045dc804064d139678899de5839ea8b72afce32cc73ff26162070cf9ad00" + }, + { + "name": "strictclippyfinal", + "result": "pass", + "log": "clippy-final.log", + "sha256": "9619b84e21e0080e3391562e370647415ecf3b3184ff0cb2933ca977a2c63105" + }, + { + "name": "realCLIcopyreopencontrolManagedDirectfailure", + "result": "2/2pass", + "log": "native-copy-01.log", + "sha256": "0064f422b10e361b7385874ab3d3750a22b5cde5ebe3618e1f0bb732a3a97fc7" + }, + { + "name": "legacybudgetfieldsnolongerapply", + "result": "1/1pass", + "log": "legacy-budget-config-process.log", + "sha256": "de4e09cae58e59cc6beb6fdb07c30638986ea1a5e285c48800901d06486651e9" + }, + { + "name": "loaderallocationnotbudgetcancelled", + "result": "1/1pass", + "log": "ownership-no-ceiling-process.log", + "limit": "8MiBfixtureallocationtestsreachabilitynotphysicalexhaustion", + "sha256": "da5a191c9e1cf8480d9435ca6e40dd8a8943fbb5d2d410f369cae1e6a99ac8f5" + }, + { + "name": "mixedjumboandmultilevelmerge", + "result": "2/2pass", + "log": "final-added-unit-cases.log", + "sha256": "018b6295f42a5db0c1404ae536aaee8af25caf34561ea785525c6f881d112378" + }, + { + "name": "actualMCPgeneratedtoolschema", + "result": "1/1pass", + "log": "actual-mcp-schema-test.log", + "sha256": "723e0632b4a11c49ea9986929108f491cf9f0b43de92c598195a305f0c4807a5" + }, + { + "name": "doctests", + "result": "3/3pass", + "log": "doctests.log", + "sha256": "f428961340dfb5ef273624b119c2ad02e36fc9249dad430b7a8352ac74d1b6ec" + }, + { + "name": "optimizednativeprofilecompile", + "result": "pass", + "log": "native-profile-compile.log", + "limit": "compiled before finaltest-onlyoracleadjustments, notplatformruntimequalification", + "sha256": "cf3cda0783bb4ce5c4164841ff87c81e5cd1d305788981020350467193f25933" + }, + { + "name": "jumboCLIJSONroundtrip", + "result": "1/1pass corrected fullJSONoracle", + "log": "native-jumbo-record-02.log", + "sha256": "8a50d739f7eb1f02a5ea7eb50f581a9a68c46a0387bd69bcdd887ce1c76b90a7" + }, + { + "name": "finalallworkspacealltargetsallfeaturescheck", + "result": "pass", + "log": "check-final.log", + "sha256": "12fa6f4569df4bfcfe54cb3d5d47bc5773da03097b2fdd2253a4b22f8da34250" + }, + { + "name": "finalstrictclippysnapshot", + "result": "pass", + "log": "clippy-final-snapshot.log", + "sha256": "114ce07a093184439c71d9db313466bf5b5d797e22cb1fd3e49722e3d1bcedef" + }, + { + "name": "finalformatanddiffcheck", + "result": "pass" + }, + { + "name": "independentfinalreview", + "result": "sourceGREEN no confirmedP0-P3", + "review_model": "openai-codex/gpt-6.1-sol", + "thinking": "max", + "source_hash_manifest": "final-source-hashes-04.json", + "report_sha256": "3af8d17e1231f5fc037ef456ad4a5bdb699f699acdc63910717610884c48e81d" + }, + { + "name": "retainedcancellation", + "result": "1/1pass", + "log": "cancellation-after-removal.log", + "sha256": "595f4da7ecdc8b069c30e20a3d31f519994a8664f334d8d25e3910ec1617962e" + }, + { + "name": "retainedunknowncontainment", + "result": "1/1pass", + "log": "unknown-containment-after-removal.log", + "sha256": "73df3335b47dd81684811c71b8547846cd37f628fc0e930f5aa10ca7c17d8ceb" + } + ], + "failed_attempts": [ + { + "log": "check-01.log", + "reason": "cross-file integration remnants andserdeattribute duplication; repaired", + "sha256": "8f6282d7cf8b4e97199a4a9180b95b27fcaf6ab67d67afc95c0a6c20c1ba8b63" + }, + { + "log": "check-03.log", + "reason": "test assumed label field absentfromSearchDocument; correctedactualIDfield", + "sha256": "e3b489bfaf05c460313275e2f73287ff11177311c7f2e87f8d38362d3f1a8e47" + }, + { + "log": "focused-unit-01.log", + "reason": "wrongerrorliteralinretiredflagtest; actualrejectbehaviorverified", + "sha256": "f7f79b2bfa86c958c34404da6b6be2f0e14a1035ae5f014310096c5975c25d9a" + }, + { + "log": "clippy-01.log", + "reason": "dead olderrorparse/import andsingleelementcanceltest; removednosuppression", + "sha256": "5e3ef93591d98c5838e54be6a672423397bdf0dbf90e868f8b123b44e826519e" + }, + { + "log": "native-jumbo-record.log", + "reason": "wrongtextoracle: intentionally160charpreview; fullmetadataJSONsizeverified2MiB,correctedtestpending", + "sha256": "e63b52ab7c9a06908cd8c5dd6b2b3ec69531c9e17be12bae657a3aa5fcd5fbe9" + } + ], + "pending": [ + "newcommitfullmacOSandWindowsCI", + "aggregatefullsuite/platformqualification notclaimed" + ], + "preserved": [ + "batch/concurrency knobs", + "spill/streaming/fanin", + "diagnostic/protocol/searchformatbounds", + "ownership/containment/cancellation", + "ScopeAfailclosedCOPY", + "maintenanceperpassbounds" + ], + "removed": [ + "worker_memory_mib/rust_memory_mib fields/options/defaults/validation", + "RSSlimitkillpolicy", + "source/partition/artifactestimateadmission", + "derivedapplicationLadybugpool128..384", + "chunk-targetrecordrejection", + "MemoryBudgetExceededapplicationerrorcontract" + ], + "not_claimed": [ + "unlimitedOSmemory", + "nativeOOMimpossible", + "originalincidentrootcause", + "deployment", + "mergeready" + ], + "final_source_sha256": { + "knowledge/architecture/graph-runtime.md": "5878bd07c55a9af68ee89f3d6c8730ac0c3310fef91d5858b3d0184e761d1168", + "knowledge/architecture/graph-storage-lifecycle.md": "f1d13db5f3d1d87cd3fd17813b4bf4b3a5bf1966d7aee01a2cd2bf9c7152a70f", + "knowledge/architecture/materialization-pipeline.md": "5e2ad7af250fcb388c51a027b0237745fad8ae23d8574f4bc5cf647f9abb5881", + "src/adapters/cli/format/help.rs": "dcc1d0e9500dc21fad4f6635a46268a7a06d543cc1c2e050d63380ea3e3b0ec8", + "src/adapters/cli/materialization_input.rs": "c8963a4b67d7363d3f295de353a97af3db6096578e36f57254aff18e796c0ab7", + "src/adapters/cli/tests/dispatch_materialize.rs": "af6bcdd98f62aae44e370b2d4ddbf17dd34cb0246a49d665d3004d5abfc5256a", + "src/adapters/cli/tests/fixtures.rs": "63d2aa157ff043e5ccbf6e010074feb3e6ad1f9682d91603382f686eb676facd", + "src/adapters/cli/tests/install.rs": "64c2b26cd0110c52d8929ea76425c505a3eb2839dd1a4179cbda1cfd8a1c97f8", + "src/adapters/cli/tests/mcp.rs": "22cd4cef51368a0f3050641e7e437d7fbc4d798e6337b87c3bf1b446cf3081bc", + "src/adapters/mcp/options.rs": "4bb7a5eadd19606cf9cbe9922680c3be62b16a16fb690aa7cf55047ff10e4c9b", + "src/adapters/mcp/refresh.rs": "522e25f8ff1112aed77b221e208eff69fe8df152e6e8527013a4484fd5dca829", + "src/api/context.rs": "da07ae3b85d43d45c69cb89739c0f1ccc7d4234ed837fe1bd4fd05c07071a677", + "src/api/contracts.rs": "6cedee070d6724fdc29c76363988f3aac804931f3df9b86bad97f0845ff1be3f", + "src/api/core.rs": "b8acfa792f984269604512c2e35598e74b515bf060cca60c5b200e74cf6f317c", + "src/api/graph_read.rs": "d821e8d7c81ce44bcdb0e1ffd915fdd4321c0f4b09a023884500f300ede125d6", + "src/api/materialization.rs": "b2ba396562acc074e36d569ee5e03a380061082de7b33669451885329c89ff1c", + "src/api/normalization.rs": "56b1f81d7157cead4991bc47d709838d81aa0eab9e262d3ad24187fbc692e3d9", + "src/api/refresh.rs": "2cbe8222d464e68baf6234a1ac9bcc61eb96c78a0b5a9fece815ee8ba6c3eaf6", + "src/artifact_store.rs": "b63b79ba4dbe311f0e39ff69e876b120695017e077023bcb532d18c6bccbb7d9", + "src/db_writer/access.rs": "b390915f919bd6a68511afe858335cc929d3c082f706894c561dbb804904451d", + "src/db_writer/mod.rs": "dcf0a934c4fa96d95460a8c1e0fed6c9a7f78ae3aea1f0cf579aa0302fce9954", + "src/db_writer/phase.rs": "8cb94f7a81b1c03636f9c2aec4d492d6f62a267dac34797f0ad85b471650e846", + "src/db_writer/request.rs": "181ebf945156a82d0c894daf4d8a478a96eb51ca638a03b2a62ccef534ab4693", + "src/db_writer/tests.rs": "2249074e24508baa735fea6f27a9c5f6a7163d15215879ebb46549c418e6e42b", + "src/db_writer/write.rs": "e5e1856ceed991087ed69d5bf337bcd52ebbde0118b74f528438220cf4af4897", + "src/error.rs": "760cdf7c7744434ca4e4d60f701e02bfe99ec19c3c4745d8d8011c9a3cee93e4", + "src/execution/parallel.rs": "2c0973cc24637da4841d4603fb2b2df6f35de6ef6cb8a91ae89e779b8dcddabd", + "src/execution/run.rs": "35f2677f17822aac2ad90f0af1b0dc6f09c6f222acd78233769ee52f8e4c0f31", + "src/lib.rs": "509f314907f3860c8408f1ed32f8e94867ba79d906b01d7de2e7cc253910d751", + "src/materialization_worker.rs": "9512f83683901e5186ec3262e360ca733b39175f37a4cfb812441d55ad3daf55", + "src/process_lifecycle/mod.rs": "75b22b2db8dc471ed7d89e6982c9019b0a232bed411b9ae71b76b611d19241f6", + "src/process_lifecycle/windows.rs": "8e77aae5e43d84c59c3b143a91ca969a921f610b3abf7d1ca1e04767339e439f", + "src/protocol.rs": "a890e48f19cc6a21d5f753bf98b614e2c9d03896db7b830c0a449807a7d91c72", + "src/scan.rs": "04741059c6992f59f917eec89514a4674cf7fce80727d06f3cd32ffd2a346cb9", + "src/search_index/build.rs": "b7bb1a176951c81a2fd1d4bd0276ec92c132334d5bb6f12cb4c7b508af9ec875", + "src/search_index/io.rs": "ae4a620bd4958de7f5758322363e5aa367697106275dd8c2f54e4c6dddf59f51", + "src/search_index/mod.rs": "baa8c1ef3f4d840d757973baae21ee55491da190fad4228dc7fb13f9105b1fef", + "src/search_index/model.rs": "802670178e9af23a2ae615a51cdb08365ba1b0f8e441b477f617f6277f3a0389", + "src/search_index/read.rs": "103c69d3c8de3f45d33005cc2eb395bd5eaa3dc4fd79522699a80269c38c0940", + "src/search_index/tests.rs": "745f994751d697166e0236ee24a6a032ac5fd3903c564d12e97611809c9b9e9e", + "src/staging_writer/accumulator.rs": "2fa14e6f977981fa88ad44024bff7e3cd4fe33e8feebfc48e1ca45d919829915", + "src/staging_writer/spill.rs": "a1870eac16d7299ef937178b9520e68ff7603a308b7a5f59ed1b63c248035efd", + "src/staging_writer/tests.rs": "a500f7b2ad80e601a73e9dd0780106d305d74f842f654757099bf5daf69c3548", + "src/staging_writer/writer.rs": "7fec0e506c03a0707f92191e40fb21a2ccf0b0beb32d175ff10ae96315de4244", + "src/storage/containment.rs": "519c4f58c1d4be02970775b9ac1c341b5d05ef4523ae31909f429f804824f482", + "src/storage/maintenance_candidate.rs": "2d63f7e9950cd82784f84fb3cbffcb4ff266508949161ef65e5934d02ae4dd17", + "src/storage/maintenance_generations.rs": "3d2d2d0365bb86774fb65bddc61b11f6c79fd151db33a52cd2824bfcddb05c66", + "src/storage/maintenance_legacy_association.rs": "55f0f45c5b1a75f5a6a6ebada44d0b552cdb0275b3ac164f887c4e01fce8f7ac", + "src/storage/maintenance_ownership.rs": "760542a617a01a6bb09fe0ff75e052d6c66e6b9efbe8505d53ca8e22450cfafe", + "src/storage/test_harness.rs": "7023f205de1b9c16ac3ccdd1c87f44158595c423f5583a43ebed32d854b5f80e", + "tests/coordinator_process.rs": "4ffc40b55894159a45df943f5b924ea267e7781da6b4e20e036ebc05e358845a", + "tests/copy_phase_process.rs": "dbff1132733d5557a7108033f9bb41b8e5968357614c1b1bb8a4344fc046424b", + "tests/generation_storage.rs": "ab0628e9ec2b1ba42d873b4a399ad90632ad11a083fc16e5902c78eabd8d1807", + "tests/refresh_recovery_process.rs": "56c4133d2c75aa7b248d84ab1fc9b8aae8ef1b29fb4808155d70e201e07db248", + "tests/support/lifecycle_faults.rs": "6fa03996e99accc18adda746fbb376d55c35a8191a14ce261e918243154175a7", + "tests/support/storage_maintenance/collector.rs": "46ceff561e3252155dba410ff646f7cf25f5eb21960fee27f2dd9a7c57303a2c", + "tests/support/storage_maintenance/workload.rs": "9548cc7b66402bfc8af53031edfd6d8bcac73b38897d7fc7f63194b2aeec1008" + } +} diff --git a/docs/specs/CBG-COPY-01/resource-removal-evidence/focused-unit-02.junit.xml b/docs/specs/CBG-COPY-01/resource-removal-evidence/focused-unit-02.junit.xml new file mode 100644 index 00000000..7cfb8733 --- /dev/null +++ b/docs/specs/CBG-COPY-01/resource-removal-evidence/focused-unit-02.junit.xml @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/specs/CBG-COPY-01/review-resolution.md b/docs/specs/CBG-COPY-01/review-resolution.md new file mode 100644 index 00000000..b6d8a8d2 --- /dev/null +++ b/docs/specs/CBG-COPY-01/review-resolution.md @@ -0,0 +1,18 @@ +# CBG-COPY-01 — Proposal review resolution + +- Canonical scope: [spec.md](spec.md), proposed revision 0.2. +- Implementation authority: none. The user requested planning and explicitly withheld implementation sign-off. +- Initial independent reviewer: fresh/default GPT-6.1 Sol at Max effort, required proposal-review skill; [full initial report](proposal-review.md). +- Reviewed initial snapshot: revision 0.1, SHA-256 `54d51d60096d1038485f3d335fc25911bac4d092afbfaee034fff3e2697c64a2`. +- Current final candidate: revision 0.2, SHA-256 `1a0ea5f860ce2764e1666bd6345faf9bb24929f35795255772c04380839b4dd2`. +- Focused verification: [independent final-candidate readback](proposal-review-verification.md), SHA-256 `81e472f12bfde54b5a0a6bf964d137750ae3e68b803250e7a85f21a0ed4fb7f5`. APR-001–003 are resolved at the specification level; no unresolved material finding remains. Reviewer recommendation does not replace user approval. + +| Finding | Proposed resolution in revision 0.2 | Verification | +| --- | --- | --- | +| APR-001 — Existing-object substring suppression contradicts the all-mutation failure rule | Scope A.1, R1, implementation step 3 and A1 remove blanket native `Err` suppression in both paths; actual successful idempotent SQL remains valid. Query-result-level tests supplement phase-runner tests. | Resolved for plan approval; independently verified against the final candidate. | +| APR-002 — Trimming only after full stderr ingestion is not bounded allocation | Scope A.4, R6, implementation step 3 and A8 require bounded file-tail/window ingestion before allocation and a capped assembled phase diagnostic, on both selected-main native routes including successful exits. Existing 64 KiB budget/retention/containment remain unchanged. | Resolved for plan approval; independently verified against the final candidate. | +| APR-003 — COPY statement/control state grows with chunks | R3 links existing Graph Writer intent; risks disclose retained/cloned strings; A7 records statement/file count and control-state cost under unchanged limits. No streaming-plan framework is added. | Resolved for plan approval; independently verified against the final candidate. | + +Additional clarified gates: search already enumerates contiguous chunk files and needs cross-chunk search parity, not a reader rewrite; A7 separately requires a representative normal successful/default profile and a deliberate budget-abort case. Low-level request pools are not falsely constrained by the materialization calculator. [Prior research evidence](investigation-evidence.md) is kept distinct from unexecuted fix acceptance. + +Planning checks found all current local spec links and cited line ranges valid and all six requirements/ten acceptance IDs present. Inspected source hashes remain unchanged. These are document checks, not compilation, native tests, runtime qualification or user sign-off. Linear remains pending verification; existing Scryer drift is not reconciled or folded. No code, dependency, configured constant, database or daemon changed. diff --git a/docs/specs/CBG-COPY-01/spec.md b/docs/specs/CBG-COPY-01/spec.md new file mode 100644 index 00000000..6d68cfb6 --- /dev/null +++ b/docs/specs/CBG-COPY-01/spec.md @@ -0,0 +1,152 @@ +# CBG-COPY-01 — Safe Ladybug COPY failure handling + +## 1. Identity, status and approval boundary + +- **Status:** proposed revision 0.2; planning only. No implementation, runtime tests, release qualification or rollout approved. +- **Canonical path:** `docs/specs/CBG-COPY-01/spec.md`. This new, narrow fix record supplements, but does not copy or supersede, the existing storage/recovery contracts below. No `.omx` state is used. +- **Decision / closure owner:** repository maintainer (user). No milestone or aggregate closure ownership changes. +- **Scryer ledger:** `chg-5qnevq`. Reuse Graph Store `node-88`, Materialization Worker `node-208`, Materialization API `node-56`, and their existing claims. No new component, topology or responsibility is proposed for the recommended scope. +- **Linear:** connection is pending verification; issue search/synchronization is unavailable. Do not invent an issue, reuse unrelated `CBG-C4-01` work, or change project settings. Search existing/completed work when available before linking/creating the approved fix in the established CodebaseGraph delivery project. +- **Source examined:** checkout HEAD `6aa5ba8bd99c3071e106a8bfc7a408652a8e4bb5`. Local `origin/main` is `ca115690b226384164cf364458a0e3255b8908d3`; the writer, staging accumulator, execution/run, write request and resource-limit files compared equal. Lifecycle paths differ. No fetch or branch change was performed. +- **Implementation baseline unresolved:** recommend a clean worktree on then-current main, retaining newer approved lifecycle fixes. Recheck source anchors and Scryer drift there. Do not implement against this older checkout or change its unrelated dirty files without the user's baseline decision. +- **Proposal review:** fresh independent GPT-6.1 Sol / Max reviewed revision 0.1; [findings and read-only evidence](proposal-review.md). Revision 0.2 proposes resolutions of APR-001–003; [review-resolution.md](review-resolution.md) records their current verification status and exact snapshot hashes. Review is not user approval. +- **Approval record:** none. Before dependent implementation, commit the agreed specification, identify its full Git SHA and exact approved scope in a separate approval record, and record the real user decision with Scryer `sign_off`. A drafted spec, reviewer verdict or commit is not user approval. + +Canonical constraints (read in their existing locations): + +- [Storage lifecycle and recovery](../../../knowledge/architecture/graph-storage-lifecycle.md): fresh candidates; atomic publication; preserve active graph; writer ownership; Direct paired recovery; confined cleanup. +- [Worker recovery contract](../../../knowledge/architecture/worker-recovery-regression-contract.md): verified process-tree teardown before cleanup/new execution, qualification of the old baseline, and unchanged public interfaces/budgets. +- [Graph Runtime](../../../knowledge/architecture/graph-runtime.md): application/storage/pipeline boundaries. +- Scryer is authoritative for intended responsibility and detailed execution briefs. Source and executed evidence establish implementation. Existing scoped drift is not reconciled by this proposal. + +## 2. Problem, evidence and intended outcome + +The reported failure was `COPY statement 8/166 (DocumentationSource)` with Ladybug's non-empty-primary-key-table/no-hash-index guard. Statement 8/166 does not prove a second chunk or establish the original first failure. Original attempt logs and executable/plan provenance are unavailable. + +Verified exposure at the inspected source: + +| Evidence | Anchor | Implication | +| --- | --- | --- | +| A failed phase can be rerun on the same candidate after matching `buffer pool is full`, unless the text contains `checkpoint after`. | [write.rs:215–246](../../../src/db_writer/write.rs#L215) | Engine-internal checkpoint errors need not have the application's prefix. Message matching does not prove rollback. The same loop covers schema, COPY, hash-index and post-COPY mutation. | +| A repeated node target receives its early hash index only after its first COPY phase succeeds. Both writer paths already implement this. | [write.rs:103–157](../../../src/db_writer/write.rs#L103), [248–320](../../../src/db_writer/write.rs#L248) | Do not replace existing sequencing with a speculative missing-detector fix. A failed COPY may be replayed before this index step. | +| Fallback loading wraps the whole write operation in a generic transient-error retry; a fresh-path check occurs inside it. | [write.rs:25–37](../../../src/db_writer/write.rs#L25), [170–180](../../../src/db_writer/write.rs#L170), [access.rs:29–62](../../../src/db_writer/access.rs#L29) | Audit/remove write-operation replay, not just isolated COPY replay. Keep unrelated read retry policy unchanged. | +| Final JSON output limit is `usize::MAX`, independently of bounded sort/spill chunks. | [run.rs:16–18,40–55](../../../src/execution/run.rs#L16) | Production can send an entire table document to one native COPY. | +| JSON sink already supports deterministic rotation and rejects an oversized encoded row. | [accumulator.rs:57–77](../../../src/staging_writer/accumulator.rs#L57), [721–810](../../../src/staging_writer/accumulator.rs#L721), [1062–1117](../../../src/staging_writer/accumulator.rs#L1062) | Reuse it with the existing configured byte limit; do not introduce JSON list/glob loading. | +| Failed-attempt stderr is read in full, then trimmed to its last 64 KiB; helper files are deleted and retryable errors are discarded. | [phase.rs:135–149,340–343](../../../src/db_writer/phase.rs#L135), [write.rs:219–229](../../../src/db_writer/write.rs#L219) | Both ingestion and returned context need a bound. Keep the first failed attempt primary; do not add indefinite raw-log retention. | +| Schema/post-COPY helpers turn native errors containing `already exists`, `exists already` or `already installed` into success. | [write.rs:376–393](../../../src/db_writer/write.rs#L376), [phase.rs:305–322](../../../src/db_writer/phase.rs#L305) | Substring matching is not proof that a native error is a harmless no-op. Scope A must address it as well as retries. | +| Search construction already enumerates the base JSON file and contiguous suffixed chunks. | [search_index/build.rs:97–101,197–240](../../../src/search_index/build.rs#L97) | No search-reader rewrite is indicated; prove cross-chunk search parity in regressions. | +| Materialization owns the candidate and aborts on pipeline failure; publication comes later. | [materialization.rs:285–319](../../../src/api/materialization.rs#L285), [482–503](../../../src/api/materialization.rs#L482), [903–929](../../../src/api/materialization.rs#L903) | Loader reports failure. Storage owner alone controls abandonment/cleanup/publication. | +| Reopened validation compares manifest, sidecar, counts and artifact existence, not expected IDs or primary-key lookups. | [materialization.rs:1078–1162](../../../src/api/materialization.rs#L1078) | Regression oracles must verify lookup and endpoint correctness, not only counts. This fix does not claim a full native-integrity validator. | + +Prior native observations and their limits/hashes are retained in [investigation-evidence.md](investigation-evidence.md). No new native experiments ran during planning. + +Ladybug evaluation is complete but does not authorize adoption. Official 0.22.0 still has the guard; a twice-reproduced failed-checkpoint/successful-retry/reopen sequence preserved rows but lost PK lookup visibility ([upstream #1406](https://github.com/LadybugDB/ladybug/issues/1406)). Bulk COPY row payload is not WAL-backed; a commit record does not guarantee durability after checkpoint failure ([PR1204](https://github.com/LadybugDB/ladybug/pull/1204), [PR1361](https://github.com/LadybugDB/ladybug/pull/1361)). Treat a failed mutating operation as an uncertain candidate, not proof of rollback or durable success. This is not a proven diagnosis of the original incident. + +**Outcome sought:** either one successful, correctly reopened candidate is published, or the first failure reaches the caller and the existing graph remains readable and unchanged. Bounded sequential files reduce per-COPY input size; they do not guarantee every workload fits Ladybug's native memory. + +## 3. Recommended scope and decision + +### Scope A — recommended first delivery + +1. Run each mutating phase at most once per candidate. On any native `Err` from schema/COPY/hash/post-COPY/explicit-checkpoint execution, stop the load and propagate that failure. Remove the blanket `already exists` / `exists already` / `already installed` error-to-success mapping in both writer paths. Successful idempotent SQL/results remain successful; an actual native error is not a verified no-op merely because its text matches. No index repair, duplicate skipping, implicit same-candidate retry, or success inferred from row counts. +2. Use `LadybugWriteRequest.buffer_pool_bytes` for that single execution. This is already computed from configured resources (128–384 MiB for normal materialization), not a new limit. Remove the isolated 256 MiB start / 64 MiB escalation mechanism and fallback write-operation retry. Read-only transient retries stay unchanged. +3. Bound final JSON and connector files using the existing `spill_chunk_mib` byte value already used for spill/record limits. Keep one sequential COPY per file and preserve the current early-hash-index-after-first-success ordering for repeated node tables. +4. Bound both native diagnostic ingestion and returned phase-failure context using the existing 64 KiB child-error budget. Read only a file tail/window before allocation; never read all stderr then truncate it. Cover both inherited and standalone/guarded execution routes on the selected main baseline; skip unused successful-exit stderr rather than ingesting it unboundedly. Reserve space within the returned phase diagnostic for bounded parent-known phase/kind, COPY ordinal/total and target, effective pool and worker ceiling; retain a bounded native tail from the first failed attempt and identify truncation. The bound is on the assembled isolated-phase diagnostic, not every later API/JSON envelope. Distinguish COPY-query from explicit-checkpoint failure without claiming the engine-internal outcome is known. Cleanup errors are secondary. +5. Regress actual isolated/reopen behavior, lookup/endpoint correctness, prior-active preservation and bounded resource failure. Do not broaden production validation into an expensive full-graph audit. + +**Behavior changes requiring sign-off:** there is no immediate automatic recovery from a failed database mutation; the operation fails safely instead. Raw schema/extension/post-COPY errors previously hidden by substring matching now fail the invocation; intended idempotent setup must return success and pass its regression. The initial native pool may rise from 256 to the already configured 320/384 MiB ceiling. This changes startup RSS and must pass the existing worker RSS supervisor. More final files mean more sequential child phases/checkpoints. No new public setting or resource ceiling is proposed. + +### Scope B — optional later delivery, NOT included in Scope A approval + +Restore automatic pool escalation only by reconstructing an entirely fresh disposable candidate from the same completed immutable staging. This is a new sequencing/recovery behavior, not something current publication recovery already promises. + +Required order: failed attempt stops → existing lifecycle owner proves all owned writers exited → storage owner abandons its complete candidate and sidecars under ownership → owner allocates a fresh candidate → replay the complete schema/node/index/connector/post-COPY plan, not only the failed statement → reopen and validate → publish once. If teardown/cleanup/ownership is uncertain, stop, preserve evidence and keep the active graph. Never reset an arbitrary `db_path` from inside `run_phase`. + +Before Scope B can be approved, specify the finite attempt/pool schedule within the existing ceiling, immutable-staging lifetime, sidecar/manifest rebinding, writer-lock lifetime, durable crash/cleanup behavior, and bounded diagnostics across attempts. `StorageExecution` is the existing owner boundary ([materialization.rs:384–503](../../../src/api/materialization.rs#L384)); it currently has no retry loop ([311–319](../../../src/api/materialization.rs#L311)). Scope B must not reparse changed live source while claiming to replay the same staged generation. Runtime hooks/journals and tests must follow the approved main lifecycle contract, not the weaker old checkout. Detailed design belongs to a separate Scryer brief and spec amendment; no Scope B implementation is authorized by this draft. + +### Alternatives and tradeoffs + +| Option | Advantage | Cost / reason not selected | +| --- | --- | --- | +| A: fail closed + bounded sequential files + one configured pool | Small, explicit correctness boundary; no new reset/retry state machine; avoids false rollback assumptions. | Some builds fail instead of recovering automatically; higher initial pool; more phase overhead. Recommended. | +| A+B: automatic whole-candidate reconstruction now | Can recover from a low-pool attempt without unsafe replay. | Needs new owner-level sequencing, crash/staging/sidecar rules and extra fault tests. Defer unless user selects it. | +| Fail closed but retain the 256 MiB starting pool | Least initial memory change. | Rejects loads that currently succeed after reaching their already configured larger pool. Viable safety-only alternative if startup RSS is the priority. | +| Eager/create-after-error hash indexes, duplicate skipping or disabling auto-checkpoint | May hide this guard or reduce checkpoints. | Does not prove an uncertain mutation rolled back or became durable. Rejected as a correctness fix. | +| Upgrade to 0.22.0 alone | New upstream features and API-compatible build observed. | Guard and checkpoint/PK-integrity blocker remain; ABI/storage/platform work is independent. Excluded. | + +## 4. Requirements, boundaries and failure behavior + +| Requirement | Contract and observable behavior | Applicable existing intent | +| --- | --- | --- | +| CBG-COPY-01/R1 | After any native schema/COPY/hash/post-COPY/checkpoint execution returns `Err`, no further mutation is issued to that candidate in that invocation. Do not suppress a native error because its text resembles an existing-object message. Return the first failure; mark the candidate ineligible for publication. | Graph Store `resp-110`, `resp-108`; Materialization API `resp-238`. | +| CBG-COPY-01/R2 | The only normal write attempt uses the configured request pool and threads; configured Rust/worker limits and RSS enforcement remain active. Resource exhaustion is failure, never permission to exceed the limit or replay. | Worker `resp-263`, `resp-264`, `resp-281`; Graph Store `resp-274`. | +| CBG-COPY-01/R3 | Pass the same existing spill byte value as both record and final-output limits. Each final JSON/CSV file is no larger than that value, with JSON brackets/newline and CSV headers counted. An individual framed row that cannot fit fails with the existing structured budget error before native loading, even if its unframed serialization alone fits. Preserve deterministic merged rows and single-file COPY syntax. | Materialization API `resp-72`; Graph Writer `node-81` / `resp-273`; existing bounded-build contracts. | +| CBG-COPY-01/R4 | For repeated primary-key node tables, finish/checkpoint the first chunk, create/checkpoint its hash index before the next chunk, finish all node indexes before connectors, and stop immediately on any failed step. | Graph Store `resp-110`. | +| CBG-COPY-01/R5 | During failure, the Managed active generation or Direct committed DB/manifest/search bundle remains unchanged and readable. Cleanup requires the existing owner/teardown proof; unresolved cleanup is reported without masking the load failure. An independent later invocation creates a fresh candidate. | Graph Store `resp-108`, `resp-109`, `resp-232`; Worker `resp-265`, `resp-281`. | +| CBG-COPY-01/R6 | Native stderr tail/window ingestion allocates only bounded diagnostic working storage, independent of stderr file length. The assembled isolated-phase diagnostic, including its parent-known context and truncation marker, stays within the existing 64 KiB budget. Keep the first failed attempt primary and cleanup errors secondary; preserve typed resource failures. A bounded native tail cannot promise every original byte or first line. No raw staged rows, capabilities, new log-retention policy or public error schema is required. | Worker `resp-264`; existing error compatibility. | + +Unchanged: Ladybug 0.19.0 dependency/extension ABI; public CLI/MCP interfaces; graph/manifest/search formats; writer leases; Managed/Direct publication; read retry policy; thread count (database writer is single-threaded); resource defaults and maximums; background refresh scheduling. See [writer.rs:17–31](../../../src/staging_writer/writer.rs#L17), [protocol.rs:169–215](../../../src/protocol.rs#L169), [access.rs:22–25,74–88](../../../src/db_writer/access.rs#L22). + +Exclusions: repairing an already suspect database in place; engine WAL/checkpoint fixes or a fork; migration/downgrade; daemon installation/restart; disk-retention policy; arbitrary custom COPY SQL parser redesign; typed-plan framework; production full-ID scans; benchmarks with invented performance thresholds; automatic Scope B retries. Pending refresh work must retain the existing contract, not acquire a new retry schedule. + +## 5. Implementation sequence after approval + +These are reviewable outcomes, not assignments or permission to execute. Detailed briefs, prerequisites, precise file ownership and claim evidence belong to Scryer before implementers are launched. + +1. **Establish the approved snapshot and evidence contract.** Select clean main baseline; re-read relevant Scryer claims/directives/drift and current lifecycle ownership; search/link Linear when available; commit the agreed spec and record the user's exact scope/full-SHA decision. Resolve any changed baseline premise before touching code. Preserve unrelated work in this checkout. +2. **Add failing policy and native regressions.** At [write.rs tests:396–434](../../../src/db_writer/write.rs#L396), [db_writer/tests.rs:12–87](../../../src/db_writer/tests.rs#L12), [generation_storage.rs:16–120](../../../tests/generation_storage.rs#L16) and the approved main native fixture helpers, specify call-count/no-replay and actual process/reopen tests below. Use fixture-owned fault barriers and a narrow test mechanism; do not depend on filling the host disk or enabling production-only behavior to satisfy tests. +3. **Replace write retry with one-attempt failure propagation and bounded diagnostics.** Change [write.rs:25–33,215–246](../../../src/db_writer/write.rs#L25). Remove the blanket native-error suppression at [write.rs:376–393](../../../src/db_writer/write.rs#L376) and [phase.rs:305–322](../../../src/db_writer/phase.rs#L305); retain normal successful idempotent setup. Apply to all mutating phases and in-process writes, not reads. Pass the request pool directly, preserving typed resource errors and child supervision. Replace full stderr ingestion at [phase.rs:135–149](../../../src/db_writer/phase.rs#L135) with a bounded tail/window read and bounded assembled phase context; on approved main, cover each corresponding guarded/inherited route without changing containment or retention. Remove obsolete retry arithmetic/classification tests rather than layering a second retry policy. No candidate deletion in the loader. +4. **Activate existing bounded final-file rotation.** At [run.rs:16–18,40–55](../../../src/execution/run.rs#L16), use the same existing `spill_chunk_bytes` for both record and final-output limits. Reuse [accumulator.rs:721–810](../../../src/staging_writer/accumulator.rs#L721) and the connector sink, and preserve [write.rs:103–157,248–320](../../../src/db_writer/write.rs#L103) index ordering. No JSON list/glob, new default, chunk-size knob, table-wide buffering or parallel native loads. Check cross-chunk search through the existing chunk iterator; update the stale 256 MiB comment at [writer.rs:18–20](../../../src/staging_writer/writer.rs#L18) to describe the configured pool, not a new limit. +5. **Qualify failure/publication behavior and review the final diff.** Run focused unit/native/publication suites, affected test radius and strict formatting/lints/full workspace checks; include clean offline extensions and normal supported-platform gates. Record exact binary/native/test provenance, JUnit where required, warnings and limits. Require independent final review, reconcile existing claims/evidence and delivery records, then PR/rollout approval. A passing local test is not a deployment or platform qualification. + +## 6. Acceptance and verification + +All rows are **planned**, not executed for this fix. Use the project's normal environment (`cargo ... --locked`, documented native setup); keep scratch HOME/db/staging and native/crate/extension versions aligned. Planned new test names below are descriptive, not existing symbols. + +| Criterion | Given / When / Then | Method and relevant existing anchor | +| --- | --- | --- | +| CBG-COPY-01/A1 → R1,R6 | Given a test runner which writes a marker/row and then returns an ambiguous failure during schema, COPY, hash or post-COPY, when loading, then each failing phase is invoked once, no later mutation occurs, and bounded first-failure context/target/pool survives. At the query-result boundary, native schema/post-COPY errors containing each formerly suppressed substring must fail in both writer paths; intended idempotent setup returning `Ok` still succeeds. Include a table appearing once in the plan. | Deterministic runner/fixture trace plus query-result-level regressions; replace [write.rs:404–434](../../../src/db_writer/write.rs#L404) and cover the two error-suppression helpers. Runner-level injection alone cannot test suppression inside a phase; string-classifier tests are insufficient. | +| CBG-COPY-01/A2 → R1,R5 | Given a real isolated helper which has successfully mutated/checkpointed scratch data but fails before reporting success, when the parent sees failure, then it does not replay that COPY, create a compensating hash index, continue connectors or publish. | Native subprocess fault after an authenticated fixture barrier; later query the scratch state only for test evidence. No unsafe host disk-full requirement. | +| CBG-COPY-01/A3 → R3,R4 | Given multiple chunks in at least two node tables, an edge-node table and both connector directions, when the production executable loads sequential files with deferred indexes across separate phase processes, then read-only reopen returns every expected ID exactly once via scan AND PK lookup and every expected endpoint/edge once. | Extend native chunk regression beyond [db_writer/tests.rs:57–87](../../../src/db_writer/tests.rs#L57); prove the isolated path actually registered/launched its helper. Include first/chunk-boundary/last IDs and JSON metadata. | +| CBG-COPY-01/A4 → R3 | Given a small configured spill bound, when final staging completes, then every JSON/CSV file including framing/header is within it; merged table rows/order equal an unchunked control. A row exceeding it yields `MemoryBudgetExceeded` rather than a larger file. | Existing rotation/oversize tests [accumulator.rs:1062–1117](../../../src/staging_writer/accumulator.rs#L1062) plus production-path configuration assertion; deterministic-row comparison. | +| CBG-COPY-01/A5 → R1,R5 | Given a readable Managed or Direct prior graph, when COPY, explicit checkpoint, index construction, cancellation or RSS budget failure is injected before publication, then the prior graph's identity/content/manifest/search remain unchanged and queryable; no incomplete generation is activated. | Parameterized native/publication fixtures; [materialization.rs:311–319,482–503](../../../src/api/materialization.rs#L311), [generation_storage.rs:56–120](../../../tests/generation_storage.rs#L56). Assert clean teardown before destructive cleanup; unknown containment blocks it. | +| CBG-COPY-01/A6 → R5 | Given the failed invocation, when a separate subsequent materialization runs after verified teardown/cleanup, then it creates a fresh candidate, publishes correct current results and leaves no failed-run files/extra idle generations. If cleanup proof is unavailable, it preserves evidence and fails closed. | Existing main lifecycle/ownership fixtures plus Managed and Direct follow-up builds; keep failure/cancellation tests distinct from automatic Scope B retry. | +| CBG-COPY-01/A7 → R2 | Given materialization pools below/equal/above 256 MiB (for example 128/256/384 within admitted limits), then exactly that calculated pool and one native thread reach the phase. For a direct low-level write request, honor its supplied pool rather than claiming the plain struct imposes the materialization calculator's range. Run the existing representative previously successful/default profile under unchanged limits and require successful correct publication. In a separate deliberate RSS-over-budget case, abort with the typed error and never retry or publish. | Request/policy tests and actual selected-main native supervisor. Record effective pool, parent/child high-water marks, retained/control-state cost, statement/file count and phase overhead. Configured allowance is not measured RSS or proof of a fully resident pool. [protocol.rs:169–215](../../../src/protocol.rs#L169), [writer.rs:17–31](../../../src/staging_writer/writer.rs#L17). No higher limit to make a test pass. If the normal profile fails, return to the owner; safe abort alone does not satisfy normal-build usability. | +| CBG-COPY-01/A8 → R6 | Given a diagnostic file far larger than the existing 64 KiB budget, an explicit checkpoint failure and secondary cleanup failure, then neither successful-exit handling nor either native execution route reads the full file into memory. The reader consumes only its bounded tail/window; assembled phase diagnostic stays within the same budget after metadata/truncation framing. The first failed attempt remains primary, operation boundary is labelled correctly, and cleanup is secondary. | Verify bounded reader requests/bytes or allocations through a narrow instrumented reader plus an oversized-file case; assert output size and metadata/native-tail preservation. Cover selected-main guarded/inherited paths and successful-exit behavior, not just displayed truncation. Use existing budget at [phase.rs:135–149,340–343](../../../src/db_writer/phase.rs#L135); retain [owner cleanup ordering](../../../src/api/materialization.rs#L482). Do not claim every original byte or API envelope is covered by the phase-diagnostic cap. | +| CBG-COPY-01/A9 → R1–R5 | Given the same sources/artifacts under chunked and clean-control builds, including searchable terms in the base and later suffixed files, when reopened in a separate process, then schema, exact node/edge IDs, genuine PK-index results, connectors and search results match. Establish that the pinned native query actually takes the intended PK lookup path; matching query syntax alone is insufficient. The existing 10-update churn gate keeps one idle generation/no runs and its established clean-control size bound. | [generation_storage.rs:178–250](../../../tests/generation_storage.rs#L178), existing cached/clean staging-equivalence tests in [run.rs](../../../src/execution/run.rs). Do not require identical physical DB bytes or performance. | +| CBG-COPY-01/A10 → R1–R6 | Given the final reviewed revision, then formatting, strict workspace Clippy, affected suites and normal workspace/native-platform CI pass without new ignores, weakened assertions, warning suppression or version-skewed native inputs. | Record exact commands, feature/profile, JUnit paths, hashes, platform and result. Native platform failures block that qualification; unavailable execution is a named pending gate, not a pass. | + +Planned command families (refine filters after selecting main and its test radius): `cargo fmt --all -- --check`; `cargo test -p codebase-graph --lib db_writer --locked`; `cargo test -p codebase-graph --lib staging_writer --locked`; `cargo test -p codebase-graph --test generation_storage --locked`; the approved main's native lifecycle/process suite; `cargo clippy --workspace --all-targets --all-features --locked -- -D warnings`; `cargo test --workspace --locked`. Native CI uses each existing target/profile; do not mistake the in-process unit path for isolated production coverage. + +## 7. Risks, mitigations and stop conditions + +| Risk | Mitigation / stop condition | +| --- | --- | +| Fail-closed removes successful low-pool recovery; starting at the configured maximum raises initial RSS. | User explicitly approves the change. Measure with existing RSS tests/reference workload inside unchanged ceilings. If that profile fails, stop and select the 256-start fail-closed alternative or separately design Scope B; do not silently raise limits. | +| More chunk files trigger more index/child/checkpoint overhead and can still exceed native memory. | Reuse early indexing; verify isolated boundaries and representative existing workload without inventing an SLA. Do not claim a file bound caps database/index memory. Surface native failure rather than replay. | +| Smaller files increase the retained/cloned COPY-statement list with chunk count. | The existing sink keeps a `Vec` and the writer clones it ([accumulator.rs:695](../../../src/staging_writer/accumulator.rs#L695), [writer.rs:30](../../../src/staging_writer/writer.rs#L30)). Qualify Rust/control memory with the unchanged resource suite. A file-size bound is not a cardinality-independent bound on the entire staging/control state. Do not introduce a streaming plan subsystem unless measured evidence makes it necessary and the user approves that scope. | +| A regression passes with correct counts while PK entries/endpoints are wrong. | Reopen in a new process and compare expected fixture IDs through both scan and PK lookup plus connectors. No count-only correctness claim. | +| The source checkout lacks newer teardown/guardian safety described by contracts. | Implement on an approved main baseline with existing lifecycle fixes; reverify actual source. Do not backport process safety by assumption or delete a possibly live candidate. | +| Fault test never reached the target mutation, or uses only in-process unit fallback. | Require fixture readiness/mutation evidence and observed isolated helper invocation. Record pre-fault setup failures separately. Keep engine full-disk experiment evidence separate from deterministic application policy tests. | +| Existing unrelated dirty specs/wiki work is overwritten or unrelated model drift folded. | Use a separate worktree; touch only approved files; preserve existing identities/approvals; scope reconciliation. No global drift reset. | + +Pre-mortem: (1) a COPY commits but acknowledgement fails: no replay/publication and the old graph survives; (2) two valid chunks expose missing PK entries after reopen: the PK/connector oracle fails the build's qualification; (3) process death leaves an uncertain writer: existing main containment proof blocks cleanup/new mutation until safely resolved. These are planned tests, not new runtime guarantees proven by this document. + +## 8. Pending decisions and handoff + +**Requested user decision:** approve Scope A on a clean main baseline, including all-mutating-phase fail-closed behavior without substring-based error suppression, one configured pool from the start, bounded final files and bounded diagnostic ingestion/reporting; or request that Scope B automatic fresh-candidate reconstruction be designed into this first delivery. Retaining the old 256 MiB starting pool is an explicit alternative, not an implicit fallback. No answer/preselected recommendation counts as approval. + +After scope/baseline approval: freeze/commit agreed spec → attach the exact user approval/full SHA → author proportionate Scryer briefs/evidence mapping and record actual sign-off → synchronize delivery records when available → implement/test/review. Reuse claims rather than creating a retry subsystem. One owner handles writer/phase/shared errors; a staging lane can proceed independently only after the interface is settled; integration and Git operations are serialized. Required reviewer model/settings and independent final review follow `AGENTS.md`; unavailable reviewers remain a gate. + +Planning handoff must include the final proposal review's snapshot/hash, findings/resolution record, updated source references, Scryer change ID, canonical path, open decisions and Linear limitation. + +### Revision 0.2 changes (proposal only) + +- APR-001: remove substring-based native-error-to-success conversion; query-result tests cover real `Err` propagation and successful idempotent setup. +- APR-002: bound stderr ingestion before allocation and the assembled phase diagnostic on both selected-main execution routes, including success handling; expand A8 accordingly. +- APR-003: expose retained/cloned COPY-list scaling and require existing-resource qualification rather than inventing a streaming subsystem. +- Confirmed search already reads contiguous chunks; A9 verifies later-file search parity. A7 now separately requires a normal successful profile and a deliberate budget-abort case. + +Supported-platform release/operational deployment approval remain separate. No daemon restart, database rewrite or automatic native dependency upgrade is authorized. + +Reusable knowledge was assessed: the existing verified uncertain-COPY-outcome memory already captures the durable lesson. An unapproved plan adds no verified runtime fact; no new repository memory is warranted. diff --git a/docs/testing/ci-directory-scan-recompression.json b/docs/testing/ci-directory-scan-recompression.json new file mode 100644 index 00000000..4ea5e5a9 --- /dev/null +++ b/docs/testing/ci-directory-scan-recompression.json @@ -0,0 +1,26 @@ +[ + { + "asset": "assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz", + "before_bytes": 3495384, + "after_bytes": 3495248, + "before_sha256": "78e2febd0f3f29df5379a014cc755d5540f04282a6f5bd2128738266a7c25d01", + "after_sha256": "a8a9d74be93a54482b6bb6df7c44ead6cb751f398b87b10c990d63e2da77910d", + "decoded_bytes": 14779904, + "decoded_sha256": "7f82059c8149bb7851420a9bfd0c46859e92efaeda0bcd5c65418d8f174edac4", + "actual_lzma_rs_decoder_equality": true, + "dictionary_bytes": 16777216, + "integrity_check": "CRC64" + }, + { + "asset": "assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz", + "before_bytes": 313512, + "after_bytes": 313284, + "before_sha256": "066f43565dfca6c1cbe79f40f9102aeb52c98b68b7fb74861a6be81d4318df4a", + "after_sha256": "e34b00df38ced0b09993112a6d3a2d76735ecea639bb7deaf4e804351343f891", + "decoded_bytes": 1562680, + "decoded_sha256": "83ae4be9fa718577ca971f27ff890b3aea5789c9dd908784e28f52917febbfdb", + "actual_lzma_rs_decoder_equality": true, + "dictionary_bytes": 1572864, + "integrity_check": "CRC64" + } +] diff --git a/docs/testing/ci-native-platform-primitives-packaging.json b/docs/testing/ci-native-platform-primitives-packaging.json new file mode 100644 index 00000000..fbea16c8 --- /dev/null +++ b/docs/testing/ci-native-platform-primitives-packaging.json @@ -0,0 +1,21 @@ +{ + "schema_whitespace_only": { + "path": "assets/graph_schema.json", + "before_sha256": "244fbce950f51ed5db6b52fa8aadb418fdd96d73bc475b4a1a0d1fb66e34d3bd", + "after_sha256": "b85a8d549c24c7193cf03d66c1e687148ec3602286bc9172e2e60c7f60ac3124", + "identical_json_values": true, + "raw_json_token_lexemes_preserved": true + }, + "extension": { + "path": "assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz", + "before_bytes": 342516, + "after_bytes": 342164, + "before_sha256": "a9d60bfae1e6121eeb8ea0f610a9c8177e3f439956eeab39127822dfd2fa4016", + "after_sha256": "b5e476a440a893c365863b7349c0c308faecacf426809d799ccca0d5dbd10d0b", + "decoded_sha256": "5eac6f37333e29b25c6393cad6bbc3542c59a72e77e03b17caf6a5c0183ff7fa", + "decoded_bytes": 1560888, + "actual_lzma_rs_decoder_equality": true, + "dictionary_bytes": 1572864, + "integrity_check": "CRC64" + } +} diff --git a/docs/testing/ci-native-repair-2026-10-25.json b/docs/testing/ci-native-repair-2026-10-25.json new file mode 100644 index 00000000..975eecaf --- /dev/null +++ b/docs/testing/ci-native-repair-2026-10-25.json @@ -0,0 +1,35 @@ +{ + "baseline": "5bcec75c44a259d1e4e15fbc37d79b9e49f59e47", + "baseline_ci_run": 40094039013, + "baseline_windows_failures": 33, + "checks": { + "xtask_tests": { + "passed": 35 + }, + "focused_storage_tests": { + "passed": 10, + "profile": "native-tests", + "platform": "macos-arm64" + }, + "format": "passed", + "clippy_workspace_all_targets_all_features_warnings_denied": "passed", + "workflow_policy": "passed", + "windows_projection_locks_cross_check": "passed; compile-only, not a native Windows runtime result", + "package_bytes": 10485511, + "package_limit_bytes": 10485760 + }, + "native_verification": "pending; local optimized unit cohort running; hosted execution required", + "inputs": { + ".github/workflows/native.yml": "393ee139dce235d5cc8745d6a2b1728e88d6f2ba60e1d780e022c02c535e78e7", + "Cargo.toml": "344ab68b5e38b642c86d27548807fc5632d8f2aa4b2cb6e33bc20d90d728b5da", + "crates/xtask/src/main.rs": "85e9e107b93238c04649a9bfa99b6f3ae2fda978020985056981e4c7d7967c41", + "crates/xtask/src/native_tests.rs": "15f82582ca10ce0507a31cdc644b09e93c87802166e5fbd9cd5468b4f0501369", + "src/storage/inventory.rs": "7efaa197a9a43af5a1820fe2f776b675f8b5a975e972edd159e3efdcb68755b0", + "src/storage/locks.rs": "4c4a00f1186bea9c9d279376b8009260a0415e039050f7896974cb1fa8483114", + "src/storage/maintenance_candidate.rs": "e5b12f88d4c8b18329734c347a3bf1eed2aa3b986484a81e5bf8891c11392b3f", + "src/storage/maintenance_legacy_bootstrap.rs": "83aa5326b85153ad782b0df2766d9cd3c4800c58915417d3cf8152259fe92246", + "src/storage/maintenance_projection.rs": "4565d3a507eee3122bf67d03689f0d1cc415544e5a79e07604378f73c3b1feb5", + "src/storage/maintenance_workspace_gate.rs": "f8abc36c8d3ca88b2ce87b690dd0b5d513707e89add5f453e17d91198090d772", + "src/storage/managed.rs": "29790ff1fe1f16c77645b408654f8bda325e5d771afa29539fdcd8c5aae461a2" + } +} diff --git a/docs/testing/ci-native-selector-recompression.json b/docs/testing/ci-native-selector-recompression.json new file mode 100644 index 00000000..c4b5bc57 --- /dev/null +++ b/docs/testing/ci-native-selector-recompression.json @@ -0,0 +1,30 @@ +{ + "asset": "assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz", + "before_bytes": 3372056, + "after_bytes": 3371312, + "saved_bytes": 744, + "before_sha256": "6eb7946117aaffeaf6042a10a171bf77af9ee57d9d7983c4c3693e2dc311df87", + "after_sha256": "adf5e05d5a63c181a1be3e1e6c90a8ffb9901118171e7caf8f860af055868ada", + "decoded_bytes": 14178304, + "decoded_sha256": "bdecb5201dd6abccc06ddc1f6cb4fcf0be3adb7aa3ddaac2f383ed129fca2b1f", + "actual_lzma_rs_decoder_equality": true, + "lzma_rs_dependency_sha256": "9a4ed494d1463638bbb3794d7b08a02783a06f7f8087a391ea262cd224646bf6", + "dictionary_bytes": 16777216, + "integrity_check": "CRC64", + "encoder_command": [ + "/private/tmp/cgraph-official-7zip-26.05/7zz", + "a", + "-txz", + "-mx=9", + "-md=16m", + "-mmt=1", + "-mf=off", + "-mcrc=8", + "-mfb=220", + "-mlc=3", + "-mlp=1", + "-mpb=2", + "/private/tmp/pr153-selector-package/win-json-fb220.xz", + "/private/tmp/pr153-package-tuning/json.raw" + ] +} diff --git a/docs/testing/ci-run-40129261627/mac-generation.log.gz b/docs/testing/ci-run-40129261627/mac-generation.log.gz new file mode 100644 index 00000000..0af54f9f Binary files /dev/null and b/docs/testing/ci-run-40129261627/mac-generation.log.gz differ diff --git a/docs/testing/ci-run-40129261627/mac-integration.log.gz b/docs/testing/ci-run-40129261627/mac-integration.log.gz new file mode 100644 index 00000000..a748556a Binary files /dev/null and b/docs/testing/ci-run-40129261627/mac-integration.log.gz differ diff --git a/docs/testing/ci-run-40129261627/mac-unit.log.gz b/docs/testing/ci-run-40129261627/mac-unit.log.gz new file mode 100644 index 00000000..605f7d0c Binary files /dev/null and b/docs/testing/ci-run-40129261627/mac-unit.log.gz differ diff --git a/docs/testing/ci-run-40129261627/report.json b/docs/testing/ci-run-40129261627/report.json new file mode 100644 index 00000000..2b539b6a --- /dev/null +++ b/docs/testing/ci-run-40129261627/report.json @@ -0,0 +1,38 @@ +{ + "source_commit": "90f2f17308e9c309e27eb0c30fab919f26c70390", + "run_id": 40129261627, + "macos": { + "unit": { + "job": "success", + "active_tests": 879, + "test_seconds": 1373.994 + }, + "generation": { + "job": "failure", + "all_five_tests_passed": true, + "test_seconds": 942.593, + "reason": "Cargo rebuilt the compiler output; captured CLI hash changed" + }, + "integration": { + "job": "failure", + "all_test_and_doctest_assertions_passed": true, + "test_seconds": [ + 2139.987, + 1.456, + 2.309 + ], + "reason": "Cargo rebuilt the compiler output; captured CLI hash changed" + } + }, + "windows": { + "preflight_passed": 12, + "preflight_failed": 13, + "full_suite_executed": false, + "causes": [ + "directory reopen access denied", + "inventory fixture reread its still-locked writer file" + ] + }, + "linux": "all tests and artifact passed", + "required": "failure; native repair verification remains incomplete" +} diff --git a/docs/testing/ci-run-40129261627/windows.log.gz b/docs/testing/ci-run-40129261627/windows.log.gz new file mode 100644 index 00000000..9cd6489d Binary files /dev/null and b/docs/testing/ci-run-40129261627/windows.log.gz differ diff --git a/docs/testing/ci-run-40145493998/report.json b/docs/testing/ci-run-40145493998/report.json new file mode 100644 index 00000000..f5090138 --- /dev/null +++ b/docs/testing/ci-run-40145493998/report.json @@ -0,0 +1,28 @@ +{ + "source_commit": "0dd460a45a0a914de77fa617a89bd6a734fc4a0e", + "run_id": 40145493998, + "windows_preflight": { + "passed": 17, + "failed": 8, + "full_suite_executed": false, + "passed_repairs": [ + "directory flushing", + "generation publication and retirement", + "configuration fingerprints", + "contention classification", + "guardian fixture" + ], + "remaining": [ + "native buffer alignment", + "live legacy owner observation and recovery", + "pinned workspace namespace removal" + ] + }, + "follow_up_local_checks": { + "legacy_owner_cases_passed": 8, + "schema_oracle_passed": 1, + "clippy": "passed", + "msvc_platform_primitives_cross_check": "passed; compile-only" + }, + "pending": "Hosted native qualification required after follow-up changes" +} diff --git a/docs/testing/ci-run-40145493998/windows-preflight.log.gz b/docs/testing/ci-run-40145493998/windows-preflight.log.gz new file mode 100644 index 00000000..13130cc1 Binary files /dev/null and b/docs/testing/ci-run-40145493998/windows-preflight.log.gz differ diff --git a/docs/testing/ci-run-40149674096/mac-unit.log.gz b/docs/testing/ci-run-40149674096/mac-unit.log.gz new file mode 100644 index 00000000..45c96f16 Binary files /dev/null and b/docs/testing/ci-run-40149674096/mac-unit.log.gz differ diff --git a/docs/testing/ci-run-40149674096/report.json b/docs/testing/ci-run-40149674096/report.json new file mode 100644 index 00000000..ce101e43 --- /dev/null +++ b/docs/testing/ci-run-40149674096/report.json @@ -0,0 +1,19 @@ +{ + "source_commit": "c60f143323d32775c2ca1c7f2323c16d56327de6", + "run_id": 40149674096, + "windows": { + "preflight_passed": 43, + "preflight_failed": 0, + "full_suite_finished": false, + "full_suite_collector_failures_observed": 3, + "job": "timed out", + "common_rejection": "terminal workspace root-removal preparation reported Stale" + }, + "macos": { + "generation": "passed", + "integration": "passed", + "unit": "833 passed, one logical admission fixture failed, two existing ignored" + }, + "linux": "tests and artifact passed", + "follow_up": "Windows cohorts and package-specific bundled features; raw workspace rejection diagnostics; logical fixture grant separated from unchanged production grant" +} diff --git a/docs/testing/ci-run-40149674096/windows.log.gz b/docs/testing/ci-run-40149674096/windows.log.gz new file mode 100644 index 00000000..0cba350c Binary files /dev/null and b/docs/testing/ci-run-40149674096/windows.log.gz differ diff --git a/docs/testing/ci-run-40161885892/mac-integration.log.gz b/docs/testing/ci-run-40161885892/mac-integration.log.gz new file mode 100644 index 00000000..0aaf85b0 Binary files /dev/null and b/docs/testing/ci-run-40161885892/mac-integration.log.gz differ diff --git a/docs/testing/ci-run-40161885892/report.json b/docs/testing/ci-run-40161885892/report.json new file mode 100644 index 00000000..9a98fc0f --- /dev/null +++ b/docs/testing/ci-run-40161885892/report.json @@ -0,0 +1,27 @@ +{ + "source_commit": "c1c6b06c8663bd5e904efcff8d8e12f6f78171e0", + "run_id": 40161885892, + "windows_unit_preflight": { + "passed": 43, + "failed": 1, + "raw_rejection": "Invalid(Busy)", + "collector_case": "artifact use lease convergence" + }, + "macos": { + "unit": "passed", + "generation": "passed", + "integration": "coordinator quiescence fixture failed" + }, + "follow_up": { + "windows_directory_scans": "independent pinned-directory handles prevent recursive enumeration consuming the outer cursor", + "coordinator_fixture": "canonical paths and retained shared artifact observation lease", + "local_verification": { + "coordinator_process": "passed; twenty clients and existing RSS assertions", + "msvc_test_cross_check": "passed; compile-only", + "clippy": "passed", + "xtask_tests_passed": 37, + "package_bytes": 10485565 + } + }, + "native_verification": "pending on follow-up commit" +} diff --git a/docs/testing/ci-run-40161885892/windows-unit.log.gz b/docs/testing/ci-run-40161885892/windows-unit.log.gz new file mode 100644 index 00000000..b1c918d4 Binary files /dev/null and b/docs/testing/ci-run-40161885892/windows-unit.log.gz differ diff --git a/docs/testing/ci-run-40164309832/linux.log.gz b/docs/testing/ci-run-40164309832/linux.log.gz new file mode 100644 index 00000000..c6064c28 Binary files /dev/null and b/docs/testing/ci-run-40164309832/linux.log.gz differ diff --git a/docs/testing/ci-run-40164309832/macos-generation.log.gz b/docs/testing/ci-run-40164309832/macos-generation.log.gz new file mode 100644 index 00000000..5060442f Binary files /dev/null and b/docs/testing/ci-run-40164309832/macos-generation.log.gz differ diff --git a/docs/testing/ci-run-40164309832/macos-integration.log.gz b/docs/testing/ci-run-40164309832/macos-integration.log.gz new file mode 100644 index 00000000..68766af9 Binary files /dev/null and b/docs/testing/ci-run-40164309832/macos-integration.log.gz differ diff --git a/docs/testing/ci-run-40164309832/macos-unit.log.gz b/docs/testing/ci-run-40164309832/macos-unit.log.gz new file mode 100644 index 00000000..5a39aa8b Binary files /dev/null and b/docs/testing/ci-run-40164309832/macos-unit.log.gz differ diff --git a/docs/testing/ci-run-40164309832/report.json b/docs/testing/ci-run-40164309832/report.json new file mode 100644 index 00000000..709253a4 --- /dev/null +++ b/docs/testing/ci-run-40164309832/report.json @@ -0,0 +1,82 @@ +{ + "source_commit": "e1004eca426fb05bf865772091f24e0f6badd083", + "tested_merge_commit": "86f246a61976cdb2e93eb9b40cb85488021f3272", + "merge_difference": [ + "CHANGELOG.md" + ], + "run_id": 40164309832, + "macos": { + "unit": "passed", + "generation": "passed", + "integration": "passed", + "arm_artifact": "passed", + "intel_artifact": "passed", + "test_step_minutes_including_compilation": { + "unit": 30.9, + "generation": 18.3, + "integration": 47.3 + }, + "target_inventory": { + "unit": 5, + "generation": 1, + "integration_and_doctests": 24, + "disjoint": true, + "complete": true + } + }, + "windows_generation": { + "passed": 4, + "failed": 1, + "case": "managed_reads_survive_deferred_retired_generation_deletion", + "failure": "Expected cleanup_pending=false immediately after releasing the blocking handle; materialization correctly reported pending bounded cleanup.", + "test_seconds": 2779.1, + "follow_up_commit": "ef01aab", + "follow_up": "Use the existing shipping-health convergence helper, which checks readability, active-generation identity, empty errors, 100-entry grants and physical owned idle." + }, + "remaining_ci": "run complete; required failed", + "local_follow_up": { + "format": "passed", + "clippy_workspace_all_targets_all_features_warnings_denied": "passed", + "package_bytes": 10485571, + "package_limit": 10485760, + "msvc_cross_check": "unavailable: this host's C++ compiler cannot locate Windows SDK wchar.h; native verification pending" + }, + "windows_unit": { + "focused_preflight_passed": 45, + "remaining_observed_failure": "dc_s5_real_copied_orphan_staging_requires_confirmation_then_retires_without_source_edit", + "raw_error": "maintenance target changed during preparation", + "conclusion": "cancelled at 90-minute job limit before full coverage completed" + }, + "windows_integration": { + "observed_failure": "blocked_refresh_recovers_after_memory_budget_restore_without_source_edit", + "failure_state": "live worker and a readable successor, recovery deadline reached with another epoch pending", + "conclusion": "cancelled at 90-minute job limit" + }, + "linux": { + "library_passed": 830, + "library_failed": 1, + "case": "dc_driver_real_requested_allocations_fit_tight_ceiling_and_aligned_factory", + "observed_retained_bytes": 144, + "integration_and_doctests": "passed", + "follow_up": "native support removed by explicit user request; retain this executed failure evidence" + }, + "follow_up": { + "supported_targets": [ + "macos-arm64", + "windows-x86_64" + ], + "windows_runtime": "four serial unit groups and four serial integration groups; generation runs once", + "windows_legacy_diagnostics": "preserve source-owner stamps and staging operation context before generic errors", + "recovery_fixture": "Windows uses the same 5000 ms reconciliation cadence before/after restoring only memory limits, with a 240-second logical recovery bound; production timings unchanged", + "local_verification": { + "xtask_tests_passed": 42, + "format": "passed", + "clippy_workspace_all_targets_all_features_warnings_denied": "passed", + "workflow_policy": "passed", + "native_macos_focused_tests_passed": 3, + "native_macos_focused_tests_seconds": 245.32, + "package_bytes": 8444246, + "package_limit": 10485760 + } + } +} diff --git a/docs/testing/ci-run-40164309832/windows-generation.log.gz b/docs/testing/ci-run-40164309832/windows-generation.log.gz new file mode 100644 index 00000000..db1f88f8 Binary files /dev/null and b/docs/testing/ci-run-40164309832/windows-generation.log.gz differ diff --git a/docs/testing/ci-run-40164309832/windows-integration.log.gz b/docs/testing/ci-run-40164309832/windows-integration.log.gz new file mode 100644 index 00000000..f5992b32 Binary files /dev/null and b/docs/testing/ci-run-40164309832/windows-integration.log.gz differ diff --git a/docs/testing/ci-run-40164309832/windows-unit.log.gz b/docs/testing/ci-run-40164309832/windows-unit.log.gz new file mode 100644 index 00000000..4897240f Binary files /dev/null and b/docs/testing/ci-run-40164309832/windows-unit.log.gz differ diff --git a/docs/testing/ci-run-40172216502/macos-generation.log.gz b/docs/testing/ci-run-40172216502/macos-generation.log.gz new file mode 100644 index 00000000..bc1c8709 Binary files /dev/null and b/docs/testing/ci-run-40172216502/macos-generation.log.gz differ diff --git a/docs/testing/ci-run-40172216502/macos-integration.log.gz b/docs/testing/ci-run-40172216502/macos-integration.log.gz new file mode 100644 index 00000000..75d272dd Binary files /dev/null and b/docs/testing/ci-run-40172216502/macos-integration.log.gz differ diff --git a/docs/testing/ci-run-40172216502/macos-unit.log.gz b/docs/testing/ci-run-40172216502/macos-unit.log.gz new file mode 100644 index 00000000..ef79323d Binary files /dev/null and b/docs/testing/ci-run-40172216502/macos-unit.log.gz differ diff --git a/docs/testing/ci-run-40172216502/package.log.gz b/docs/testing/ci-run-40172216502/package.log.gz new file mode 100644 index 00000000..b46d5f19 Binary files /dev/null and b/docs/testing/ci-run-40172216502/package.log.gz differ diff --git a/docs/testing/ci-run-40172216502/report.json b/docs/testing/ci-run-40172216502/report.json new file mode 100644 index 00000000..7ff04adf --- /dev/null +++ b/docs/testing/ci-run-40172216502/report.json @@ -0,0 +1,51 @@ +{ + "source_commit": "55819ba7ff03f26c51f243435c41c9fceb60908c", + "tested_merge": "c2d9bae897438dc380f2b7b4891943d7f22606ae", + "merge_difference": [ + "CHANGELOG.md" + ], + "run_id": 40172216502, + "supported_targets": [ + "macos-arm64", + "windows-x86_64" + ], + "static_checks": "all passed", + "package_bytes": 8444431, + "package_files": 203, + "macos": { + "generation": "5 passed; about 28 minutes including compilation", + "integration": "passed", + "unit": "833 passed, 1 failed, 2 ignored; trickled-request fixture started its sleep before server accept" + }, + "windows": { + "unit1": "passed, including copied-orphan staging and expanded preflight", + "unit4": "passed", + "unit3": "reader-lease fixture deferred after release; source_changed; 85 attempted entries; 3008 attempts", + "unit2": "90-minute cancellation; twenty-update case reached the last revision while making steady durable progress", + "generation": "deferred-deletion regression passed; full cohort cancelled at 90 minutes during ten-update convergence", + "integration1": "passed near the 90-minute limit", + "integration2": "manual publication after failed phase exit exceeded 90-second fixture bound", + "integration3": "manual publication before legacy-PID recovery exceeded 90-second fixture bound", + "integration4": "initial graph_context returned an MCP error; original assertion omitted its body" + }, + "follow_up": { + "deadline_fixture": "synchronize real accept time and inspect actual dispatcher slot/queue; original 50 ms request budget retained", + "manual_publication_fixture": "240-second Windows logical wait; ordering checks unchanged", + "daemon_fixture": "retry only graph_busy and retain full diagnostics for all other errors", + "temporary_storage": "use runner.temp via TMP/TEMP for native tests instead of system-profile temporary storage", + "generation_groups": "four disjoint discovered case groups on Windows, no duplicated regression preflight; macOS retains one complete generation cohort", + "collector_diagnostics": "retain exact expected/actual stamps and mutation role before deferred classification", + "production_constraints": "unchanged 8 MiB ceiling, 256 KiB worker stack, and 100-entry/100 ms grants" + }, + "local_validation": { + "format": "passed", + "clippy_workspace_all_targets_all_features_warnings_denied": "passed", + "workflow_policy": "passed", + "xtask_tests_passed": 42, + "deadline_repeat_passes": 20, + "optimized_deadline_tests_passed": 2, + "native_http_daemon_fixture": "passed, 44.69 seconds", + "old_source_unit_repeat": "passed all cases; stopped afterward at expected dirty-source guard" + }, + "native_follow_up": "pending" +} diff --git a/docs/testing/ci-run-40172216502/windows-generation.log.gz b/docs/testing/ci-run-40172216502/windows-generation.log.gz new file mode 100644 index 00000000..5b5ef257 Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-generation.log.gz differ diff --git a/docs/testing/ci-run-40172216502/windows-integration1.log.gz b/docs/testing/ci-run-40172216502/windows-integration1.log.gz new file mode 100644 index 00000000..cb8595f5 Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-integration1.log.gz differ diff --git a/docs/testing/ci-run-40172216502/windows-integration2.log.gz b/docs/testing/ci-run-40172216502/windows-integration2.log.gz new file mode 100644 index 00000000..37f99080 Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-integration2.log.gz differ diff --git a/docs/testing/ci-run-40172216502/windows-integration3.log.gz b/docs/testing/ci-run-40172216502/windows-integration3.log.gz new file mode 100644 index 00000000..dcb3e96a Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-integration3.log.gz differ diff --git a/docs/testing/ci-run-40172216502/windows-integration4.log.gz b/docs/testing/ci-run-40172216502/windows-integration4.log.gz new file mode 100644 index 00000000..b8efa316 Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-integration4.log.gz differ diff --git a/docs/testing/ci-run-40172216502/windows-unit1.log.gz b/docs/testing/ci-run-40172216502/windows-unit1.log.gz new file mode 100644 index 00000000..cb64120c Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-unit1.log.gz differ diff --git a/docs/testing/ci-run-40172216502/windows-unit2.log.gz b/docs/testing/ci-run-40172216502/windows-unit2.log.gz new file mode 100644 index 00000000..ee6ca861 Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-unit2.log.gz differ diff --git a/docs/testing/ci-run-40172216502/windows-unit3.log.gz b/docs/testing/ci-run-40172216502/windows-unit3.log.gz new file mode 100644 index 00000000..eb771644 Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-unit3.log.gz differ diff --git a/docs/testing/ci-run-40172216502/windows-unit4.log.gz b/docs/testing/ci-run-40172216502/windows-unit4.log.gz new file mode 100644 index 00000000..27d3988f Binary files /dev/null and b/docs/testing/ci-run-40172216502/windows-unit4.log.gz differ diff --git a/docs/testing/ci-run-40178983306/macos-generation.log.gz b/docs/testing/ci-run-40178983306/macos-generation.log.gz new file mode 100644 index 00000000..1f208da9 Binary files /dev/null and b/docs/testing/ci-run-40178983306/macos-generation.log.gz differ diff --git a/docs/testing/ci-run-40178983306/macos-integration.log.gz b/docs/testing/ci-run-40178983306/macos-integration.log.gz new file mode 100644 index 00000000..8ee2b12f Binary files /dev/null and b/docs/testing/ci-run-40178983306/macos-integration.log.gz differ diff --git a/docs/testing/ci-run-40178983306/macos-unit.log.gz b/docs/testing/ci-run-40178983306/macos-unit.log.gz new file mode 100644 index 00000000..bd57b853 Binary files /dev/null and b/docs/testing/ci-run-40178983306/macos-unit.log.gz differ diff --git a/docs/testing/ci-run-40178983306/report.json b/docs/testing/ci-run-40178983306/report.json new file mode 100644 index 00000000..30a2c07c --- /dev/null +++ b/docs/testing/ci-run-40178983306/report.json @@ -0,0 +1,40 @@ +{ + "source_commit": "2eb7d458e708a62589978e2a6ac9bd28b2d55e91", + "tested_merge": "e266c72cf4cb4077e50b9d93c1a81140af1529ca", + "run_id": 40178983306, + "supported_targets": [ + "macos-arm64", + "windows-x86_64" + ], + "macos": { + "unit": "passed", + "generation": "passed", + "integration": "passed", + "release_artifact": "passed" + }, + "windows": { + "generation_groups": "all four passed, including ten-update convergence", + "unit_groups": "groups 2/3/4 passed; group 1 failed copied-orphan staging after grant expiration", + "integration_groups": "groups 1/3/4 passed; group 2 failed twenty-client coordinator terminal-state fixture", + "twenty_updates": "all twenty revisions published/drained/reused and final oracle checks passed", + "integration_main_batch_seconds_before": 3975.309, + "integration_main_batch_seconds_runner_temp": 322.752 + }, + "root_cause": { + "legacy_control": "A 100 ms grant expired during a durable metadata transaction; dispatch interpreted role payload before completing the authenticated pending intent. Subsequent legacy stream steps required committed metadata and rejected it forever.", + "coordinator_fixture": "Preexisting-root capture silently skipped identity errors and raced owner/birth shutdown. Capture now holds actual worker/writer leases, requires all identities, and uses metadata-only no-follow Windows directory handles." + }, + "repair": { + "control_recovery": "Perform existing authenticated metadata recovery before role dispatch; only exact intended slot/commit bytes are replayed through existing ownership guards.", + "regression": "Actual dispatcher recovery with both durable slot-write cuts; failed before fix, passed after.", + "constraints": "No production ceiling, stack, mutation-grant, durability or ownership-check reductions." + }, + "local_validation": { + "coordinator_process": "passed, 36.01 seconds, resource and containment assertions retained", + "new_recovery_regression": "passed both durable cut points", + "clippy_workspace_all_targets_all_features_warnings_denied": "passed", + "format": "passed", + "native_storage_group": "4 passed, 337.60 seconds; durable metadata cuts, interrupted role dispatch, copied orphan and reader lease" + }, + "native_follow_up": "pending" +} diff --git a/docs/testing/ci-run-40178983306/windows-generation4.log.gz b/docs/testing/ci-run-40178983306/windows-generation4.log.gz new file mode 100644 index 00000000..60cb09f8 Binary files /dev/null and b/docs/testing/ci-run-40178983306/windows-generation4.log.gz differ diff --git a/docs/testing/ci-run-40178983306/windows-integration1.log.gz b/docs/testing/ci-run-40178983306/windows-integration1.log.gz new file mode 100644 index 00000000..ed7e71db Binary files /dev/null and b/docs/testing/ci-run-40178983306/windows-integration1.log.gz differ diff --git a/docs/testing/ci-run-40178983306/windows-integration2.log.gz b/docs/testing/ci-run-40178983306/windows-integration2.log.gz new file mode 100644 index 00000000..6e6e2d70 Binary files /dev/null and b/docs/testing/ci-run-40178983306/windows-integration2.log.gz differ diff --git a/docs/testing/ci-run-40178983306/windows-unit1.log.gz b/docs/testing/ci-run-40178983306/windows-unit1.log.gz new file mode 100644 index 00000000..79f9683e Binary files /dev/null and b/docs/testing/ci-run-40178983306/windows-unit1.log.gz differ diff --git a/docs/testing/ci-run-40178983306/windows-unit2.log.gz b/docs/testing/ci-run-40178983306/windows-unit2.log.gz new file mode 100644 index 00000000..bf4eb95f Binary files /dev/null and b/docs/testing/ci-run-40178983306/windows-unit2.log.gz differ diff --git a/docs/testing/ci-run-40183244906/jobs-at-fixture-fix.json b/docs/testing/ci-run-40183244906/jobs-at-fixture-fix.json new file mode 100644 index 00000000..dda0e5f5 --- /dev/null +++ b/docs/testing/ci-run-40183244906/jobs-at-fixture-fix.json @@ -0,0 +1,128 @@ +[ + { + "id": 121138549985, + "name": "cargo clippy", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550037, + "name": "cargo publish dry-run", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550046, + "name": "native (windows-x86_64) / native tests (windows-x86_64, integration, shard 2)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550048, + "name": "native (windows-x86_64) / native tests (windows-x86_64, integration, shard 1)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550073, + "name": "native (windows-x86_64) / native tests (windows-x86_64, generation, shard 4)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550078, + "name": "native (windows-x86_64) / native tests (windows-x86_64, generation, shard 3)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550088, + "name": "native (windows-x86_64) / native tests (windows-x86_64, integration, shard 4)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550095, + "name": "cargo fmt", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550141, + "name": "native (windows-x86_64) / native tests (windows-x86_64, integration, shard 3)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550146, + "name": "cargo audit", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550179, + "name": "native (macos-arm64) / native tests (macos-arm64, integration)", + "status": "completed", + "conclusion": "failure" + }, + { + "id": 121138550324, + "name": "native (macos-arm64) / native tests (macos-arm64, generation)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550355, + "name": "native (windows-x86_64) / native tests (windows-x86_64, unit, shard 2)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550372, + "name": "native (windows-x86_64) / native tests (windows-x86_64, unit, shard 4)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550381, + "name": "native (windows-x86_64) / native tests (windows-x86_64, generation, shard 1)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550406, + "name": "native (macos-arm64) / native tests (macos-arm64, unit)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550413, + "name": "native (windows-x86_64) / native tests (windows-x86_64, unit, shard 1)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550441, + "name": "native (windows-x86_64) / native tests (windows-x86_64, unit, shard 3)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121138550494, + "name": "native (windows-x86_64) / native tests (windows-x86_64, generation, shard 2)", + "status": "completed", + "conclusion": "success" + }, + { + "id": 121147837128, + "name": "native (macos-arm64) / native artifact (${{ inputs.target }})", + "status": "completed", + "conclusion": "skipped" + }, + { + "id": 121150195940, + "name": "native (windows-x86_64) / native artifact (windows-x86_64)", + "status": "in_progress", + "conclusion": null + } +] diff --git a/docs/testing/ci-run-40183244906/macos-integration.log.gz b/docs/testing/ci-run-40183244906/macos-integration.log.gz new file mode 100644 index 00000000..d6a867e2 Binary files /dev/null and b/docs/testing/ci-run-40183244906/macos-integration.log.gz differ diff --git a/docs/testing/ci-run-40183244906/report.json b/docs/testing/ci-run-40183244906/report.json new file mode 100644 index 00000000..6ee514fe --- /dev/null +++ b/docs/testing/ci-run-40183244906/report.json @@ -0,0 +1,23 @@ +{ + "source_commit": "c1faa03f5cab17a9f9512c0b9a6b0f3e27c7e5b7", + "run_id": 40183244906, + "windows": "all twelve native test groups passed; release artifact build still running at snapshot", + "macos": { + "unit": "passed", + "generation": "passed", + "integration": "21 passed, 1 failed, 1 ignored; pre-injection initial build START exchange returned EAGAIN; authenticated cleanup reported containment_empty=true" + }, + "local_same_source_repeat": { + "unit": "passed", + "generation": "5 passed; 971.425 test-command seconds", + "integration": "not started; source guard stopped after fixture edit" + }, + "repair": "Setup retries at most twice only for the exact guarded-phase START EAGAIN plus successful containment-empty cleanup with no supervision error. All other errors, unknown containment and injected faults remain failures. Production timeouts unchanged.", + "validation": { + "classifier": "passed positive and negative cleanup/error cases", + "native_manual_publication_lifecycle": "passed; 192.28 seconds for both selected tests", + "clippy_workspace_all_targets_all_features_warnings_denied": "passed", + "format": "passed" + }, + "qualification": "pending on fixture-repair commit" +} diff --git a/docs/testing/graph-disk-cleanup-evidence.json b/docs/testing/graph-disk-cleanup-evidence.json new file mode 100644 index 00000000..cb02617b --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence.json @@ -0,0 +1,1501 @@ +{ + "schema_version": 1, + "work_id": "DC-S5", + "specification": "knowledge/architecture/graph-disk-cleanup-contract.md", + "architecture_change": "chg-6vx5tj", + "architecture_authority": "Scryer", + "checkpoint": "readiness-only; not aggregate acceptance", + "implementation_baseline": "018df35e72447fc3e014e950d1d3c6648bbd4820", + "observed_head": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "status": { + "scope_approval": "DC-S1\u2013DC-S5 revision0.1 approved2026-10-21", + "local_implementation": "S1 reviewed; S2 reviewed foundation; S3/S4 partial primitives; integration pending", + "executed_evidence": "Historicalselectedfoundationexecutions, fresh2-testexternalhealth/requestexecution,10auditregressions; wholeS5pending", + "native_platform_qualification": { + "macos_arm64": "foundation observations only", + "linux": "pending", + "windows": "pending", + "rust_1_82": "blocked by preexisting pinned dependencies" + }, + "aggregate_closed": false, + "rollout": "not performed; separately authorized operational task" + }, + "limitations": [ + "Acceptance rows below remain pending until integrated production behavior is executed and independently reviewed.", + "JUnit counts count executions, not unique scenarios: library and storage_foundation suites duplicate some test bodies.", + "No database/generation/run/legacy/Direct retirement qualification while public raw-handle policy is unresolved.", + "No live repository graph rebuild, daemon operation, installation upgrade or data cleanup performed.", + "Evidence source snapshots are individually identified; passing an earlier snapshot does not qualify later changes.", + "S3 expanded partial review found confirmed regressions; passing31 foundation executions do not establish review acceptance. See separate review-outcomes evidence." + ], + "traceability": [ + { + "id": "DC-S1-R1", + "type": "requirement", + "specification": "Observe only the current repository's configured managed root and recognized repository-local historical/Direct state whose association can be established. Do not scan other repositories or arbitrary siblings.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ], + "supporting_foundation_reports": [ + "s1-post-main" + ] + }, + { + "id": "DC-S1-R2", + "type": "requirement", + "specification": "Inventory performs no materialization, publication replay, repair, deletion, configuration update or metadata creation. A missing installation is reported without creating directories or lock files.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ], + "supporting_foundation_reports": [ + "s1-post-main" + ] + }, + { + "id": "DC-S1-R3", + "type": "requirement", + "specification": "Categorize entries as active graph, active parsing cache, leased generation, live build, recovery evidence, artifact temporary/replacement state, historical staging, retired-Direct candidate, coordination metadata or unknown. Each physical file is accounted for once in totals.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ], + "supporting_foundation_reports": [ + "s1-post-main" + ] + }, + { + "id": "DC-S1-R4", + "type": "requirement", + "specification": "Give each entry a disposition: retain, potential reclamation candidate, deferred, or unknown, with a concrete reason. Active-manifest non-reference alone cannot override a live run, lease or recovery reference.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ], + "supporting_foundation_reports": [ + "s1-post-main" + ] + }, + { + "id": "DC-S1-R5", + "type": "requirement", + "specification": "Retain the active graph bundle, active parsing cache and stable locks. Treat recognized old repository-local Direct destinations as retirement candidates under the already selected policy, subject to their own ownership checks.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ], + "supporting_foundation_reports": [ + "s1-post-main" + ] + }, + { + "id": "DC-S1-R6", + "type": "requirement", + "specification": "Report logical bytes, filesystem-reported allocated bytes where available, file/directory counts, measurement time, scope identity, active-generation identity and scan completeness. Unknown size remains unknown rather than being reported as zero. Allocated-byte estimates do not promise an equal increase in free disk space.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ], + "supporting_foundation_reports": [ + "s1-post-main" + ] + }, + { + "id": "DC-S1-R7", + "type": "requirement", + "specification": "If state changes during scanning, report a moving/partial observation or bounded retry; never label inconsistent totals as a coherent exact snapshot. A collector cannot delete using a stale inventory.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ], + "supporting_foundation_reports": [ + "s1-post-main" + ] + }, + { + "id": "DC-S1-R8", + "type": "requirement", + "specification": "Do not follow symlinks. Preserve unrecognized entries. Invalid roots, unreadable metadata, missing/corrupt active pointers, ambiguous Direct mappings and permissions failures produce explicit incomplete/blocked reasons; they do not make all files reclaimable.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ], + "supporting_foundation_reports": [ + "s1-post-main" + ] + }, + { + "id": "DC-S1-A1", + "type": "acceptance", + "specification": "Given active artifacts, old staging and a live run, when inventory runs, then every item is attributed once and active/live state is retained or deferred.", + "applicable_existing_claims": "Graph Store resp-109, resp-232, resp-233, resp-234; additive inventory claim to be planned if needed", + "planned_method": "Isolated fixture and independent size/count oracle; planned", + "integrated_status": "local foundation supported; integrated/platform qualification pending", + "implementation_anchors": [], + "executed_evidence": [ + { + "report_id": "s1-post-main", + "tests": [ + "storage::inventory::tests::dc_s1_a1_classifies_and_counts_active_live_recovery_and_legacy" + ], + "result": "passed", + "scope": "local foundation; does not close integrated or native-platform gates" + } + ], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ] + }, + { + "id": "DC-S1-A2", + "type": "acceptance", + "specification": "Given a missing root or a valid installation, when inventory runs or is interrupted, then the file tree, configuration and active pointer remain unchanged.", + "applicable_existing_claims": "Existing coordination/confinement contracts; no invented runtime claim", + "planned_method": "Before/after tree and metadata comparison; planned", + "integrated_status": "local foundation supported; integrated/platform qualification pending", + "implementation_anchors": [], + "executed_evidence": [ + { + "report_id": "s1-post-main", + "tests": [ + "storage::inventory::tests::dc_s1_a2_missing_and_dropped_observations_do_not_create_or_repair" + ], + "result": "passed", + "scope": "local foundation; does not close integrated or native-platform gates" + } + ], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ] + }, + { + "id": "DC-S1-A3", + "type": "acceptance", + "specification": "Given a pointer change, inaccessible subtree, symlink or ambiguous Direct pair, when inventory runs, then it exposes incomplete/unknown state without traversing or authorizing removal.", + "applicable_existing_claims": "Graph Store resp-109; Runtime Resolver resp-144", + "planned_method": "Deterministic mutation/error fixtures; planned", + "integrated_status": "local foundation supported; integrated/platform qualification pending", + "implementation_anchors": [], + "executed_evidence": [ + { + "report_id": "s1-post-main", + "tests": [ + "storage::inventory::tests::dc_s1_a3_constant_pointer_does_not_hide_artifact_and_run_mutations", + "storage::inventory::tests::dc_s1_a3_invalid_pointer_and_foreign_direct_are_retained", + "storage::inventory::tests::dc_s1_a3_symlinks_are_not_followed_and_sizes_are_unknown" + ], + "result": "passed", + "scope": "local foundation; does not close integrated or native-platform gates" + } + ], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ] + }, + { + "id": "DC-S1-A4", + "type": "acceptance", + "specification": "Given two unchanged observations with the same options, when inventory runs twice, then categories, reasons and totals agree apart from observation metadata.", + "applicable_existing_claims": "Existing identity and deterministic behavior contracts", + "planned_method": "Repeat-fixture comparison; planned", + "integrated_status": "local foundation supported; integrated/platform qualification pending", + "implementation_anchors": [], + "executed_evidence": [ + { + "report_id": "s1-post-main", + "tests": [ + "storage::inventory::tests::dc_s1_a4_repeat_observation_is_deterministic" + ], + "result": "passed", + "scope": "local foundation; does not close integrated or native-platform gates" + } + ], + "supporting_foundation_anchors": [ + { + "path": "src/storage/inventory.rs", + "symbol": "inspect_storage", + "commit": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "scope": "read-only inventory foundation" + } + ], + "applicable_planned_claims": [ + "node-2yhnem/resp-a133nw", + "node-2yhnem/resp-hd92fb" + ] + }, + { + "id": "DC-S2-R1", + "type": "requirement", + "specification": "Reuse the approved process-containment and worker-recovery contracts from PR #145 and their qualification work; verify their presence on the exact selected implementation baseline. Do not duplicate the older repair or treat wiki claims as proof of deployed code.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-R2", + "type": "requirement", + "specification": "Require the destination's exclusive writer ownership before deciding destructive eligibility. Direct retirement uses that exact Direct destination's ownership protocol; managed ownership alone is insufficient.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-R3", + "type": "requirement", + "specification": "Respect generation reader leases, live run leases, worker/phase containment and interrupted-publication journals. Missing or unverifiable evidence beside possible execution state remains unknown.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-R4", + "type": "requirement", + "specification": "Recover or resolve interrupted publication through its established protocol before computing authoritative references. Preserve the last committed graph while recovery cannot safely proceed.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-R5", + "type": "requirement", + "specification": "Keep a consistent writer-to-state acquisition order. A caller that already owns the writer lease uses that ownership without reacquiring it. Contention defers maintenance instead of bypassing ownership.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-R6", + "type": "requirement", + "specification": "Preserve stable lock files/inodes and evidence while authority is unknown. Never kill a process or authorize deletion solely from a numeric PID or elapsed age.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-R7", + "type": "requirement", + "specification": "Return explicit proven-idle, in-use or unknown outcomes with destination-specific reasons. Recheck the relevant authority immediately before detaching/deleting state.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-R8", + "type": "requirement", + "specification": "Historical helpers that predate current ownership protocols require a verified quiescent migration procedure. Current-lock acquisition by itself cannot authorize deletion against those helpers. If proof is unavailable, preserve state and report what is required.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-A1", + "type": "acceptance", + "specification": "Given a paused owned loader, when its outer worker dies and cleanup/writing is requested, then evidence remains until teardown is verified and no conflicting publication occurs.", + "applicable_existing_claims": "Worker node-208, approved Guardian claims from PR #145, Graph Store resp-109/232", + "planned_method": "Reuse/extend native recovery regressions on the selected baseline; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-A2", + "type": "acceptance", + "specification": "Given an active managed reader or Direct reader/writer, when maintenance tries retirement, then protected files survive and the attempt is deferred.", + "applicable_existing_claims": "Graph Store resp-109, resp-233", + "planned_method": "Separate-process lease fixture; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-A3", + "type": "acceptance", + "specification": "Given unverifiable evidence or a reused unrelated PID, when maintenance runs, then it preserves unknown state and never signals the unrelated process.", + "applicable_existing_claims": "Existing approved worker/Guardian legacy and unknown-containment contracts", + "planned_method": "Existing native cases plus collector integration; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S2-A4", + "type": "acceptance", + "specification": "Given a caller already holding writer ownership or a historical helper using old state, when collection is requested, then there is no self-deadlock or unsafe legacy deletion.", + "applicable_existing_claims": "Graph Store resp-109", + "planned_method": "Ownership-order and legacy-consumer fixtures; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_ownership.rs", + "symbol": "claim_maintenance", + "commit": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "scope": "owned use-guard foundation; public native raw-handle policy unresolved" + } + ], + "applicable_planned_claims": [ + "node-2xwzve/resp-b9ty5n", + "node-2xwzve/resp-vc8v6p" + ], + "supporting_foundation_reports": [ + "s2-family-focused", + "s2-family-regression" + ] + }, + { + "id": "DC-S3-R1", + "type": "requirement", + "specification": "Collect unreferenced valid artifact keys only after recomputing current references under DC-S2 authority. Preserve active/live-run/recovery references and any unknown necessary state.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-R2", + "type": "requirement", + "specification": "Recover or remove abandoned artifact temporaries and replacement backups. A valid backup of a missing referenced artifact must be reconciled before it can be classified disposable. Invalid/ambiguous backup evidence is preserved with a reason.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-R3", + "type": "requirement", + "specification": "Recover abandoned modern workspaces through their established journals and collect them only after the associated execution is proven empty. Continue lease-aware retirement of superseded generations.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-R4", + "type": "requirement", + "specification": "Run an idempotent migration for recognized old native-staging files and snapshots once legacy quiescence is proven. Unrecognized content is retained and reported. No age-based grace period is introduced for proven unnecessary state.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-R5", + "type": "requirement", + "specification": "Retire approved former repository-local Direct destinations as coherent database/manifest/sidecar/cache bundles. Keep stable ownership files. A later explicit request must receive a clear rebuild-required outcome; explicit rebuilding may create a fresh destination through the normal writer protocol. Exact public wording/schema is validated with DC-S4.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-R6", + "type": "requirement", + "specification": "Persist recoverable retirement state before destructive work. An interrupted collection resumes idempotently; it cannot make a partial Direct bundle appear committed or revive partially deleted state as a valid graph.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-R7", + "type": "requirement", + "specification": "Keep reader-facing state-lock sections short. Detach only proven-unreferenced state, then perform bounded deletion outside the reader-facing critical section. Before detachment fails, preserve the original state; after durable detachment, retry the recorded deletion.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-R8", + "type": "requirement", + "specification": "A filesystem failure records remaining work and preserves the primary operation's result/error. Preserve active graph readability, caller-owned outputs and foreign data. Do not evict referenced active artifacts to satisfy a disk quota.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-A1", + "type": "acceptance", + "specification": "Given obsolete keys and synthetic temporary/replacement residue, when ownership is idle and maintenance completes twice, then unnecessary bytes disappear and active cache reuse survives.", + "applicable_existing_claims": "Graph Store resp-232, resp-234; additional collection claims if needed", + "planned_method": "Artifact interruption/recovery fixtures; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-A2", + "type": "acceptance", + "specification": "Given a missing active artifact with a usable replacement backup, when recovery runs, then usable required state is restored/preserved before backup removal.", + "applicable_existing_claims": "Graph Store resp-234", + "planned_method": "Replacement-boundary fixtures; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-A3", + "type": "acceptance", + "specification": "Given historical staging and an approved old Direct destination, when migration completes, then both are retired coherently and current managed query results remain unchanged.", + "applicable_existing_claims": "Graph Store resp-108/109/232/233; lifecycle node-136; proposed retirement policy needs Scryer mapping", + "planned_method": "Legacy/Direct fixtures and current graph comparison; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-A4", + "type": "acceptance", + "specification": "Given process death at each retirement boundary or a permissions failure, when maintenance restarts, then it converges without double publication, unsafe deletion or loss of the active graph.", + "applicable_existing_claims": "Graph Store resp-108, resp-109, resp-232", + "planned_method": "Journal-boundary process fixtures; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S3-A5", + "type": "acceptance", + "specification": "Given many eligible files and a concurrent graph reader, when recursive deletion runs, then readers can proceed and external sentinels remain unchanged.", + "applicable_existing_claims": "Graph Store resp-109 and confinement contract", + "planned_method": "Deterministic paused-collector/concurrent-reader fixture; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance.rs", + "symbol": "maintain_storage", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "scope": "partial guarded primitives; canonical collection gated" + } + ], + "applicable_planned_claims": [ + "node-eje4rb/resp-dnnxna", + "node-eje4rb/resp-4mkaxr" + ], + "supporting_foundation_reports": [ + "s3-expanded" + ] + }, + { + "id": "DC-S4-R1", + "type": "requirement", + "specification": "Schedule maintenance at coordinator startup, after build success/failure/cancellation, after unchanged refresh, and periodically while the coordinator runs. With no long-lived coordinator, ordinary runtime entries request bounded maintenance through the same protocol.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ] + }, + { + "id": "DC-S4-R2", + "type": "requirement", + "specification": "At most one collector acts on a destination at a time. Coalesce duplicate triggers; do not compete with an active writer or create a graph rebuild because cleanup changed internal files.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ] + }, + { + "id": "DC-S4-R3", + "type": "requirement", + "specification": "Proposed schedule: a 60-second background maintenance interval; event triggers request an immediate eligible attempt. Proposed deletion batch: up to 100 entries or a 100 ms cooperative work budget, then yield. A single filesystem operation may overrun that soft budget; this is not a hard syscall or completion deadline. Exact defaults require DC-S4 validation.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ] + }, + { + "id": "DC-S4-R4", + "type": "requirement", + "specification": "Retry deferred work on later triggers/intervals without requiring source edits. Track and resume progress so repeated bounded attempts eventually visit every eligible entry. Do not busy-loop on blocked or failing paths.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ] + }, + { + "id": "DC-S4-R5", + "type": "requirement", + "specification": "Proposed reporting surface: additive storage-maintenance fields on existing health/materialization reporting, available through existing CLI/MCP operation paths. No new GUI or standalone command is assumed. Existing response fields and error meanings remain compatible unless a specific change is separately approved.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ], + "supporting_external_contract_evidence": { + "manifest": "graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "tests": [ + "health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown", + "nonboolean_quiescence_confirmation_is_rejected_before_repository_effects" + ], + "result": "2passed; narrowpresentationandinputcontractonly; fullcriterionpending" + } + }, + { + "id": "DC-S4-R6", + "type": "requirement", + "specification": "Expose observation time/completeness, logical/allocated bytes by category, potential reclaimable bytes, deferred/unknown bytes, last attempt/outcome, reclaimed allocation estimates, and concrete blocked reasons. Do not report a live build as confirmed garbage or report unknown space as zero.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ], + "supporting_external_contract_evidence": { + "manifest": "graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "tests": [ + "health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown", + "nonboolean_quiescence_confirmation_is_rejected_before_repository_effects" + ], + "result": "2passed; narrowpresentationandinputcontractonly; fullcriterionpending" + } + }, + { + "id": "DC-S4-R7", + "type": "requirement", + "specification": "A cleanup failure must not mislabel an already committed generation as unpublished. Preserve the primary build failure and report cleanup pending separately. Do not silently erase cleanup failures.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ] + }, + { + "id": "DC-S4-R8", + "type": "requirement", + "specification": "New maintenance configuration/response contracts, defaults and compatibility behavior require Scryer planning and explicit DC-S4 validation before implementation. Do not log control capabilities or file contents as disk diagnostics.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ], + "supporting_external_contract_evidence": { + "manifest": "graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "tests": [ + "health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown", + "nonboolean_quiescence_confirmation_is_rejected_before_repository_effects" + ], + "result": "2passed; narrowpresentationandinputcontractonly; fullcriterionpending" + } + }, + { + "id": "DC-S4-A1", + "type": "acceptance", + "specification": "Given only failed builds followed by no-op refreshes, when maintenance opportunities occur, then eligible artifacts converge without a successful publication or source edit.", + "applicable_existing_claims": "Materialization resp-231/270; Refresh resp-147/268; proposed maintenance trigger claims required", + "planned_method": "Controlled-clock lifecycle/integration tests; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ] + }, + { + "id": "DC-S4-A2", + "type": "acceptance", + "specification": "Given repeated simultaneous triggers and an active writer, when the scheduler runs, then attempts coalesce, yield and later resume with no overlapping collector/writer or rebuild loop.", + "applicable_existing_claims": "Refresh resp-266/267/268; Graph Store resp-109", + "planned_method": "Scheduler and process interleavings; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ] + }, + { + "id": "DC-S4-A3", + "type": "acceptance", + "specification": "Given a permission failure, unknown containment or live lease, when health is read, then pending work and reason are distinguishable from retained active space; recovery later clears the specific reason.", + "applicable_existing_claims": "Refresh resp-269; public-contract node-96; new storage reporting claims required", + "planned_method": "CLI/MCP contract fixtures; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ], + "supporting_external_contract_evidence": { + "manifest": "graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "tests": [ + "health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown", + "nonboolean_quiescence_confirmation_is_rejected_before_repository_effects" + ], + "result": "2passed; narrowpresentationandinputcontractonly; fullcriterionpending" + } + }, + { + "id": "DC-S4-A4", + "type": "acceptance", + "specification": "Given a committed generation and a later cleanup failure, when the operation responds, then its committed publication remains truthful and cleanup is separately pending.", + "applicable_existing_claims": "Graph Store resp-108; Materialization resp-238", + "planned_method": "Fault injection after commit; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [ + { + "path": "src/storage/maintenance_scheduler.rs", + "symbol": "MaintenanceScheduler", + "source_sha256": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf", + "scope": "scheduler primitives only; production trigger/report wiring pending" + } + ], + "applicable_planned_claims": [ + "node-hxxbg1/resp-76md43" + ], + "supporting_foundation_reports": [ + "s4-scheduler-primitive" + ] + }, + { + "id": "DC-S5-R1", + "type": "requirement", + "specification": "Trace every accepted requirement and criterion to applicable Scryer claims, real implementation anchors, a verification method and executed evidence. Do not invent claims or label configured/attached tests as passing executions.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-R2", + "type": "requirement", + "specification": "Extend whole-state convergence coverage beyond database size: valid keys, temporary/replacement state, runs, retired generations, tombstones, legacy staging and Direct retirement. Use an isolated clean-control fixture with the same final source state.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-R3", + "type": "requirement", + "specification": "Proposed churn qualification: 20 supported source updates plus failures, unchanged refreshes and repeated maintenance. At verified idle, retain one active generation, current cache and necessary coordination files, with no known collectible residue. Blocked foreign/unknown state must be reported and excluded from a successful clean-state claim.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-R4", + "type": "requirement", + "specification": "Prove graph/search/context equivalence and preservation of unchanged artifact reuse. Compare expected reachable content, not only counts or empty directories.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-R5", + "type": "requirement", + "specification": "Exercise process death at temporary-write, replacement, publication, detachment and deletion boundaries; reader/writer contention; cancellation; permissions failures; stale inventory; missing/corrupt authority; symlink/foreign paths; historical helper ownership; and retries without new edits. Faults control only fixture-owned processes.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-R6", + "type": "requirement", + "specification": "Use existing native CI platform gates and lifecycle profiles. Local macOS observations, extracted-function checks, default compilation, native Windows qualification and aggregate closure remain separate evidence. New required regressions run normally, without ignore or expected-failure treatment.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-R7", + "type": "requirement", + "specification": "Before any implementation, obtain the repository-required fresh GPT-6.1 Sol Max proposal review and resolve decision-blocking findings. Before completion, obtain the required fresh final integrated review, fix every confirmed P0\u2013P3 defect, and reverify affected paths. This specification turn does not claim either implementation gate is complete.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-R8", + "type": "requirement", + "specification": "Record exact source revision, binary/environment identity, commands, JUnit evidence, outcomes and limitations. Complete required formatting/build/static/test checks without unresolved compiler warnings, then reconcile Scryer and wiki outcomes.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-R9", + "type": "requirement", + "specification": "Rollout requires a compatible verified binary/helper set and truthful inventory before collection. Selection of installation/restart/live migration actions is validated separately with the actual operational context. Specification preparation neither updates an old daemon nor deletes this installation's data.", + "applicable_existing_claims": null, + "planned_method": null, + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-A1", + "type": "acceptance", + "specification": "Given the same final source workload, when churn and clean-control builds reach verified idle, then the collector leaves only allowed state and equivalent graph behavior.", + "applicable_existing_claims": "Existing Graph Store/Materialization contracts and accepted DC-S1\u2013S4", + "planned_method": "Whole-state/native integration reports; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-A2", + "type": "acceptance", + "specification": "Given every listed interruption/ownership case, when recovery and repeated maintenance run, then required data and external sentinels survive and eligible work eventually clears.", + "applicable_existing_claims": "Approved recovery contracts and accepted DC-S2/S3", + "planned_method": "Native lifecycle JUnit reports; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-A3", + "type": "acceptance", + "specification": "Given a claimed completed implementation, when its evidence is audited, then each requirement has a real anchor, executed passing evidence, reviewed snapshot and honest platform limits.", + "applicable_existing_claims": "Scryer closure rules; AGENTS review/CI/Git workflow", + "planned_method": "Traceability/evidence audit and independent reviewer verification; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + }, + { + "id": "DC-S5-A4", + "type": "acceptance", + "specification": "Given a candidate rollout against this repository, when inventory and binary identity are checked, then live migration uses the approved policy and the proven ownership protocol.", + "applicable_existing_claims": "Accepted DC-S1\u2013S4 plus actual deployment context", + "planned_method": "Deployment inventory, identity, before/after query and disk evidence; planned", + "integrated_status": "pending", + "implementation_anchors": [], + "executed_evidence": [], + "supporting_foundation_anchors": [], + "applicable_planned_claims": [], + "supporting_foundation_reports": [] + } + ], + "foundation_evidence": "graph-disk-cleanup-evidence/foundation-reports.json", + "msrv_diagnosis": "graph-disk-cleanup-evidence/msrv-readiness.json", + "pending_verification": [ + "20 supported source updates plus failures/no-ops against clean control with same final source", + "Whole owned-state convergence including more than one bounded batch and blocked-first fairness", + "Native graph/search/context equivalence and unchanged artifact reuse", + "Process-death boundaries; paused reader/writer validation races; alias/overlap; permission recovery", + "Fresh Direct ACTIVE incarnation while old detached deletion pending", + "Integrated format/check/clippy/full applicable suites without warnings", + "Fresh different GPT-6.1 Sol Max integrated review and fix verification", + "Required native platform and MSRV gates" + ], + "review_outcomes": "graph-disk-cleanup-evidence/review-outcomes.json", + "s5_external_execution": "graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "historical_s5_external_execution": "graph-disk-cleanup-evidence/s5-external-health-source-manifest.json", + "audit_regression_execution": "graph-disk-cleanup-evidence/audit-regression-execution.json" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json b/docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json new file mode 100644 index 00000000..6b0620ff --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json @@ -0,0 +1,22 @@ +{ + "command": [ + "python3", + "-B", + "docs/testing/graph-disk-cleanup-evidence/test_audit.py", + "-v" + ], + "executed_utc": "2026-10-20T18:02:15.736094+00:00", + "actual_exit_code": 0, + "actual_result": "10 unittestcases passed: originalreportcontrol,7semanticmutations,losslessgziphistorycheck,validgzipdecodedtamperrejection", + "stdout": "audit-regressions.stdout", + "stderr": "audit-regressions.stderr", + "sha256": { + "audit.py": "316630c38e1638370b8c1873174d4311cc32ae4d0d9bcbbf0045e1083ccfb518", + "test_audit.py": "b317ac2a5f4ab7ec3c52e98169e37090d65c9a7a92d397bd898962516f6f545c", + "audit-regressions.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "audit-regressions.stderr": "37deacba5b2b624d0962949aa5cb60ce368a873c072cfff764cf836bb098a66b" + }, + "historical_eight_regression_execution": "historical-eight-audit-regression-execution.json", + "historical_eight_source_commit": "9eaa8dedf4f626ebf0974f327f81d0a3c0315e57", + "limitations": "Auditcorrectnessregressions, not productioncollectorqualification andnotJUnit." +} diff --git a/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr b/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr new file mode 100644 index 00000000..72b03c73 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr @@ -0,0 +1,15 @@ +test_compressed_artifact_matches_historical_decoded_bytes (__main__.EvidenceMutationTests) ... ok +test_compressed_payload_tamper_rejected_even_with_matching_encoded_hash (__main__.EvidenceMutationTests) ... ok +test_external_case_name_mismatch_rejected_despite_valid_hash (__main__.EvidenceMutationTests) ... ok +test_external_claimed_failure_count_mismatch_rejected (__main__.EvidenceMutationTests) ... ok +test_external_missing_case_rejected_despite_valid_hash (__main__.EvidenceMutationTests) ... ok +test_external_run_identity_mismatch_rejected_despite_valid_hash (__main__.EvidenceMutationTests) ... ok +test_external_skip_rejected_even_with_matching_manifest_counts (__main__.EvidenceMutationTests) ... ok +test_external_skip_rejected_with_original_claimed_counts (__main__.EvidenceMutationTests) ... ok +test_named_foundation_skip_rejected_even_with_matching_hash_and_counts (__main__.EvidenceMutationTests) ... ok +test_original_actual_reports_pass (__main__.EvidenceMutationTests) ... ok + +---------------------------------------------------------------------- +Ran 10 tests in 0.232s + +OK diff --git a/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stdout b/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stdout new file mode 100644 index 00000000..e69de29b diff --git a/docs/testing/graph-disk-cleanup-evidence/audit.py b/docs/testing/graph-disk-cleanup-evidence/audit.py new file mode 100644 index 00000000..18a90517 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/audit.py @@ -0,0 +1,124 @@ +"""Audit archived executions without treating them as aggregate acceptance. + +Run with Python 3.8+: python3 docs/testing/graph-disk-cleanup-evidence/audit.py +This checks evidence integrity, not implementation correctness or platform gates. +""" + +import hashlib +import gzip +import json +from pathlib import Path +import xml.etree.ElementTree as ET + + +def sha256(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def passed(case): + return not any(case.find(tag) is not None for tag in ("failure", "error", "skipped")) + + +def audit(root=None): + root = Path(root) if root is not None else Path(__file__).resolve().parent + index = json.loads((root.parent / "graph-disk-cleanup-evidence.json").read_text()) + reports = json.loads((root / "foundation-reports.json").read_text())["reports"] + report_ids = set() + executions = 0 + cases_by_report = {} + for report in reports: + report_id = report["id"] + assert report_id not in report_ids, report_id + report_ids.add(report_id) + path = root / report["report"] + assert sha256(path) == report["sha256"], path + xml = ET.parse(path).getroot() + cases = xml.findall(".//testcase") + cases_by_report[report_id] = cases + actual = { + "cases": len(cases), + "failed": sum(bool(c.findall("failure") + c.findall("error")) for c in cases), + "skipped": sum(c.find("skipped") is not None for c in cases), + } + assert actual == report["execution_counts"], (report_id, actual) + names = [ + {"suite": suite.get("name"), "name": case.get("name"), "classname": case.get("classname")} + for suite in xml.findall(".//testsuite") + for case in suite.findall("testcase") + ] + assert names == report["case_names"], report_id + if "source_manifest" in report: + assert sha256(root / report["source_manifest"]) == report["source_manifest_sha256"] + executions += actual["cases"] + + ids = [row["id"] for row in index["traceability"]] + assert len(ids) == len(set(ids)), "duplicate work identity" + expected = { + "DC-S%d-%s%d" % (step, kind, number) + for step, requirements, acceptance in [(1, 8, 4), (2, 8, 4), (3, 8, 5), (4, 8, 4), (5, 9, 4)] + for kind, count in [("R", requirements), ("A", acceptance)] + for number in range(1, count + 1) + } + assert set(ids) == expected, (expected - set(ids), set(ids) - expected) + by_id = {report["id"]: report for report in reports} + for row in index["traceability"]: + for evidence in row["executed_evidence"]: + report = by_id[evidence["report_id"]] + names = {case["name"] for case in report["case_names"]} + assert evidence["tests"] and set(evidence["tests"]) <= names, row["id"] + if evidence["result"] == "passed": + for name in evidence["tests"]: + referenced = [case for case in cases_by_report[evidence["report_id"]] if case.get("name") == name] + assert referenced and all(passed(case) for case in referenced), (row["id"], name) + msrv = json.loads((root / "msrv-readiness.json").read_text()) + for attempt in msrv["attempts"]: + assert sha256(root / attempt["stderr"]) == attempt["stderr_sha256"] + assert attempt["actual_exit_code"] == 101 + assert msrv["baseline_manifest_lock_diff"] == "", "dependency baseline changed" + reviews = json.loads((root / "review-outcomes.json").read_text()) + for artifact in reviews["s3_expanded_partial"]["executed_failure_artifacts"]: + assert sha256(root / artifact["path"]) == artifact["sha256"] + if artifact.get("encoding") == "gzip": + decoded = gzip.decompress((root / artifact["path"]).read_bytes()) + assert hashlib.sha256(decoded).hexdigest() == artifact["decoded_sha256"] + else: + assert artifact.get("encoding") is None, "unsupported evidence encoding" + scheduler = reviews["s4_scheduler_primitive"]["actual_junit"] + assert sha256(root / scheduler["path"]) == scheduler["sha256"] + external = json.loads((root.parent / index["s5_external_execution"]).read_text()) + assert sha256(root / external["junit"]) == external["junit_sha256"] + external_xml = ET.parse(root / external["junit"]).getroot() + external_cases = external_xml.findall(".//testcase") + assert external_xml.get("uuid") == external["run_id"], "external run identity mismatch" + actual_names = sorted((case.get("name"), case.get("classname")) for case in external_cases) + expected_names = sorted((case["name"], case["classname"]) for case in external["expected_test_cases"]) + assert expected_names == sorted((name, "codebase-graph::storage_maintenance") for name in ( + "health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown", + "nonboolean_quiescence_confirmation_is_rejected_before_repository_effects", + )), "unexpected external test contract" + assert actual_names == expected_names, "external case identity mismatch" + actual_results = { + "passed": sum(passed(case) for case in external_cases), + "failed": sum(bool(case.findall("failure") + case.findall("error")) for case in external_cases), + "skipped": sum(case.find("skipped") is not None for case in external_cases), + } + assert actual_results == external["actual_result"], "external result mismatch" + assert actual_results == {"passed": 2, "failed": 0, "skipped": 0} + assert "src/api/normalization.rs" in external["source_sha256"] + for path, expected_hash in external["capture_files_sha256"].items(): + assert sha256(root / path) == expected_hash, path + capture = root / "s5-fresh-capture" + source_before = json.loads((capture / "source-before.json").read_text()) + source_after_build = json.loads((capture / "source-after-build.json").read_text()) + source_after_execution = json.loads((capture / "source-after-execution.json").read_text()) + assert source_before == source_after_build == source_after_execution == external["source_sha256"] + binary_before = json.loads((capture / "binary-before.json").read_text()) + binary_after = json.loads((capture / "binary-after.json").read_text()) + assert binary_before == binary_after == external["binary_sha256"] + print("Evidence integrity passed: %d traceability rows, %d reports, %d executions." % (len(ids), len(reports), executions)) + print("Execution counts are not unique scenarios. This audit does not establish aggregate acceptance.") + print("Separate narrow S5 health/request report: 2 passed; final integration remains pending.") + + +if __name__ == "__main__": + audit() diff --git a/docs/testing/graph-disk-cleanup-evidence/foundation-reports.json b/docs/testing/graph-disk-cleanup-evidence/foundation-reports.json new file mode 100644 index 00000000..852cb2f7 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/foundation-reports.json @@ -0,0 +1,1748 @@ +{ + "count_semantics": "Execution counts, not unique scenarios; duplicate library and storage_foundation bodies are retained.", + "reports": [ + { + "id": "s1-post-main", + "report": "s1-post-main-junit.xml", + "sha256": "43789b2717a390d201e486c0c1def0e0f7aa8cfe3548a61fe2ee4fc8546e0603", + "root_attributes": { + "name": "nextest-run", + "tests": "29", + "failures": "0", + "errors": "0", + "uuid": "467d04e0-f3a8-4d37-ab03-ea8a7216e0e9", + "timestamp": "2026-10-21T01:05:15.953+09:30", + "time": "0.724" + }, + "execution_counts": { + "cases": 29, + "failed": 0, + "skipped": 0 + }, + "case_names": [ + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::busy_writer_is_reported_without_waiting_or_mutation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::configured_ancestor_root_is_rejected_without_scanning_sources", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a1_classifies_and_counts_active_live_recovery_and_legacy", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a2_missing_and_dropped_observations_do_not_create_or_repair", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a3_constant_pointer_does_not_hide_artifact_and_run_mutations", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a3_invalid_pointer_and_foreign_direct_are_retained", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a3_symlinks_are_not_followed_and_sizes_are_unknown", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a4_repeat_observation_is_deterministic", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::existing_live_lease_is_observed_without_recovery", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::hardlinked_direct_identity_is_unknown", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::physical_hardlinks_count_once_and_allocation_uses_filesystem_blocks", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::recognized_direct_pair_uses_repository_identity", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_bound_external_store_survives_missing_conventional_state", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_case_alias_roots_are_rejected_without_double_counting", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_complete_root_reconciliation_is_insertion_order_independent", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_config_repository_parent_traversal_cannot_erase_symlink", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_config_storage_parent_traversal_cannot_erase_symlink_or_missing_component", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_configured_storage_cannot_be_overridden_with_unselected_local_root", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_current_configuration_is_revalidated_on_every_observation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_default_direct_only_is_current_graph_and_cache", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_direct_cache_inside_managed_root_is_rejected", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_explicit_direct_alongside_managed_is_current", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_external_config_requires_matching_repository_binding", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_external_current_direct_config_observes_exact_bundle_without_local_state", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_managed_config_makes_unselected_default_direct_former", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_managed_root_inside_direct_cache_is_rejected", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_raw_config_direct_spelling_selects_matching_cache", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_real_directory_dot_parent_config_paths_remain_supported", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_scope_rejects_source_state_and_unregistered_external_storage", + "classname": "codebase-graph" + } + ], + "qualification": "Foundation slice only; no aggregate acceptance conclusion.", + "source_identity": "See canonical wiki S1 accepted snapshot bb3c716; S4 source manifest linkage pending" + }, + { + "id": "s2-family-focused", + "report": "s2-family-focused-junit.xml", + "sha256": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876", + "root_attributes": { + "name": "nextest-run", + "tests": "128", + "failures": "0", + "errors": "0", + "uuid": "ea7ebe62-3562-4851-99ac-bcaf424b1e6f", + "timestamp": "2026-10-21T02:00:41.862+09:30", + "time": "3.356" + }, + "execution_counts": { + "cases": 128, + "failed": 0, + "skipped": 0 + }, + "case_names": [ + { + "suite": "codebase-graph", + "name": "execution::run::tests::dc_s2_pure_planning_does_not_create_use_coordination_files", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::configured_ancestor_root_is_rejected_without_scanning_sources", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a3_invalid_pointer_and_foreign_direct_are_retained", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::busy_writer_is_reported_without_waiting_or_mutation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a3_constant_pointer_does_not_hide_artifact_and_run_mutations", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a4_repeat_observation_is_deterministic", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a3_symlinks_are_not_followed_and_sizes_are_unknown", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a2_missing_and_dropped_observations_do_not_create_or_repair", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::dc_s1_a1_classifies_and_counts_active_live_recovery_and_legacy", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_config_repository_parent_traversal_cannot_erase_symlink", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::existing_live_lease_is_observed_without_recovery", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_complete_root_reconciliation_is_insertion_order_independent", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_case_alias_roots_are_rejected_without_double_counting", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::recognized_direct_pair_uses_repository_identity", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::physical_hardlinks_count_once_and_allocation_uses_filesystem_blocks", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_bound_external_store_survives_missing_conventional_state", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::hardlinked_direct_identity_is_unknown", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_config_storage_parent_traversal_cannot_erase_symlink_or_missing_component", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_configured_storage_cannot_be_overridden_with_unselected_local_root", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_current_configuration_is_revalidated_on_every_observation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_direct_cache_inside_managed_root_is_rejected", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_default_direct_only_is_current_graph_and_cache", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_external_config_requires_matching_repository_binding", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_managed_root_inside_direct_cache_is_rejected", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_explicit_direct_alongside_managed_is_current", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_managed_config_makes_unselected_default_direct_former", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_external_current_direct_config_observes_exact_bundle_without_local_state", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_real_directory_dot_parent_config_paths_remain_supported", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_scope_rejects_source_state_and_unregistered_external_storage", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_exact_borrowed_writer_requires_exclusive_exact_destination", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::inventory::tests::review_raw_config_direct_spelling_selects_matching_cache", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_current_v2_receipt_and_unknown_v1_follow_existing_guardian_policy", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_group_deduplicates_same_parent_and_retains_all_guards", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_external_bound_managed_store_does_not_require_local_state", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_direct_borrowed_session_truthfully_defers_active_companions", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_direct_reader_and_alternate_manifest_exclude_collector", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_group_handles_distinct_parents_and_duplicate_resource_keys", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_unicode_nfc_nfd_existing_and_creation_aliases_exclude", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_parent_aliases_deduplicate_physical_family", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_producer_conversion_holds_parent_until_session_release", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_shared_existing_readable_coordination_requires_no_write_permission", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_long_s_consumer_blocks_ascii_direct_even_with_valid_record", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_replaced_parent_incarnation_invalidates_held_use", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_hardlinks_symlinks_and_case_aliases_cannot_bypass_resource_guard", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_family_recreated_coordination_invalidates_destination_proof", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_inventory_cohort_observer_is_noncreating_and_validates_lock_identity", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_guarded_creation_and_controlled_proof_are_incarnation_bound", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_missing_startup_lease_keeps_unknown_evidence", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_owned_state_transition_never_waits_or_holds_state_during_validation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_preexisting_root_never_becomes_modern_from_guard_acquisition", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_producer_and_collector_exclude_whole_artifact_operation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_managed_borrowed_session_can_admit_without_reacquiring_writer", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_replaced_writer_inode_does_not_validate_borrowed_authority", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_review_dangling_worker_state_is_unknown_and_preserved", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_review_hardlinked_coordination_invalidates_held_authority", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_review_unreadable_worker_state_is_unknown_and_preserved", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_review_managed_read_resolution_completes_with_paused_collector", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_worker_launch_evidence_without_lifecycle_is_unknown", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_waiter_on_detached_incarnation_cannot_write_fresh_public_root", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_writer_worker_and_startup_contention_are_nonblocking", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_registered_shared_manifest_and_shared_database_are_retained", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_review_legacy_database_new_cache_requires_destination_migration_proof", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_shared_manifest_registration_across_database_parents_is_retained", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_ownership::tests::dc_s2_review_publication_proof_does_not_bless_legacy_destination_or_sibling", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::dc_s2_public_pipeline_keeps_use_guards_through_final_result_assembly", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::configured_ancestor_root_is_rejected_without_scanning_sources", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::busy_writer_is_reported_without_waiting_or_mutation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a3_invalid_pointer_and_foreign_direct_are_retained", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a2_missing_and_dropped_observations_do_not_create_or_repair", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a3_constant_pointer_does_not_hide_artifact_and_run_mutations", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a4_repeat_observation_is_deterministic", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a3_symlinks_are_not_followed_and_sizes_are_unknown", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::existing_live_lease_is_observed_without_recovery", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::hardlinked_direct_identity_is_unknown", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a1_classifies_and_counts_active_live_recovery_and_legacy", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::recognized_direct_pair_uses_repository_identity", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::physical_hardlinks_count_once_and_allocation_uses_filesystem_blocks", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_bound_external_store_survives_missing_conventional_state", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_complete_root_reconciliation_is_insertion_order_independent", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_case_alias_roots_are_rejected_without_double_counting", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_config_repository_parent_traversal_cannot_erase_symlink", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_current_configuration_is_revalidated_on_every_observation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_default_direct_only_is_current_graph_and_cache", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_config_storage_parent_traversal_cannot_erase_symlink_or_missing_component", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_configured_storage_cannot_be_overridden_with_unselected_local_root", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_direct_cache_inside_managed_root_is_rejected", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_explicit_direct_alongside_managed_is_current", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_managed_root_inside_direct_cache_is_rejected", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_external_config_requires_matching_repository_binding", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_external_current_direct_config_observes_exact_bundle_without_local_state", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_managed_config_makes_unselected_default_direct_former", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_real_directory_dot_parent_config_paths_remain_supported", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_scope_rejects_source_state_and_unregistered_external_storage", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_raw_config_direct_spelling_selects_matching_cache", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_current_v2_receipt_and_unknown_v1_follow_existing_guardian_policy", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_exact_borrowed_writer_requires_exclusive_exact_destination", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_direct_borrowed_session_truthfully_defers_active_companions", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_group_deduplicates_same_parent_and_retains_all_guards", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_external_bound_managed_store_does_not_require_local_state", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_group_handles_distinct_parents_and_duplicate_resource_keys", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_parent_aliases_deduplicate_physical_family", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_producer_conversion_holds_parent_until_session_release", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_unicode_nfc_nfd_existing_and_creation_aliases_exclude", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_direct_reader_and_alternate_manifest_exclude_collector", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_replaced_parent_incarnation_invalidates_held_use", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_shared_existing_readable_coordination_requires_no_write_permission", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_hardlinks_symlinks_and_case_aliases_cannot_bypass_resource_guard", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_inventory_cohort_observer_is_noncreating_and_validates_lock_identity", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_long_s_consumer_blocks_ascii_direct_even_with_valid_record", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_recreated_coordination_invalidates_destination_proof", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_missing_startup_lease_keeps_unknown_evidence", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_guarded_creation_and_controlled_proof_are_incarnation_bound", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_owned_state_transition_never_waits_or_holds_state_during_validation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_preexisting_root_never_becomes_modern_from_guard_acquisition", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_replaced_writer_inode_does_not_validate_borrowed_authority", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_producer_and_collector_exclude_whole_artifact_operation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_managed_borrowed_session_can_admit_without_reacquiring_writer", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_hardlinked_coordination_invalidates_held_authority", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_dangling_worker_state_is_unknown_and_preserved", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_managed_read_resolution_completes_with_paused_collector", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_unreadable_worker_state_is_unknown_and_preserved", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_waiter_on_detached_incarnation_cannot_write_fresh_public_root", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_writer_worker_and_startup_contention_are_nonblocking", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_worker_launch_evidence_without_lifecycle_is_unknown", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_shared_manifest_registration_across_database_parents_is_retained", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_legacy_database_new_cache_requires_destination_migration_proof", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_registered_shared_manifest_and_shared_database_are_retained", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_publication_proof_does_not_bless_legacy_destination_or_sibling", + "classname": "codebase-graph::storage_foundation" + } + ], + "qualification": "Foundation slice only; no aggregate acceptance conclusion.", + "source_manifest": "s2-family-focused-source-manifest.json", + "source_manifest_sha256": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8" + }, + { + "id": "s2-family-regression", + "report": "s2-family-regression-junit.xml", + "sha256": "ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92", + "root_attributes": { + "name": "nextest-run", + "tests": "127", + "failures": "0", + "errors": "0", + "uuid": "c2881b37-1476-4cea-ad1c-d580d5fb1c5e", + "timestamp": "2026-10-21T02:03:12.586+09:30", + "time": "65.973" + }, + "execution_counts": { + "cases": 127, + "failed": 0, + "skipped": 0 + }, + "case_names": [ + { + "suite": "codebase-graph", + "name": "db_writer::phase::tests::phase_protocol_requires_v2_containment_contract", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "db_writer::phase::tests::phase_registry_scope_restores_nested_and_unwound_contexts", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "db_writer::phase::tests::phase_request_v2_preserves_native_resource_settings", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "db_writer::phase::tests::phase_test_allocation_is_scoped_to_the_ready_loader", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "db_writer::phase::tests::phase_test_allocation_requires_trigger_and_rejects_unbounded_memory", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "db_writer::phase::tests::scoped_phase_request_lives_outside_candidate_and_preserves_database_path", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "db_writer::write::tests::database_pool_escalation_is_bounded_and_incremental", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "db_writer::write::tests::only_failed_database_operations_retry_pool_exhaustion", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::artifact_reuse_preserves_materialization_output", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::cached_and_clean_builds_stage_identical_rows", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::dc_s2_public_pipeline_keeps_use_guards_through_final_result_assembly", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::dc_s2_pure_planning_does_not_create_use_coordination_files", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::deleted_files_are_excluded_from_materialized_entries", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::digest_only_build_changes_can_reuse_all_raw_artifacts", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::graph_summary_counts_unique_ids_once", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::missing_or_corrupt_unchanged_artifacts_rebuild_from_scanned_sources", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::scanned_payload_completes_materialization_after_source_removal", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "execution::run::tests::unchanged_partitions_reuse_artifacts_without_reparsing", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "atomic_write_failure_preserves_existing_file_and_cleans_temp", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "control_lease_rejects_a_symlinked_lock_file", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "control_leases_are_exclusive_nonblocking_and_transfer_after_release", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "direct_control_lock_paths_are_stable_and_destination_scoped", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "direct_phase_unknown_preserves_base_and_blocks_publish_replay_and_cleanup", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "direct_prepared_recovery_preserves_expected_sidecars_on_validation_failure", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "direct_recovery_replays_every_rename_and_checkpoint", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "direct_store_recovers_database_manifest_and_committed_journal_phases", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "direct_store_recovers_interrupted_publication_with_sidecars_and_split_parents", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "direct_store_rejects_forged_journal_candidate_paths", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "direct_write_session_holds_writer_lock_and_drop_cleans_unpublished_candidates", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "empty_prelease_startup_is_retired_and_does_not_block_next_writer", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "error::tests::memory_budget_failure_can_report_supervised_process_split", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "error::tests::memory_budget_failure_has_a_structured_machine_readable_message", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "error::tests::source_change_failure_has_a_stable_machine_readable_message", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "existing_lock_probe_does_not_recreate_a_vanished_workspace", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_abort_and_drop_retain_runs_while_phase_unknown", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_begin_write_holds_writer_lock_and_marks_candidate_ready_explicitly", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_cleanup_recovers_published_crash_after_active_pointer_write", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_cleanup_recovers_publishing_crash_after_active_pointer_write", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_cleanup_recovers_publishing_crash_after_generation_rename", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_cleanup_recovers_publishing_crash_before_generation_rename", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_cleanup_reports_and_later_removes_locked_runs", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_control_lock_paths_are_stable_and_role_scoped", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_last_reader_drop_triggers_best_effort_retirement_cleanup", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_metadata_sizes_include_db_sidecars_and_allocated_bytes", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_phase_unknown_preserves_candidate_and_active_read_until_empty", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_publish_reconciles_an_error_after_active_pointer_commit", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_publish_rejects_a_stale_base_generation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_store_rejects_untrusted_generation_ids", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "managed_writer_is_serialized_across_sessions", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "missing_lease_with_any_launch_evidence_remains_unknown", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "owned_writer_cleanup_reports_readers_and_collects_after_their_release", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "persisted_relative_barrier_survives_cleanup_from_a_different_process_cwd", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "persisted_run_barrier_survives_drop_and_blocks_generic_orphan_collection", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "phase_registry_collection_retains_unknown_siblings_then_reclaims_empty_orphans", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "phase_registry_probe_never_recreates_missing_roots_or_locks", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "private_lifecycle_receipt_accepts_ancestor_alias_but_rejects_foreign_or_symlink_roots", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "relative_registry_probe_matches_absolute_private_descriptor", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "run_workspace_cleanup_rejects_symlink_content", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::atomic::tests::atomic_temp_paths_are_scoped_to_the_writer_process", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::containment::tests::windows_job_name_does_not_expose_or_depend_on_control_nonce", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::containment::tests::windows_job_name_rejects_malformed_build_identity", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::busy_writer_is_reported_without_waiting_or_mutation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::configured_ancestor_root_is_rejected_without_scanning_sources", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a1_classifies_and_counts_active_live_recovery_and_legacy", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a2_missing_and_dropped_observations_do_not_create_or_repair", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a3_constant_pointer_does_not_hide_artifact_and_run_mutations", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a3_invalid_pointer_and_foreign_direct_are_retained", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a3_symlinks_are_not_followed_and_sizes_are_unknown", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::dc_s1_a4_repeat_observation_is_deterministic", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::existing_live_lease_is_observed_without_recovery", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::hardlinked_direct_identity_is_unknown", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::physical_hardlinks_count_once_and_allocation_uses_filesystem_blocks", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::recognized_direct_pair_uses_repository_identity", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_bound_external_store_survives_missing_conventional_state", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_case_alias_roots_are_rejected_without_double_counting", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_complete_root_reconciliation_is_insertion_order_independent", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_config_repository_parent_traversal_cannot_erase_symlink", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_config_storage_parent_traversal_cannot_erase_symlink_or_missing_component", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_configured_storage_cannot_be_overridden_with_unselected_local_root", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_current_configuration_is_revalidated_on_every_observation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_default_direct_only_is_current_graph_and_cache", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_direct_cache_inside_managed_root_is_rejected", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_explicit_direct_alongside_managed_is_current", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_external_config_requires_matching_repository_binding", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_external_current_direct_config_observes_exact_bundle_without_local_state", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_managed_config_makes_unselected_default_direct_former", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_managed_root_inside_direct_cache_is_rejected", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_raw_config_direct_spelling_selects_matching_cache", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_real_directory_dot_parent_config_paths_remain_supported", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::inventory::tests::review_scope_rejects_source_state_and_unregistered_external_storage", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_current_v2_receipt_and_unknown_v1_follow_existing_guardian_policy", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_direct_borrowed_session_truthfully_defers_active_companions", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_direct_reader_and_alternate_manifest_exclude_collector", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_exact_borrowed_writer_requires_exclusive_exact_destination", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_external_bound_managed_store_does_not_require_local_state", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_group_deduplicates_same_parent_and_retains_all_guards", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_group_handles_distinct_parents_and_duplicate_resource_keys", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_long_s_consumer_blocks_ascii_direct_even_with_valid_record", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_parent_aliases_deduplicate_physical_family", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_macos_unicode_nfc_nfd_existing_and_creation_aliases_exclude", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_producer_conversion_holds_parent_until_session_release", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_recreated_coordination_invalidates_destination_proof", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_replaced_parent_incarnation_invalidates_held_use", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_family_shared_existing_readable_coordination_requires_no_write_permission", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_guarded_creation_and_controlled_proof_are_incarnation_bound", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_hardlinks_symlinks_and_case_aliases_cannot_bypass_resource_guard", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_inventory_cohort_observer_is_noncreating_and_validates_lock_identity", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_managed_borrowed_session_can_admit_without_reacquiring_writer", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_missing_startup_lease_keeps_unknown_evidence", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_owned_state_transition_never_waits_or_holds_state_during_validation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_preexisting_root_never_becomes_modern_from_guard_acquisition", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_producer_and_collector_exclude_whole_artifact_operation", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_registered_shared_manifest_and_shared_database_are_retained", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_replaced_writer_inode_does_not_validate_borrowed_authority", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_dangling_worker_state_is_unknown_and_preserved", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_hardlinked_coordination_invalidates_held_authority", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_legacy_database_new_cache_requires_destination_migration_proof", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_managed_read_resolution_completes_with_paused_collector", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_publication_proof_does_not_bless_legacy_destination_or_sibling", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_review_unreadable_worker_state_is_unknown_and_preserved", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_shared_manifest_registration_across_database_parents_is_retained", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_waiter_on_detached_incarnation_cannot_write_fresh_public_root", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_worker_launch_evidence_without_lifecycle_is_unknown", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::maintenance_ownership::tests::dc_s2_writer_worker_and_startup_contention_are_nonblocking", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::run_workspace::tests::cleanup_barrier_records_its_absolute_origin", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::run_workspace::tests::confined_run_cleanup_is_idempotent", + "classname": "codebase-graph::storage_foundation" + }, + { + "suite": "codebase-graph::storage_foundation", + "name": "storage::run_workspace::tests::unanchored_persisted_barrier_cannot_authorize_orphan_cleanup", + "classname": "codebase-graph::storage_foundation" + } + ], + "qualification": "Foundation slice only; no aggregate acceptance conclusion.", + "source_manifest": "s2-family-regression-source-manifest.json", + "source_manifest_sha256": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8" + }, + { + "id": "s3-expanded", + "report": "s3-expanded-junit.xml", + "sha256": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627", + "root_attributes": { + "name": "nextest-run", + "tests": "31", + "failures": "0", + "errors": "0", + "uuid": "fc78ee6c-cec6-42bb-9bda-544e10913b47", + "timestamp": "2026-10-21T02:36:27.487+09:30", + "time": "1.185" + }, + "execution_counts": { + "cases": 31, + "failed": 0, + "skipped": 0 + }, + "case_names": [ + { + "suite": "codebase-graph", + "name": "artifact_store::tests::artifact_cleanup_rejects_symlinked_entries", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::artifact_key_changes_when_inputs_change", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::artifact_store_commits_only_durable_artifact_directories", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::artifact_store_rejects_prior_format_envelopes", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::artifact_store_rejects_wrong_expected_content", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::artifact_store_roundtrips_raw_partition_and_reuses_existing_payload", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::artifact_store_treats_corrupt_or_mismatched_payloads_as_cache_miss", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::corrupt_existing_artifact_is_replaced_atomically_on_rebuild", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::dc_s3_cooperative_envelope_preserves_checksum_and_rejects_invalid_metadata", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::dc_s3_cooperative_rows_match_existing_oracle_across_metadata_boundaries", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::malformed_artifact_keys_are_treated_as_cache_miss_and_safe_delete_noops", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::oversized_artifact_is_rejected_before_deserialization", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::reference_sized_artifact_fits_the_default_per_worker_budget", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "artifact_store::tests::streaming_partition_checksum_matches_the_legacy_canonical_payload", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_buffered_parser_parks_without_storage_locks_and_resumes_stack", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_cancel_wakes_parser_without_another_grant", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_cursor_spans_grants_and_does_not_starve_later_entries", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_detachment_defers_for_reader_but_private_deletion_does_not", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_owned_validation_job_releases_lease_before_wait_and_cancels", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_parser_and_foreground_share_single_entry_allowance", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_payload_symlink_and_hardlink_never_enter_retirement", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_prepare_then_foreign_child_rejects_public_detach", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_prepared_record_replay_preserves_original", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_private_identity_replacement_is_preserved", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_public_pending_surface_does_not_create_missing_installation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_replay_preserves_new_payload_after_durable_unlink", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_replay_rejects_unrelated_or_replaced_journal", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_retirement_one_entry_budget_resumes_private_work", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_retirement_rechecks_reference_before_detachment", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_retirement_replay_never_deletes_reused_public_name", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance::tests::dc_s3_retirement_retains_foreign_contents", + "classname": "codebase-graph" + } + ], + "qualification": "Foundation slice only; no aggregate acceptance conclusion.", + "source_manifest": "s3-expanded-source-manifest.json", + "source_manifest_sha256": "95f89f93b01ad3c9b02cbab6b97278cdbb14d604080963a4a2b8a08deee39c5c" + }, + { + "id": "s4-scheduler-primitive", + "report": "s4-scheduler-primitive-junit.xml", + "sha256": "c4d0cbb89af4996d859c7da5b1bed3b04a1a9b90dd44135bc7c9d2d2c9ecfb3f", + "root_attributes": { + "name": "nextest-run", + "tests": "10", + "failures": "0", + "errors": "0", + "uuid": "4afc80e9-29b9-4522-a8d9-a290d790d9ab", + "timestamp": "2026-10-21T02:33:19.150+09:30", + "time": "0.168" + }, + "execution_counts": { + "cases": 10, + "failed": 0, + "skipped": 0 + }, + "case_names": [ + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::cached_report_retains_observation_time_nulls_and_age", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::continuation_retries_even_when_report_pending_flag_is_clear", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::deferred_work_resumes_without_new_events_and_never_hot_loops", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::error_and_panic_release_permits_and_retry_with_unknown_allocation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::execution_contention_preserves_demand_without_fabricating_observation", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::limits_and_completion_based_cooldown_are_explicit", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::observation_from_a_future_wall_clock_has_unknown_age", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::running_requests_coalesce_and_execution_permit_is_released_before_parking", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::shutdown_during_callback_drains_then_rejects_all_new_grants", + "classname": "codebase-graph" + }, + { + "suite": "codebase-graph", + "name": "storage::maintenance_scheduler::tests::startup_duplicates_periodic_and_new_events_coalesce", + "classname": "codebase-graph" + } + ], + "qualification": "Foundation slice only; no aggregate acceptance conclusion.", + "source_identity": "Pending linkage to frozen source manifest; report alone does not qualify source." + } + ] +} diff --git a/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regression-execution.json b/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regression-execution.json new file mode 100644 index 00000000..b7a41536 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regression-execution.json @@ -0,0 +1,20 @@ +{ + "command": [ + "python3", + "-B", + "docs/testing/graph-disk-cleanup-evidence/test_audit.py", + "-v" + ], + "executed_utc": "2026-10-20T17:55:36.307466+00:00", + "actual_exit_code": 0, + "actual_result": "8 unittest cases passed, including original-report control and7isolated evidence mutations", + "stdout": "audit-regressions.stdout", + "stderr": "audit-regressions.stderr", + "sha256": { + "audit.py": "03cb5168970c16f3691018510a1ad0c4b61f0bd2800ed4267d1869cb8fc57efe", + "test_audit.py": "a18988b8ed73c090b3b05337cf6f2150b619b403be2e3cd9cc7387971a02e9c9", + "audit-regressions.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "audit-regressions.stderr": "e4ed00080d9030eba59f6c9af6ac3d6b784e69ad1e9dcb2ce9107c19fb07be0d" + }, + "limitations": "These are audit correctness regressions, not productioncollectorqualification and notJUnit." +} diff --git a/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stderr b/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stderr new file mode 100644 index 00000000..ff58c456 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stderr @@ -0,0 +1,13 @@ +test_external_case_name_mismatch_rejected_despite_valid_hash (__main__.EvidenceMutationTests) ... ok +test_external_claimed_failure_count_mismatch_rejected (__main__.EvidenceMutationTests) ... ok +test_external_missing_case_rejected_despite_valid_hash (__main__.EvidenceMutationTests) ... ok +test_external_run_identity_mismatch_rejected_despite_valid_hash (__main__.EvidenceMutationTests) ... ok +test_external_skip_rejected_even_with_matching_manifest_counts (__main__.EvidenceMutationTests) ... ok +test_external_skip_rejected_with_original_claimed_counts (__main__.EvidenceMutationTests) ... ok +test_named_foundation_skip_rejected_even_with_matching_hash_and_counts (__main__.EvidenceMutationTests) ... ok +test_original_actual_reports_pass (__main__.EvidenceMutationTests) ... ok + +---------------------------------------------------------------------- +Ran 8 tests in 0.156s + +OK diff --git a/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stdout b/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stdout new file mode 100644 index 00000000..e69de29b diff --git a/docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json b/docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json new file mode 100644 index 00000000..5319d68b --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json @@ -0,0 +1,145 @@ +{ + "status": "blocked_before_compilation", + "toolchain": "cargo1.82.0/rustc1.82.0 aarch64-apple-darwin", + "declared_project_msrv": "1.82", + "lockfile_sha256": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "baseline": "018df35e72447fc3e014e950d1d3c6648bbd4820", + "baseline_manifest_lock_diff": "", + "attempts": [ + { + "command": "cargo +1.82.0 metadata --offline --locked --format-version 1", + "outcome": "Failed resolving locked zlib-rs0.6.8; Cargo1.82 cache hash6f17 lacks the version, current Cargo cache1949 contains it.", + "stderr": "s5-msrv-metadata.stderr", + "actual_exit_code": 101, + "stdout_bytes": 0, + "stderr_sha256": "3789ba8ed22ae4a82b1fc7adaea6cd8d86d3c94beb1f30f698bd4afb3f7e6ff0" + }, + { + "command": "CARGO_HOME=/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/s5-cargo-home cargo +1.82.0 metadata --offline --locked --format-version 1", + "cache_preparation": "Copied current registry index into isolated /tmp Cargo home old-hash directory; sources/cache symlink existing current cache; no dependency or global cache modifications.", + "outcome": "Cargo1.82 cannot parse pinned clap4.6.1 manifest requiring edition2024.", + "stderr": "s5-msrv-ready-metadata.stderr", + "actual_exit_code": 101, + "stdout_bytes": 0, + "stderr_sha256": "d75191f480933414ac1b27956d159e407be16eecac57c0d782ec127207ea333e" + } + ], + "pinned_packages_declaring_higher_msrv": [ + { + "name": "clap", + "version": "4.6.1", + "declared_rust_version": "1.85", + "pinned_manifest_sha256": "1c1430d702b4b85e2cdb15b1d6444743707ce6bafb27477ebaa78789ef81ce92", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap-4.6.1/Cargo.toml" + }, + { + "name": "clap_builder", + "version": "4.6.0", + "declared_rust_version": "1.85", + "pinned_manifest_sha256": "e025ac79c30ed67555492f797f3ce2637678c1e9b1637e4571f95db81e7037e8", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_builder-4.6.0/Cargo.toml" + }, + { + "name": "clap_lex", + "version": "1.1.0", + "declared_rust_version": "1.85", + "pinned_manifest_sha256": "01d043b9d8911420d51b1b21313278cbcdac10cd852e277b25260a458f5f8fee", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_lex-1.1.0/Cargo.toml" + }, + { + "name": "crc", + "version": "3.4.0", + "declared_rust_version": "1.83", + "pinned_manifest_sha256": "bc9ea8bd807209d10bd7ab9ecd78f7275723850c3eab962093e1fe6325ce5c4b", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/Cargo.toml" + }, + { + "name": "icu_collections", + "version": "2.2.0", + "declared_rust_version": "1.86", + "pinned_manifest_sha256": "5d9b5823c57b369a560661513d2399e8e1bf0d240bc3f44b689b14a66c2c4aef", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/Cargo.toml" + }, + { + "name": "icu_locale_core", + "version": "2.2.0", + "declared_rust_version": "1.86", + "pinned_manifest_sha256": "c53044fbad7932db685457e57fde142f28e0d72f3509d3a459ac75e7b210492a", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/Cargo.toml" + }, + { + "name": "icu_normalizer", + "version": "2.2.0", + "declared_rust_version": "1.86", + "pinned_manifest_sha256": "0b2859370d4c2ea8026fde77d76c79c08038a1b9934a541868e2900a21c20959", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/Cargo.toml" + }, + { + "name": "icu_normalizer_data", + "version": "2.2.0", + "declared_rust_version": "1.86", + "pinned_manifest_sha256": "0c9f8f56b470681a8608cbf3a8e5be3b573c834594a91efce0f47be00b825eb3", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer_data-2.2.0/Cargo.toml" + }, + { + "name": "icu_properties", + "version": "2.2.0", + "declared_rust_version": "1.86", + "pinned_manifest_sha256": "34478d473cded8d8ee6a23a12fe2b3ab9d92fb5b0e88d299b5f339e4f6bdd0cb", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties-2.2.0/Cargo.toml" + }, + { + "name": "icu_properties_data", + "version": "2.2.0", + "declared_rust_version": "1.86", + "pinned_manifest_sha256": "40734ae7bdd99a9271e77406adc25203089e95d6e046ff33c0f3ff30c65ccd8b", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties_data-2.2.0/Cargo.toml" + }, + { + "name": "icu_provider", + "version": "2.2.0", + "declared_rust_version": "1.86", + "pinned_manifest_sha256": "7eb8e22e09b90f38286e669869154d4549a481e64ef670639cc21390205d1245", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.2.0/Cargo.toml" + }, + { + "name": "idna_adapter", + "version": "1.2.2", + "declared_rust_version": "1.86", + "pinned_manifest_sha256": "a5be3efe6b6b3ee7823820c9a3d47f318234f6a5fd48ab878d33cac9bc3ee573", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna_adapter-1.2.2/Cargo.toml" + }, + { + "name": "tree-sitter-language", + "version": "0.1.8", + "declared_rust_version": "1.90", + "pinned_manifest_sha256": "7f5a66a1708bac89478e7f6261d759af5643c8d68d38df73110c36482a384aaa", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-language-0.1.8/Cargo.toml" + }, + { + "name": "wasip2", + "version": "1.0.4+wasi-0.2.12", + "declared_rust_version": "1.87.0", + "pinned_manifest_sha256": "a4cf47d49115ca7543113c3c4c136e0cdd3dfff344eefa0f64ee8c5200e99fa9", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/Cargo.toml" + }, + { + "name": "wit-bindgen", + "version": "0.57.1", + "declared_rust_version": "1.85.0", + "pinned_manifest_sha256": "bb7fe08cf7f552712e29c7373c058d5fe74d650f954c2462e523e40fee273f9f", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wit-bindgen-0.57.1/Cargo.toml" + }, + { + "name": "zerovec", + "version": "0.11.6", + "declared_rust_version": "1.83", + "pinned_manifest_sha256": "90d4ff34b86ae816f0c0bcc740d8e2ad5378341e6a13231b914d6525cb29232e", + "manifest": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/Cargo.toml" + } + ], + "conclusion": "Two distinct preexisting setup blockers: old Cargo offline index lacks pinned package, then actual pinned dependency edition/MSRV incompatibility. Cleanup source compatibility has not been tested under1.82. Target-specific dependencies above1.82 are not all necessarily compiled on macOS; tree-sitter-language0.1.8 is a direct production dependency declaring1.90.", + "proposed_coordinator_action": "Choose a separate approved dependency-compatibility repair or minimum-toolchain policy; do not silently downgrade dependencies or claim1.82 qualified.", + "network_access": false, + "production_files_changed": false +} diff --git a/docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json b/docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json new file mode 100644 index 00000000..e3947685 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json @@ -0,0 +1,5 @@ +{ + "exit": 0, + "stdout": "Evidence integrity passed: 62 traceability rows, 5 reports, 325 executions.\nExecution counts are not unique scenarios. This audit does not establish aggregate acceptance.\nSeparate narrow S5 health/request report: 2 passed; final integration remains pending.\n", + "stderr": "" +} \ No newline at end of file diff --git a/docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json b/docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json new file mode 100644 index 00000000..e3947685 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json @@ -0,0 +1,5 @@ +{ + "exit": 0, + "stdout": "Evidence integrity passed: 62 traceability rows, 5 reports, 325 executions.\nExecution counts are not unique scenarios. This audit does not establish aggregate acceptance.\nSeparate narrow S5 health/request report: 2 passed; final integration remains pending.\n", + "stderr": "" +} \ No newline at end of file diff --git a/docs/testing/graph-disk-cleanup-evidence/qualification.py b/docs/testing/graph-disk-cleanup-evidence/qualification.py new file mode 100644 index 00000000..1384a329 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/qualification.py @@ -0,0 +1,449 @@ +"""Fresh DC-S5 suite execution capture; never promotes aggregate/platform gates. + +Requires Python 3.9+. Run `python3 qualification.py capture --repository REPO --output NEW_DIRECTORY` +only while holding the coordinator's Cargo target lease. `verify DIRECTORY` +checks the archived capture without rebuilding or reading the original checkout. +""" + +import argparse +import hashlib +import json +import os +from pathlib import Path +import platform +import subprocess +import xml.etree.ElementTree as ET + +SCHEMA = 3 +BINARY = "codebase-graph::storage_maintenance" +EXPECTED = { + "health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown", + "nonboolean_quiescence_confirmation_is_rejected_before_repository_effects", + "repeated_health_entries_preserve_source_and_foreign_sentinels", +} +LEGACY_LIMITATIONS = [ + "Narrow external storage_maintenance execution only; no collector qualification.", + "Native Windows/Linux, MSRV, full suite and aggregate closure remain pending.", + "Source identity covers repository build inputs; dependency provenance uses Cargo.lock and metadata.", +] +LIMITATIONS = [ + "Captured storage_maintenance suite execution only; acceptance remains criterion-specific.", + "Native Windows/Linux, MSRV, workspace full suite and aggregate closure remain pending.", + "Source identity covers repository build inputs; dependency provenance uses Cargo.lock and metadata.", +] +WORKLOAD_CASES = { + "twenty_public_builds_match_clean_control_and_preserve_unchanged_artifact", + "fixture::workload::tests::schedule_covers_twenty_updates_and_each_required_interruption_boundary", + "fixture::workload::tests::independent_control_matches_twenty_source_updates_without_rewriting_stable_input", + "fixture::workload::tests::search_oracle_omits_only_incarnation_ids_and_keeps_rank_spans_and_content", + "fixture::workload::tests::oracle_rejects_missing_response_fields_and_preserves_context_multiplicity", +} +PROCESS_CASE = "native_phase_owner_death_preserves_prior_graph_and_retries_without_source_edit" + + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +def sha256(path): + digest = hashlib.sha256() + with Path(path).open("rb") as stream: + for block in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(block) + return digest.hexdigest() + + +def write_json(path, value): + Path(path).write_text(json.dumps(value, sort_keys=True, indent=2) + "\n") + + +def read_json(path): + return json.loads(Path(path).read_text()) + + +def source_manifest(repository): + repository = Path(repository).resolve() + # Include untracked new inputs and detect additions/deletions between phases. + names = subprocess.check_output([ + "git", "ls-files", "-z", "--cached", "--others", "--exclude-standard", + ], cwd=repository).decode().split("\0") + roots = {"src", "tests", "crates", "vendor", "assets", ".cargo", ".config"} + singles = {"Cargo.toml", "Cargo.lock", "build.rs", "rust-toolchain", "rust-toolchain.toml"} + result = {} + for name in sorted(set(names) - {""}): + if name.split("/")[0] not in roots and name not in singles: + continue + path = repository / name + require(not path.is_symlink() and path.resolve().is_relative_to(repository), + "build input is a symlink or escapes repository: " + name) + result[name] = sha256(path) # Missing tracked input fails closed. + require({"Cargo.toml", "Cargo.lock", "build.rs"} <= result.keys(), "missing core build inputs") + return result + + +def run(command, repository, output, label, env=None): + with (output / (label + ".stdout")).open("wb") as stdout: + with (output / (label + ".stderr")).open("wb") as stderr: + result = subprocess.run(command, cwd=repository, stdout=stdout, stderr=stderr, check=False, env=env) + return {"argv": command, "exit_code": result.returncode} + + +def binary_paths(metadata): + binaries = metadata["rust-binaries"] + require(set(binaries) == {BINARY}, "capture must select exactly the storage_maintenance binary") + paths = {str(Path(row["binary-path"]).resolve()) for row in binaries.values()} + # nextest can launch non-test helper executables too: freeze those as inputs. + build = metadata["rust-build-meta"] + for rows in build.get("non-test-binaries", {}).values(): + for row in rows: + paths.add(str((Path(build["target-directory"]) / row["path"]).resolve())) + return sorted(paths) + + +def junit_result(path, case_names=None): + xml = ET.parse(path).getroot() + cases = xml.findall(".//testcase") + identities = [(case.get("classname"), case.get("name")) for case in cases] + expected = {(BINARY, name) for name in (EXPECTED if case_names is None else case_names)} + require(len(identities) == len(expected) and set(identities) == expected, + "unexpected, missing or duplicate external testcase") + require(all(not any(case.find(tag) is not None for tag in ("failure", "error", "skipped", "rerunFailure", "rerunError")) + for case in cases), "external testcase did not pass on its first execution") + return {"run_id": xml.get("uuid"), "passed": len(cases), "failed": 0, "skipped": 0} + + +def verify(output): + output = Path(output) + record = read_json(output / "capture.json") + schema = record["schema"] + require(schema in (1, 2, SCHEMA), "unsupported capture schema") + require(record["scope"] == ("storage_maintenance_suite" if schema == SCHEMA else "narrow_external_contract"), "capture scope cannot claim qualification") + require(record["qualification"] == {"aggregate": "pending", "windows": "pending", "linux": "pending"}, + "narrow capture cannot promote qualification gates") + for name, digest in record["artifacts"].items(): + path = Path(name) + require(not path.is_absolute() and ".." not in path.parts, "capture path escapes archive") + require(not (output / path).is_symlink() and (output / path).resolve().is_relative_to(output.resolve()), + "capture artifact escapes archive") + require(sha256(output / path) == digest, "artifact changed: " + name) + required = {"source-before.json", "source-after-build.json", "source-after-execution.json", + "binary-before.json", "binary-after.json", "binary-list.stdout", "binary-list.stderr", + "cargo-metadata.stdout", "cargo-metadata.stderr", "execution.stdout", "execution.stderr", + "junit.xml", "nextest.toml", "capture-driver.py", "rustc.stdout", "rustc.stderr", "cargo.stdout", "cargo.stderr"} + if schema >= 2: + required.update({"root-build.stdout", "root-build.stderr", "wrapper-build.stdout", "wrapper-build.stderr", "root-wrapper.rs", "root-wrapper.exe", "compile-proof.json", "build-environment.json", "root-rustc.stdout", "root-rustc.stderr"}) + if schema == SCHEMA: + required.update({"inventory.stdout", "inventory.stderr"}) + require(set(record["artifacts"]) == required, "incomplete capture artifact set") + sources = [read_json(output / name) for name in ( + "source-before.json", "source-after-build.json", "source-after-execution.json")] + require(sources[0] and sources[0] == sources[1] == sources[2], "source changed during capture") + require({"Cargo.toml", "Cargo.lock", "build.rs"} <= sources[0].keys(), "missing core build inputs") + before, after = [read_json(output / name) for name in ("binary-before.json", "binary-after.json")] + metadata = read_json(output / "binary-list.stdout") + require(before and before == after and set(before) == set(binary_paths(metadata)), "binary identity mismatch") + require(metadata["rust-build-meta"]["base-output-directories"] == ["debug"], "capture did not use debug build") + commands = record["commands"] + expected_commands = {"rustc", "cargo", "cargo-metadata", "binary-list", "execution"} + if schema >= 2: + expected_commands.update({"wrapper-build", "root-build", "root-rustc"}) + if schema == SCHEMA: + expected_commands.add("inventory") + require(set(commands) == expected_commands, "missing command provenance") + require(all(command["exit_code"] == 0 for command in commands.values()), "captured command failed") + for label, argv in [("rustc", ["rustc", "-vV"]), ("cargo", ["cargo", "-V"]), + ("cargo-metadata", ["cargo", "metadata", "--offline", "--locked", "--format-version", "1"])]: + require(commands[label]["argv"] == argv, "unexpected " + label + " command") + require(commands["binary-list"]["argv"] == ["cargo", "nextest", "list", "--offline", "--locked", "--test", "storage_maintenance", "--list-type", "binaries-only", "--message-format", "json"], "unexpected compile command") + require(commands["execution"]["argv"] == execution_command(Path(record["output"])), "unexpected execution command") + if schema >= 2: + validate_compile_proof(record, output) + if schema == SCHEMA: + require(commands["inventory"]["argv"] == inventory_command(Path(record["output"])), "unexpected inventory command") + names = inventory_cases(read_json(output / "inventory.stdout"), metadata, sources[0]) + require(record["expected_case_names"] == sorted(names), "recorded inventory identity mismatch") + else: + names = EXPECTED + result = junit_result(output / "junit.xml", names) + require(result["run_id"] and result == record["result"], "JUnit result/run identity mismatch") + require(record["limitations"] == (LIMITATIONS if schema == SCHEMA else LEGACY_LIMITATIONS), "qualification limitations changed") + return record + + +def execution_command(output): + return ["cargo", "nextest", "run", "--binaries-metadata", str(output / "binary-list.stdout"), + "--cargo-metadata", str(output / "cargo-metadata.stdout"), "--config-file", str(output / "nextest.toml"), + "--profile", "cleanup-s5-capture", "--test-threads", "1"] + + + +def inventory_command(output): + return ["cargo", "nextest", "list", "--binaries-metadata", str(output / "binary-list.stdout"), + "--cargo-metadata", str(output / "cargo-metadata.stdout"), "--config-file", str(output / "nextest.toml"), + "--profile", "cleanup-s5-capture", "--message-format", "json"] + + +def inventory_cases(inventory, binaries, sources): + require(inventory["rust-build-meta"] == binaries["rust-build-meta"], "inventory build identity mismatch") + suites = inventory["rust-suites"] + require(set(suites) == {BINARY}, "inventory selected unexpected test suites") + suite = suites[BINARY] + binary = binaries["rust-binaries"][BINARY] + require(suite["binary-path"] == binary["binary-path"] and suite["package-id"] == binary["package-id"], "inventory binary/package identity mismatch") + cases = suite["testcases"] + require(inventory["test-count"] == len(cases), "inventory count does not match named cases") + require(all(case["ignored"] is False and case["filter-match"]["status"] == "matches" for case in cases.values()), "ignored or filtered required test in inventory") + required = set(EXPECTED) + if "tests/support/storage_maintenance/workload.rs" in sources: + required.update(WORKLOAD_CASES) + if "tests/support/storage_maintenance/process.rs" in sources: + required.add(PROCESS_CASE) + require(required <= cases.keys(), "required fixture test cases were not registered") + return set(cases) + + +def capture(repository, output): + repository, output = Path(repository).resolve(), Path(output).resolve() + require(not output.is_relative_to(repository), "capture output must be outside the repository") + output.mkdir(parents=False, exist_ok=False) # Never overwrite a historical execution. + (output / "capture-driver.py").write_bytes(Path(__file__).read_bytes()) + head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository).decode().strip() + write_json(output / "source-before.json", source_manifest(repository)) + # An absolute path avoids shared target directories overwriting other JUnit runs. + (output / "nextest.toml").write_text('[profile.cleanup-s5-capture]\nfail-fast = false\nretries = 0\n' + '[profile.cleanup-s5-capture.junit]\npath = ' + json.dumps(str(output / "junit.xml")) + '\n') + commands = {} + environment = build_environment(output) + write_json(output / "build-environment.json", {name: environment[name] for name in BUILD_ENV}) + (output / "root-wrapper.rs").write_text(WRAPPER_SOURCE) + (output / "rustc-events").mkdir() + commands["wrapper-build"] = run(["rustc", "--edition=2021", str(output / "root-wrapper.rs"), "-o", str(output / "root-wrapper.exe")], repository, output, "wrapper-build", environment) + require(commands["wrapper-build"]["exit_code"] == 0, "wrapper compilation failed") + for label, argv in [ + ("rustc", ["rustc", "-vV"]), ("cargo", ["cargo", "-V"]), + ("cargo-metadata", ["cargo", "metadata", "--offline", "--locked", "--format-version", "1"]), + ]: + commands[label] = run(argv, repository, output, label, environment) + require(commands[label]["exit_code"] == 0, label + " failed; raw output retained") + commands["root-build"] = run(root_build_command(), repository, output, "root-build", environment) + require(commands["root-build"]["exit_code"] == 0, "root build failed; raw output retained") + for label, argv in [ + ("binary-list", ["cargo", "nextest", "list", "--offline", "--locked", "--test", "storage_maintenance", "--list-type", "binaries-only", "--message-format", "json"]), + ]: + commands[label] = run(argv, repository, output, label, environment) + require(commands[label]["exit_code"] == 0, label + " failed; raw output retained") + write_json(output / "source-after-build.json", source_manifest(repository)) + require(read_json(output / "source-before.json") == read_json(output / "source-after-build.json"), "source changed during build") + paths = binary_paths(read_json(output / "binary-list.stdout")) + proof = compile_proof(repository, output) + write_json(output / "compile-proof.json", proof) + compilers = {event["compiler"] for event in proof["events"] + if event["manifest"] == str(repository) and "--crate-name" in event["args"]} + require(len(compilers) == 1, "root package used ambiguous compilers") + commands["root-rustc"] = run([next(iter(compilers)), "-vV"], repository, output, "root-rustc", environment) + require(commands["root-rustc"]["exit_code"] == 0, "actual root rustc identity failed") + write_json(output / "binary-before.json", {path: sha256(path) for path in paths}) + commands["inventory"] = run(inventory_command(output), repository, output, "inventory", environment) + require(commands["inventory"]["exit_code"] == 0, "test inventory failed; raw output retained") + names = inventory_cases(read_json(output / "inventory.stdout"), read_json(output / "binary-list.stdout"), read_json(output / "source-before.json")) + commands["execution"] = run(execution_command(output), repository, output, "execution", environment) + write_json(output / "binary-after.json", {path: sha256(path) for path in paths}) + write_json(output / "source-after-execution.json", source_manifest(repository)) + require(commands["execution"]["exit_code"] == 0, "execution failed; raw output retained") + record = { + "schema": SCHEMA, "scope": "storage_maintenance_suite", "head": head, + "repository": str(repository), "output": str(output), "host": platform.platform(), + "qualification": {"aggregate": "pending", "windows": "pending", "linux": "pending"}, + "commands": commands, "compile_proof": proof, "expected_case_names": sorted(names), "result": junit_result(output / "junit.xml", names), "limitations": LIMITATIONS, + "artifacts": {path.name: sha256(path) for path in sorted(output.iterdir()) if path.is_file()}, + } + write_json(output / "capture.json", record) + verify(output) + return record + + +# A unique workspace-wrapper path is part of Cargo's crate hash. Each capture +# therefore recompiles workspace code, retaining non-workspace dependency caches. +# The wrapper records actual compiler execution and applies assertions LAST, +# after flags supplied by Cargo configuration or profiles. +WRAPPER_SOURCE = r''' +use std::{env, ffi::OsString, fs, process::{Command, exit}}; +fn hex(value: &str) -> String { + value.as_bytes().iter().map(|b| format!("{b:02x}")).collect() +} +fn main() { + let mut args: Vec = env::args_os().skip(1).collect(); + let compiler = args.remove(0); + if args.iter().any(|arg| arg == "--crate-name") { + args.push("-C".into()); + args.push("debug-assertions=on".into()); + } + let mut evidence = format!("compiler={}\nmanifest={}\ncwd={}\n", + hex(compiler.to_str().expect("non-UTF8 compiler path")), + hex(&env::var("CARGO_MANIFEST_DIR").unwrap_or_default()), + hex(env::current_dir().unwrap().to_str().expect("non-UTF8 cwd"))); + for arg in &args { evidence.push_str(&format!("arg={}\n", hex(arg.to_str().expect("non-UTF8 compiler arg")))); } + let status = Command::new(&compiler).args(&args).status().expect("launch rustc"); + let code = status.code().unwrap_or(1); + evidence.push_str(&format!("exit={code}\n")); + let dir = env::var_os("CGRAPH_QUALIFICATION_EVENTS").expect("event directory"); + fs::write(std::path::PathBuf::from(dir).join(format!("{}.txt", std::process::id())), evidence).unwrap(); + exit(code); +} +''' +BUILD_ENV = ("RUSTC_WORKSPACE_WRAPPER", "CGRAPH_QUALIFICATION_EVENTS", + "CARGO_PROFILE_TEST_DEBUG_ASSERTIONS", "CARGO_PROFILE_DEV_DEBUG_ASSERTIONS") + + +def build_environment(output): + environment = os.environ.copy() + for name in ("RUSTC_WRAPPER", "RUSTC_WORKSPACE_WRAPPER", "RUSTFLAGS", "CARGO_ENCODED_RUSTFLAGS", "RUSTC"): + require(not environment.get(name), "unsupported ambient compiler override: " + name) + environment.update({ + "RUSTC_WORKSPACE_WRAPPER": str(output / "root-wrapper.exe"), + "CGRAPH_QUALIFICATION_EVENTS": str(output / "rustc-events"), + "CARGO_PROFILE_TEST_DEBUG_ASSERTIONS": "true", + "CARGO_PROFILE_DEV_DEBUG_ASSERTIONS": "true", + }) + return environment + + +def root_build_command(): + return ["cargo", "test", "--offline", "--locked", "--package", "codebase-graph", + "--test", "storage_maintenance", "--no-run", "--message-format=json", "-vv"] + + +def compile_proof(repository, output): + events = [] + for path in sorted((output / "rustc-events").glob("*.txt")): + event = {"args": []} + for line in path.read_text().splitlines(): + key, value = line.split("=", 1) + if key == "exit": + event[key] = int(value) + elif key == "arg": + event["args"].append(bytes.fromhex(value).decode()) + else: + event[key] = bytes.fromhex(value).decode() + events.append(event) + artifacts = [json.loads(line) for line in (output / "root-build.stdout").read_text().splitlines() + if line.startswith("{")] + outputs = {} + for artifact in artifacts: + if artifact.get("reason") != "compiler-artifact" or "custom-build" in artifact["target"]["kind"]: + continue + name = artifact["target"]["name"].replace("-", "_") + for event in events: + args = event["args"] + if event["manifest"] != str(repository) or "--crate-name" not in args or args[args.index("--crate-name") + 1] != name: + continue + actual_sources = [str(Path(event["cwd"]) / arg) if not Path(arg).is_absolute() else arg + for arg in args if arg.endswith(".rs")] + if artifact["target"]["src_path"] not in actual_sources: + continue + for filename in artifact["filenames"]: + emitted = compiler_output(event, artifact, filename) + require(sha256(emitted) == sha256(filename), "Cargo artifact differs from actual compiler output") + outputs[filename] = {"compiler_output": str(emitted), "sha256": sha256(emitted)} + return {"events": events, "cargo_messages": artifacts, "outputs": outputs} + + +def compiler_output(event, artifact, filename): + args = event["args"] + out_dir = Path(args[args.index("--out-dir") + 1]) + path = Path(filename) + if path.parent == out_dir: + return path + # Cargo copies normal bin targets from deps to the profile directory. + require("bin" in artifact["target"]["kind"] and path.parent == out_dir.parent, + "root output path mismatch") + extra = [arg.split("=", 1)[1] for arg in args if arg.startswith("extra-filename=")] + require(len(extra) == 1, "missing compiler output suffix") + return out_dir / (artifact["target"]["name"].replace("-", "_") + extra[0] + (".exe" if path.suffix == ".exe" else "")) + + +def validate_compile_proof(record, output): + repository = Path(record["repository"]) + original_output = Path(record["output"]) + require(record["commands"]["root-build"]["argv"] == root_build_command(), "unexpected root build command") + require(record["commands"]["wrapper-build"]["argv"] == ["rustc", "--edition=2021", str(original_output / "root-wrapper.rs"), "-o", str(original_output / "root-wrapper.exe")], "unexpected wrapper build command") + environment = read_json(output / "build-environment.json") + require(environment == { + "RUSTC_WORKSPACE_WRAPPER": str(original_output / "root-wrapper.exe"), + "CGRAPH_QUALIFICATION_EVENTS": str(original_output / "rustc-events"), + "CARGO_PROFILE_TEST_DEBUG_ASSERTIONS": "true", "CARGO_PROFILE_DEV_DEBUG_ASSERTIONS": "true", + }, "unsupported build environment") + require((output / "root-wrapper.rs").read_text() == WRAPPER_SOURCE, "compiler wrapper changed") + metadata = read_json(output / "cargo-metadata.stdout") + require(metadata["workspace_root"] == str(repository), "wrong Cargo workspace") + packages = [p for p in metadata["packages"] if p["manifest_path"] == str(repository / "Cargo.toml")] + require(len(packages) == 1 and packages[0]["name"] == "codebase-graph", "wrong root Cargo package") + package = packages[0] + require(package["id"] in metadata["workspace_members"], "root package is not a workspace member") + proof = read_json(output / "compile-proof.json") + require(proof == record["compile_proof"], "compile proof mismatch") + raw_messages = [json.loads(line) for line in (output / "root-build.stdout").read_text().splitlines() if line.startswith("{")] + require(proof["cargo_messages"] == raw_messages, "Cargo compile messages mismatch") + artifacts = [message for message in raw_messages if message.get("reason") == "compiler-artifact" + and message["package_id"] == package["id"] and "custom-build" not in message["target"]["kind"]] + require(artifacts, "missing root compile artifacts") + selected = read_json(output / "binary-list.stdout") + require(all(row["package-id"] == package["id"] for row in selected["rust-binaries"].values()), "nextest selected wrong Cargo package") + require(set(selected["rust-build-meta"].get("non-test-binaries", {})) <= {package["id"]}, "wrong helper package") + compilers = {event["compiler"] for event in proof["events"] + if event["manifest"] == str(repository) and "--crate-name" in event["args"]} + require(len(compilers) == 1 and record["commands"]["root-rustc"]["argv"] == [next(iter(compilers)), "-vV"], "actual root compiler identity mismatch") + compiled_paths = set() + saw_library = False + sources = read_json(output / "source-before.json") + for artifact in artifacts: + require(artifact["fresh"] is False, "root artifact reused from cache") + require(artifact["profile"]["debug_assertions"] is True, "root profile disables debug assertions") + source = Path(artifact["target"]["src_path"]) + require(source.is_relative_to(repository) and str(source.relative_to(repository)) in sources, "unrecorded root source") + name = artifact["target"]["name"].replace("-", "_") + matching = [] + for event in proof["events"]: + args = event["args"] + if event["manifest"] != str(repository) or event["exit"] != 0 or "--crate-name" not in args: + continue + if args[args.index("--crate-name") + 1] != name: + continue + actual_sources = [str(Path(event["cwd"]) / arg) if not Path(arg).is_absolute() else arg + for arg in args if arg.endswith(".rs")] + if str(source) in actual_sources: + matching.append(event) + require(matching, "no actual rustc invocation for root artifact") + for event in matching: + require(event["args"][-2:] == ["-C", "debug-assertions=on"], "actual root compiler assertions are not enabled") + for path in artifact["filenames"]: + emitted = compiler_output(event, artifact, path) + require(proof["outputs"][path]["compiler_output"] == str(emitted), "compiler output identity mismatch") + if path in read_json(output / "binary-before.json"): + require(proof["outputs"][path]["sha256"] == read_json(output / "binary-before.json")[path], "selected binary differs from compiler output") + compiled_paths.update(artifact["filenames"]) + saw_library |= "lib" in artifact["target"]["kind"] + require(saw_library, "root library was not freshly compiled") + require(set(binary_paths(selected)) <= compiled_paths, "selected binary/helper did not come from root compilation") + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="action", required=True) + command = commands.add_parser("capture") + command.add_argument("--repository", required=True) + command.add_argument("--output", required=True) + command = commands.add_parser("verify") + command.add_argument("output") + args = parser.parse_args() + record = capture(args.repository, args.output) if args.action == "capture" else verify(args.output) + if record["schema"] < SCHEMA: + print("Historical %d-case capture integrity verified for its recorded scope; no new suite qualification." % record["result"]["passed"]) + else: + print("Verified all %d registered storage_maintenance cases; aggregate/platform gates remain pending." % record["result"]["passed"]) + + +if __name__ == "__main__": + main() diff --git a/docs/testing/graph-disk-cleanup-evidence/review-outcomes.json b/docs/testing/graph-disk-cleanup-evidence/review-outcomes.json new file mode 100644 index 00000000..fc5a7ded --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/review-outcomes.json @@ -0,0 +1,87 @@ +{ + "s3_expanded_partial": { + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "status": "review findings awaiting fixes and verification; partial foundation is not accepted integrated behavior", + "provenance": "Actual independent fresh-session Max review report archived with exact source snapshot; see artifacts.", + "executed_failure_artifacts": [ + { + "path": "s3-expanded-review-expanded-report.json", + "sha256": "65d9f8a5c1124a215f66e63f5819f60aef52382559cb627d807ce7a7d5bab718" + }, + { + "path": "s3-expanded-reviewer-probe-notes.txt", + "sha256": "ffd58ad5367a31890d30ae14515523e74e76c810742d011f028c46a4729ae6ed" + }, + { + "path": "s3-expanded-zero-budget-probe-output.txt.gz", + "sha256": "9deb649f914ef65bc0ad3dc7632409c2b4d4615091c8ee74781b4e390abf806a", + "encoding": "gzip", + "decoded_sha256": "c84d342763d48ca6fee1d2d644bb05cf49b4cd4b2f9f9cc26c21a9e4e2f2fc6c", + "original_source_path": "/private/tmp/codebasegraph-s3-expanded-review-axcpl_ww/zero-budget-probe-output.txt", + "historical_repository_path": "docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt", + "historical_commit": "9eaa8dedf4f626ebf0974f327f81d0a3c0315e57", + "archive_method": "gzip.GzipFile(filename=\"\",mtime=0); exact originalbytes, no trimming or newline normalization" + } + ], + "reviewer": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max", + "fresh_independent_session": true, + "role": "continued original S3 reviewer, no implementation" + }, + "findings": [ + { + "id": "S3-EXP-R1", + "priority": "P1", + "title": "Authority-required GC error becomes a post-publication build failure" + }, + { + "id": "S3-EXP-R2", + "priority": "P2", + "title": "A torn progress marker blocks replay permanently" + }, + { + "id": "S3-EXP-R3", + "priority": "P2", + "title": "The new validation memory limit does not bound retained decoded state" + }, + { + "id": "S3-EXP-R4", + "priority": "P3", + "title": "Marker housekeeping bypasses the work allowance" + } + ], + "executions": { + "observation_probes": { + "passed": 4, + "meaning": "Successful probes reproduce defects; they do not establish acceptance." + }, + "existing_artifact_gc_regression": { + "failed": 1, + "test": "api::materialization::tests::artifact_gc_retains_active_manifest_keys_and_removes_stale_keys", + "provenance": "Reviewer actual execution recorded in archived review report; no separate raw failed-test log supplied." + } + } + }, + "s4_scheduler_primitive": { + "status": "Coordinator reports primitive review accepted; full S4 pending", + "reviewer_settings": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max" + }, + "actual_junit": { + "path": "s4-independent-source-junit.xml", + "sha256": "8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414", + "cases": 10, + "scope": "10 existing source tests; these are executions already represented in primitive coverage, not10new independent scenarios" + }, + "boundary_harness": { + "source": "/private/tmp/codebasegraph-s4-independent-review-kffyyuw2/scheduler_boundary_probes.rs", + "source_sha256": "bd5af237e0569c2ca0af546e0d444db7a13c9df7390d130562c781389db555e9", + "coordinator_reported_passes": 17, + "composition": "10 existing source tests plus7new boundary probes", + "actual_junit_for_boundary_harness": null, + "qualification": "17 is not17 independent/new cases; outcome reported by coordinator, no boundary execution log archived here." + } + } +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml b/docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml new file mode 100644 index 00000000..a9450cef --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml b/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml new file mode 100644 index 00000000..2e689e70 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json b/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json new file mode 100644 index 00000000..3607bfb4 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json @@ -0,0 +1,27 @@ +{ + "baseline": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "source_identity": "0e2bde1fe2c37b664aa864175c0b95a1f9297f6f0a64094295aed98c44af2dc3", + "owned_files": { + "src/db_writer/phase.rs": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/execution/run.rs": "df6b6f259b1c542c340f7c0850befcb615d7b89c944b9404dbc0ce5f10e95ae5", + "src/storage/containment.rs": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "src/storage/direct.rs": "f1b87f90d597b8f2e7d7d2cd03b5dddb12262894f7721c60e921b0267e923c2c", + "src/storage/inventory.rs": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958", + "src/storage/layout.rs": "be57d463c715346af69497f3b7820497e8c0f195301824e4bde61518c6da762d", + "src/storage/locks.rs": "9de0e4a308233514d3ee56f4ce930005fc475654079108fd13a30459c7c2dacc", + "src/storage/maintenance_ownership.rs": "a3ab4989eb5432d099a2aa7657796ffc6f0b63c7615dccdd9a535bf5c149666a", + "src/storage/managed.rs": "7c6882f7d87403c94a5c2160012ac93efb609cc7ec5f6c6fd6b2b5da4db188e9" + }, + "excluded_handoff": [ + "src/storage/mod.rs" + ], + "context_only_storage_mod_hash": "1549d70674632db6b7233f23f0789055e162c224cf1625f8f15cd8b208bb3eb6", + "patch_sha256": "4c0580a3d9ca7bd104713e3d4a72b3a956f4ff5acfb7212c3f11abf723768a21", + "review_delta_sha256": "0f02f568ef15fa0823a0ce067ff9127c93e68d33b2f410b3e8adb683f28baa56", + "junit": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/target/nextest/cleanup-s2/junit.xml", + "junit_archive": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/s2-family-focused-junit.xml", + "junit_sha256": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876", + "focused_tests_passed": 128, + "prior_review_snapshot": "559f93b0526f8b2e9e7ec4eb57d473d3b04a8ffc385cdf68779e25ac4d14d2ca" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml b/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml new file mode 100644 index 00000000..285707d3 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json b/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json new file mode 100644 index 00000000..3607bfb4 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json @@ -0,0 +1,27 @@ +{ + "baseline": "bb3c716e899c7141425ba8b80f1238cf212ffc7e", + "source_identity": "0e2bde1fe2c37b664aa864175c0b95a1f9297f6f0a64094295aed98c44af2dc3", + "owned_files": { + "src/db_writer/phase.rs": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/execution/run.rs": "df6b6f259b1c542c340f7c0850befcb615d7b89c944b9404dbc0ce5f10e95ae5", + "src/storage/containment.rs": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "src/storage/direct.rs": "f1b87f90d597b8f2e7d7d2cd03b5dddb12262894f7721c60e921b0267e923c2c", + "src/storage/inventory.rs": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958", + "src/storage/layout.rs": "be57d463c715346af69497f3b7820497e8c0f195301824e4bde61518c6da762d", + "src/storage/locks.rs": "9de0e4a308233514d3ee56f4ce930005fc475654079108fd13a30459c7c2dacc", + "src/storage/maintenance_ownership.rs": "a3ab4989eb5432d099a2aa7657796ffc6f0b63c7615dccdd9a535bf5c149666a", + "src/storage/managed.rs": "7c6882f7d87403c94a5c2160012ac93efb609cc7ec5f6c6fd6b2b5da4db188e9" + }, + "excluded_handoff": [ + "src/storage/mod.rs" + ], + "context_only_storage_mod_hash": "1549d70674632db6b7233f23f0789055e162c224cf1625f8f15cd8b208bb3eb6", + "patch_sha256": "4c0580a3d9ca7bd104713e3d4a72b3a956f4ff5acfb7212c3f11abf723768a21", + "review_delta_sha256": "0f02f568ef15fa0823a0ce067ff9127c93e68d33b2f410b3e8adb683f28baa56", + "junit": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/target/nextest/cleanup-s2/junit.xml", + "junit_archive": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/s2-family-focused-junit.xml", + "junit_sha256": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876", + "focused_tests_passed": 128, + "prior_review_snapshot": "559f93b0526f8b2e9e7ec4eb57d473d3b04a8ffc385cdf68779e25ac4d14d2ca" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml new file mode 100644 index 00000000..1a2171d2 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json new file mode 100644 index 00000000..28e9fd6d --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json @@ -0,0 +1,106 @@ +{ + "scope": "DC-S3 expanded frozen partial: journal fixes, gated parser/cursor/job foundations. Canonical GC and aggregate DC-S3 closure remain pending.", + "reviewer": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max", + "fresh_independent_session": true, + "role": "continued original S3 reviewer, no implementation" + }, + "head": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "source_sha256": { + "src/storage/maintenance.rs": "2548f04eb3a911d43910eac63fc3eb5651cab17a63d648aebc5a6d71f45e3b1d", + "src/artifact_store.rs": "7e0fbf42a98a3ab64a97624db9cf782f8ead0db4a82abde2f40b4ee26028e7da", + "src/storage/mod.rs": "68db9239927733a14f0853d5b7330ba19c9114fe8c3cac3d56d67ed2cf3ff981", + "src/storage/maintenance_scheduler.rs": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf" + }, + "original_review_resolution": { + "P2 state-lock revalidation": "Resolved in current helper: full revalidate outside state.lock, bounded stamp/record checks inside. Final producer/reference seal and associated final tests still pending S2.", + "foreign child/payload concern": "Addressed at reviewed boundaries with source/payload stamps and durable unlink stage; new crash window in stage publication is separately reported.", + "journal argument concern": "Addressed with exact .maintenance path, id-to-tombstone binding, journal inode/link/content checks and bounded read." + }, + "confirmed_findings": [ + { + "id": "S3-EXP-R1", + "priority": "P1", + "location": "src/artifact_store.rs:319-325", + "title": "Authority-required GC error becomes a post-publication build failure", + "trigger": "A normal source change leaves a formerly referenced valid key outside the new manifest.", + "trace": "delete_key now always returns authority-required for valid keys; existing garbage_collect_artifacts propagates it at materialization.rs:1197-1199; Direct/Managed post-publication callers propagate at 1016-1023 and 1070.", + "evidence": "Existing artifact_gc_retains_active_manifest_keys_and_removes_stale_keys fails in isolated frozen copy with the authority-required error; the call trace establishes post-publication consequence. The old deletion assertion itself is superseded by guarded GC.", + "remedy": "Keep deletion fail-closed. Route these cleanup outcomes to separate pending reporting without failing a completed publication or masking the primary result. Coordinate the S4 caller change before integrated readiness.", + "regression": "Exercise two real source revisions: second publication succeeds, active queries/cache survive, and cleanup pending is reported while the reference seal is unavailable." + }, + { + "id": "S3-EXP-R2", + "priority": "P2", + "location": "src/storage/maintenance.rs:877-880", + "title": "A torn progress marker blocks replay permanently", + "trigger": "Process death/write failure after create_new_file(.unlinked) succeeds but before complete marker content is written, after payload unlink has synced.", + "trace": "Final .unlinked name is visible before contents are complete. Subsequent read_unlinked_stage parses the empty/partial marker at935 and returns an error. Retry never rewrites or atomically replaces it.", + "evidence": "Exact on-disk crash-state probes with empty and one-byte markers, then two retries under freshly acquired leases, consistently return EOF and leave private directory, stage and journal.", + "remedy": "Publish progress atomically from a fully synced temporary, or use another recoverable bounded transition that distinguishes interrupted own writes from foreign state. Preserve unknown files.", + "regression": "Fault each progress create/write/fsync/publish boundary, restart/reacquire, and verify eventual cleanup plus preservation of newly introduced foreign payloads." + }, + { + "id": "S3-EXP-R3", + "priority": "P2", + "location": "src/artifact_store.rs:366-382", + "title": "The new validation memory limit does not bound retained decoded state", + "trigger": "A valid artifact includes a metadata array with many small JSON values.", + "trace": "Admission uses 2*file_bytes+64KiB; serde Value/Vec allocation can exceed that estimate, before row-validation index allocations. New retained maintenance jobs reuse the pre-existing loader heuristic.", + "evidence": { + "valid_file_bytes": 853653, + "admitted_memory_limit_bytes": 1772842, + "single_decoded_metadata_vec_capacity_bytes": 2097152, + "result": "Maintenance validation succeeds, although one field alone exceeds the supplied ceiling." + }, + "remedy": "Use enforced decoding/accounting limits or a justified conservative structural bound, including temporary validation indices and concurrent retained jobs. Do not infer a memory ceiling from encoded bytes alone.", + "regression": "Admit/reject dense small JSON values, large maps, partial parses, and concurrent retained jobs against the actual configured total budget." + }, + { + "id": "S3-EXP-R4", + "priority": "P3", + "location": "src/storage/maintenance.rs:874-887", + "title": "Marker housekeeping bypasses the work allowance", + "trigger": "Replay an empty detached artifact with zero entries or zero duration.", + "trace": "The payload branch and its budget take are skipped. Marker create/write/fsync occurs before the first take at887. remove_retirement_journal also unlinks both stage and journal under one allowance.", + "evidence": "Executed zero-entry and zero-duration probes both create .unlinked. Separate probe confirms one final allowance removes both journal entries.", + "remedy": "Check/reserve allowance before every independent housekeeping mutation and preserve a resumable boundary; define/charge bounded atomic bookkeeping explicitly. Zero allowance should make no filesystem mutation.", + "regression": "Snapshot tree/metadata around max_entries=0 and max_duration=ZERO; verify no mutation. Verify the declared one-entry unlink bound including auxiliary records." + } + ], + "conditional_limits": [ + "Gated reads/writes, hashing and manual row comparisons cooperate, but ordinary serde BTreeMap insertion/comparison and decoded-value disposal do not consult the gate. AdvisoryArtifactJob::Drop synchronously joins; cancellation can therefore wait for those CPU/disposal atoms. No measured 100ms miss is claimed. Constrain largest atoms/resource caps or keep joining/reclamation off the foreground path before enabling the controller.", + "The two-slot cap covers live worker threads, not aggregate completed-result/job memory. Configured budget selection and controller retention policy are not wired; enforce a total resource bound there.", + "Parser job descriptor persistence is not restart-controller implementation. Canonical references, backup restore and final fast-seal authority remain fail-closed/pending, as declared in the frozen manifest.", + "Cursor results remain advisory; retained prefix handles do not independently authorize paths after between-grant mutations. Existing job capture revalidates real path and stamp under fresh authority. No deletion finding was established from the cursor alone." + ], + "checks": { + "supplied_junit": "31/31 actual nextest JUnit inspected; sha256 e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627", + "probe_checkout": "/private/tmp/codebasegraph-s3-expanded-review-axcpl_ww", + "commands": [ + { + "command": "CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo test --offline --locked --lib reviewer_expanded_ -- --nocapture --test-threads=1", + "result": "3/3 observation probes passed" + }, + { + "command": "CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo test --offline --locked --lib api::materialization::tests::artifact_gc_retains_active_manifest_keys_and_removes_stale_keys -- --exact --nocapture --test-threads=1", + "result": "1 failed; authority-required error at materialization.rs:1450" + }, + { + "command": "CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo test --offline --locked --lib reviewer_expanded_zero_budget -- --nocapture --test-threads=1", + "result": "1/1 observation probe passed; actual captured output in zero-budget-probe-output.txt" + } + ], + "toolchain": "rustc 1.96.0 (ac68faa20 2026-05-25)", + "warning": "Separately owned SystemMaintenanceClock unused warning remains in this partial snapshot." + }, + "limitations": [ + "No full graph-query execution during paused parsing; existing test proves state-lock/writer-admission accessibility.", + "No full suite, MSRV 1.82, native Linux or native Windows execution.", + "No production-source, Git, wiki, model or live-graph mutations; only isolated probe-copy test code written.", + "No new memory candidate: partial findings and pending fixes are not a settled reusable outcome." + ], + "recommendation": "Address the confirmed findings before adopting the expanded primitives/integrated runtime. Original P2 is fixed. Full DC-S3 closure remains pending." +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt new file mode 100644 index 00000000..76c5df59 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt @@ -0,0 +1 @@ +Original frozen production source preserved in probe copy; only test functions appended inside existing test modules. Passing reviewer probes assert observed behavior and do not mean contract correctness. diff --git a/docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json new file mode 100644 index 00000000..e2731384 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json @@ -0,0 +1,33 @@ +{ + "scope": "DC-S3 expanded partial: journal review fixes, gated validation/cursor/job foundations; public canonical GC remains deferred", + "head": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "files": { + "src/storage/maintenance.rs": "2548f04eb3a911d43910eac63fc3eb5651cab17a63d648aebc5a6d71f45e3b1d", + "src/artifact_store.rs": "7e0fbf42a98a3ab64a97624db9cf782f8ead0db4a82abde2f40b4ee26028e7da", + "src/storage/mod.rs": "68db9239927733a14f0853d5b7330ba19c9114fe8c3cac3d56d67ed2cf3ff981" + }, + "snapshot_sha256": "ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8", + "compile_dependencies": { + "src/storage/maintenance_scheduler.rs": "480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf" + }, + "nextest_run_id": "fc78ee6c-cec6-42bb-9bda-544e10913b47", + "profile": "cleanup-s3", + "tests": { + "passed": 31, + "failed": 0 + }, + "command": "CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo nextest run --offline --locked --lib --config-file /private/tmp/codebasegraph-disk-cleanup-7t9af0a6/nextest.toml --profile cleanup-s3 -E 'test(storage::maintenance::) | test(artifact_store::)' --test-threads 1", + "config_sha256": "3afd345f1beb9e57214beaa23413621c58308c1d6f80bbb993b834f48f6880d2", + "junit": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/s3-expanded-junit.xml", + "junit_sha256": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627", + "limitations": [ + "Canonical key collection/backup restore pending producer-wide reference seal; APIs return reference_seal_unavailable", + "Fast final ownership/epoch seal and paused full-validation/changed-seal tests pending S2 API", + "No database/generation/run/legacy-staging/Direct retirement", + "Job descriptor persisted but restart controller/reconciliation not yet wired", + "Resource ceiling supplied by internal caller; configured value selection pending", + "Individual serde BTreeMap insertion and cancellation/error disposal CPU atoms require reviewed resource/overshoot treatment", + "Paused parser test proves state-lock and writer admission accessibility, not native graph query execution", + "One S4 SystemMaintenanceClock unused warning; production integration pending" + ] +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt.gz b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt.gz new file mode 100644 index 00000000..a8d73091 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt.gz differ diff --git a/docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml b/docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml new file mode 100644 index 00000000..7ec18c12 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml b/docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml new file mode 100644 index 00000000..9b0efc5d --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml b/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml new file mode 100644 index 00000000..357414bc --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json b/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json new file mode 100644 index 00000000..c96e8569 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json @@ -0,0 +1,215 @@ +{ + "scope": "Fresh fullycaptured S5 narrow external health/requesttestexecution; no collectorqualification", + "captured_utc": "2026-10-20T17:54:43.836582+00:00", + "head": "44e467fa9ea1d0a9604a05a41cd34e763bb1c131", + "source_sha256": { + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "c2ea75969a673712e4206c3be5576929608e9820cd19a39b2b3f202add34a05e", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "51230173cd6ea2767f1ec95306f3d3d2c42a8b07b20e56ec3c7661cbb4b96775", + "src/api/graph_read.rs": "0f0f6da64105186d884dab60dfb5e34e3b948a41ad6c8ca494b4904b4b9a1ab4", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "77b89a73eebd5dc24f75291e5615db0028f3ad15f7abfe80ac162d194f43520f", + "src/api/mod.rs": "3e2b72f33f55a3670764ecde9d90c844b6cb8d5d59adee131aaef2dd79dd769b", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52", + "src/api/refresh.rs": "1cebe4ab394d446e4c2b99c34447182b8b2ac4135612040ffda200eefb3ac595", + "src/artifact_store.rs": "5b3b69365285d073a13937d88391a848cd742754bbc759aa9db966f3d7136de2", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator/tests.rs": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252", + "src/coordinator.rs": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "c783ba112f44ce247be532c475b5e39a1bdc8b19db0f1e7d27f627fcf93d3c6b", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "32510a0dccc98be71174eb0dcd5a4e72c1164d42f1a25d77d85c8bfcc534f5fd", + "src/db_writer/phase.rs": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "10fcfc158e6fa8d86ab07cbb2ccec13464488b7ddced929f6b011c0e614f6ec7", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "df6b6f259b1c542c340f7c0850befcb615d7b89c944b9404dbc0ce5f10e95ae5", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e", + "src/materialization_worker.rs": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663", + "src/process_lifecycle/unix.rs": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d", + "src/process_lifecycle/windows.rs": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "0fe724e549ca6276d205be724f8dd41ab746397c81e4043aae64d1e41baf6202", + "src/scan.rs": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da", + "src/staging_writer/files.rs": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "src/storage/direct.rs": "b751c06e1383c2574ad24a72e1919ff82a5c3fc79b28879727a2a79339f5fcbe", + "src/storage/inventory.rs": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "c6296037d04a0120cccdc405052046355d721fc2255929c7bace0e4d1a2def2b", + "src/storage/maintenance.rs": "b237227071c56e84b4f7a281c1c6fbcb6003b3954a2cd4d4f9c0ecda114b5fa8", + "src/storage/maintenance_ownership.rs": "568f69872470e91efaafb91af83b323493dd5b70e136d1ee4fa2ed2e098f7362", + "src/storage/maintenance_scheduler.rs": "fcec504f2f55a227b441d5e103ce18e4caa69c4fd9610070a9f0eaec45e9a90f", + "src/storage/managed.rs": "83364bf78763af639f1866eef61efddc7fbbe58b671296045f6744cc19c3f295", + "src/storage/mod.rs": "68db9239927733a14f0853d5b7330ba19c9114fe8c3cac3d56d67ed2cf3ff981", + "src/storage/run_workspace.rs": "03be7a368aa28a2a14d079fc7401c4d87862d10843d4fe41795f6ccba186ca40", + "src/storage/test_harness.rs": "5fcf6cc2e6f9b79dc87b1f8affe70981f870d8df12c92500bff407723710feb6", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "Cargo.toml": "f267cf8583c7eb20a70d381b461ad0e20ecf52d7e1432ba204ae3664add86a1f", + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "build.rs": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + ".cargo/config.toml": "388ad5407d7e78cae8dacd1c3c556f8c97f43d998c4e09273dcf8fca00eb0f82", + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09" + }, + "binary_sha256": { + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_maintenance-926da32abefb62ce": "eeac63536eaf120f28533f201720cab4ee7769755d6eaafaa81ba3c069092a76" + }, + "source_before_after_identical": true, + "binary_before_after_identical": true, + "junit": "s5-external-health-fixverify-junit.xml", + "junit_sha256": "3b8c4e663c2c93d3c52f93da6af4b17096b529deff117ce4f9b191ad766e44b7", + "run_id": "5f3b8e10-9d6f-414b-a4ec-6a910655d899", + "actual_result": { + "passed": 2, + "failed": 0, + "skipped": 0 + }, + "expected_test_cases": [ + { + "name": "health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown", + "classname": "codebase-graph::storage_maintenance" + }, + { + "name": "nonboolean_quiescence_confirmation_is_rejected_before_repository_effects", + "classname": "codebase-graph::storage_maintenance" + } + ], + "compile_command": "cargo nextest list --offline --locked --test storage_maintenance --list-type binaries-only --message-format json", + "execution_command": [ + "cargo", + "nextest", + "run", + "--binaries-metadata", + "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json", + "--cargo-metadata", + "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.json", + "--config-file", + "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/nextest.toml", + "--profile", + "cleanup-s5", + "--test-threads", + "1" + ], + "environment": { + "rustc": "rustc 1.96.0 (ac68faa20 2026-05-25)\nbinary: rustc\ncommit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96\ncommit-date: 2026-05-25\nhost: aarch64-apple-darwin\nrelease: 1.96.0\nLLVM version: 22.1.2\n", + "cargo": "cargo 1.96.0 (30a34c682 2026-05-25)\n" + }, + "capture_files_sha256": { + "s5-fresh-capture/binary-after.json": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "s5-fresh-capture/binary-before.json": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "s5-fresh-capture/binary-list.json": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "s5-fresh-capture/binary-list.stderr": "38b1121bacbb008a468e084a6453f3b81fb7972fc81e6670176e212381b8aa3e", + "s5-fresh-capture/binary-list.stdout": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "s5-fresh-capture/cargo-metadata.json": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3", + "s5-fresh-capture/cargo-metadata.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "s5-fresh-capture/cargo-metadata.stdout": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3", + "s5-fresh-capture/execution.stderr": "df966e27aff804782c1143e8aa9325367cfb66ead53b60fdb86348e6175bea1a", + "s5-fresh-capture/execution.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "s5-fresh-capture/source-after-build.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "s5-fresh-capture/source-after-execution.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "s5-fresh-capture/source-before.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe" + }, + "limitations": [ + "Narrow2testexecution only; collector/churn/fault/fullsuite/nativeplatform/MSRV gates pending.", + "Production dead-code warnings from unfinished integration remain; exact compilationstderr archived.", + "Originalhistoricalrun retained separately; thiscapture supersedes its incomplete sourceprovenance for narrowtestverification." + ] +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml b/docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml new file mode 100644 index 00000000..c66c7d3a --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json b/docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json new file mode 100644 index 00000000..e85cf1f9 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json @@ -0,0 +1,50 @@ +{ + "scope": "S5 narrow external production health/request contract only; collector,convergence and platform gates remainpending", + "head": "c0741ce137e09dac309ac72f5a2c2243a92ef070", + "source_sha256": { + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09", + "Cargo.toml": "f267cf8583c7eb20a70d381b461ad0e20ecf52d7e1432ba204ae3664add86a1f", + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462", + "src/api/facade.rs": "51230173cd6ea2767f1ec95306f3d3d2c42a8b07b20e56ec3c7661cbb4b96775", + "src/protocol.rs": "0fe724e549ca6276d205be724f8dd41ab746397c81e4043aae64d1e41baf6202", + "src/coordinator.rs": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "src/storage/maintenance.rs": "b237227071c56e84b4f7a281c1c6fbcb6003b3954a2cd4d4f9c0ecda114b5fa8", + "src/storage/maintenance_scheduler.rs": "fcec504f2f55a227b441d5e103ce18e4caa69c4fd9610070a9f0eaec45e9a90f", + "src/artifact_store.rs": "7e0fbf42a98a3ab64a97624db9cf782f8ead0db4a82abde2f40b4ee26028e7da" + }, + "junit": "s5-external-health-junit.xml", + "junit_sha256": "8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6", + "run_id": "9c9f4e71-b6aa-425a-8ed1-910eb25df0e2", + "command": "CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo nextest run --offline --locked --test storage_maintenance --config-file /private/tmp/codebasegraph-disk-cleanup-7t9af0a6/nextest.toml --profile cleanup-s5 --test-threads 1", + "config_sha256": "3afd345f1beb9e57214beaa23413621c58308c1d6f80bbb993b834f48f6880d2", + "environment": { + "rustc": "rustc 1.96.0 (ac68faa20 2026-05-25)\nbinary: rustc\ncommit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96\ncommit-date: 2026-05-25\nhost: aarch64-apple-darwin\nrelease: 1.96.0\nLLVM version: 22.1.2\n", + "cargo": "cargo 1.96.0 (30a34c682 2026-05-25)\n" + }, + "actual_result": { + "passed": 2, + "failed": 0, + "skipped": 0 + }, + "limitations": [ + "Library build emitted136 dead-code warnings for unfinished production integration; warning-free completion gate remains pending.", + "No graph materialization or maintenance collector execution.", + "No fullsuite/MSRV/Linux/Windowsqualification.", + "Tests compile/run against current partial working source, not an accepted finalintegrated snapshot.", + "Original selected-source manifest omitted src/api/normalization.rs; this historical report alone does not establish complete relevant source identity. A fresh captured run will supersede it for fix verification." + ], + "expected_test_cases": [ + { + "name": "health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown", + "classname": "codebase-graph::storage_maintenance" + }, + { + "name": "nonboolean_quiescence_confirmation_is_rejected_before_repository_effects", + "classname": "codebase-graph::storage_maintenance" + } + ], + "audit_identity_provenance": "Expected case identities added retrospectively from unchanged archived original actualJUnit during audit fix; no execution or source hash is backdated." +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json new file mode 100644 index 00000000..88e2f8bc --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json @@ -0,0 +1,3 @@ +{ + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_maintenance-926da32abefb62ce": "eeac63536eaf120f28533f201720cab4ee7769755d6eaafaa81ba3c069092a76" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json new file mode 100644 index 00000000..88e2f8bc --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json @@ -0,0 +1,3 @@ +{ + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_maintenance-926da32abefb62ce": "eeac63536eaf120f28533f201720cab4ee7769755d6eaafaa81ba3c069092a76" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json new file mode 100644 index 00000000..37f5c221 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json @@ -0,0 +1 @@ +{"rust-build-meta":{"target-directory":"/Users/rabii/Projects/Repositories/codebaseGraph/target","build-directory":"/Users/rabii/Projects/Repositories/codebaseGraph/target","base-output-directories":["debug"],"non-test-binaries":{"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4":[{"name":"codebase-graph","kind":"bin-exe","path":"debug/codebase-graph"}]},"build-script-out-dirs":{"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4":"debug/build/codebase-graph-85a00d40af0fbc91/out"},"build-script-info":{},"linked-paths":["debug/build/codebase-graph-85a00d40af0fbc91/out","debug/build/cxx-73984724934a7db4/out","debug/build/lbug-3bd878011e7cf72d/out","debug/build/libmimalloc-sys-daecde77e05167a8/out","debug/build/link-cplusplus-133b8a5d45c5d1c8/out","debug/build/tree-sitter-659746aa2cbac4d2/out","debug/build/tree-sitter-c-d742716439a639df/out","debug/build/tree-sitter-cpp-30401f96f9a816ee/out","debug/build/tree-sitter-css-ab5a6aad9cb4769e/out","debug/build/tree-sitter-fortran-d1d9c449ecbe0f71/out","debug/build/tree-sitter-go-e5f0ca018d62ab82/out","debug/build/tree-sitter-html-10983a9819bc81f9/out","debug/build/tree-sitter-javascript-e96d93b32ecff8fd/out","debug/build/tree-sitter-python-b531e90ba31c4199/out","debug/build/tree-sitter-rust-bc00d90a99c05f69/out","debug/build/tree-sitter-typescript-dc071d12a223b642/out"],"platforms":{"host":{"platform":{"triple":"aarch64-apple-darwin","target-features":"unknown"},"libdir":{"status":"available","path":"/Users/rabii/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib"}},"targets":[]},"target-platforms":[{"triple":"aarch64-apple-darwin","target-features":"unknown"}],"target-platform":null},"rust-binaries":{"codebase-graph::storage_maintenance":{"binary-id":"codebase-graph::storage_maintenance","binary-name":"storage_maintenance","package-id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","kind":"test","binary-path":"/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_maintenance-926da32abefb62ce","build-platform":"target"}}} \ No newline at end of file diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr new file mode 100644 index 00000000..6b2de204 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr @@ -0,0 +1,996 @@ + Compiling codebase-graph v3.0.4 (/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo) +warning: struct `ValidatedArtifact` is never constructed + --> src/artifact_store.rs:354:19 + | +354 | pub(crate) struct ValidatedArtifact { + | ^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default + +warning: function `validate_artifact_for_maintenance` is never used + --> src/artifact_store.rs:360:15 + | +360 | pub(crate) fn validate_artifact_for_maintenance( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: struct `MaintenanceRow` is never constructed + --> src/artifact_store.rs:413:8 + | +413 | struct MaintenanceRow<'a> { + | ^^^^^^^^^^^^^^ + +warning: type alias `MaintenanceRows` is never used + --> src/artifact_store.rs:418:6 + | +418 | type MaintenanceRows<'a> = std::collections::BTreeMap<[u8; 32], Vec>>; + | ^^^^^^^^^^^^^^^ + +warning: function `maintenance_string_digest` is never used + --> src/artifact_store.rs:419:4 + | +419 | fn maintenance_string_digest( + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `maintenance_compare` is never used + --> src/artifact_store.rs:430:4 + | +430 | fn maintenance_compare( + | ^^^^^^^^^^^^^^^^^^^ + +warning: function `maintenance_row_mut` is never used + --> src/artifact_store.rs:448:4 + | +448 | fn maintenance_row_mut<'a, 'b>( + | ^^^^^^^^^^^^^^^^^^^ + +warning: function `maintenance_insert_row` is never used + --> src/artifact_store.rs:463:4 + | +463 | fn maintenance_insert_row<'a>( + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `maintenance_validate_index` is never used + --> src/artifact_store.rs:482:4 + | +482 | fn maintenance_validate_index( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `validate_raw_rows_cooperatively` is never used + --> src/artifact_store.rs:533:4 + | +533 | fn validate_raw_rows_cooperatively( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `maintenance_partition_id` is never used + --> src/artifact_store.rs:571:4 + | +571 | fn maintenance_partition_id( + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: struct `MaintenanceExecutionPermit` is never constructed + --> src/coordinator.rs:178:8 + | +178 | struct MaintenanceExecutionPermit { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: method `try_acquire_maintenance` is never used + --> src/coordinator.rs:189:8 + | +188 | impl ExecutionDispatcher { + | ------------------------ method in this implementation +189 | fn try_acquire_maintenance(self: &Arc) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `lifecycle_empty_with_owned` is never used + --> src/storage/containment.rs:178:15 + | +178 | pub(crate) fn lifecycle_empty_with_owned( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: method `writer_lease` is never used + --> src/storage/direct.rs:48:19 + | +47 | impl DirectWriteSession { + | ----------------------- method in this implementation +48 | pub(crate) fn writer_lease(&self) -> Result<&WriterLease, NativeError> { + | ^^^^^^^^^^^^ + +warning: struct `MaintenanceTarget` is never constructed + --> src/storage/inventory.rs:11:19 + | +11 | pub(crate) struct MaintenanceTarget { + | ^^^^^^^^^^^^^^^^^ + +warning: struct `DirectPair` is never constructed + --> src/storage/inventory.rs:20:19 + | +20 | pub(crate) struct DirectPair { + | ^^^^^^^^^^ + +warning: enum `InventoryStatus` is never used + --> src/storage/inventory.rs:26:17 + | +26 | pub(crate) enum InventoryStatus { + | ^^^^^^^^^^^^^^^ + +warning: enum `InventoryCategory` is never used + --> src/storage/inventory.rs:35:17 + | +35 | pub(crate) enum InventoryCategory { + | ^^^^^^^^^^^^^^^^^ + +warning: enum `Disposition` is never used + --> src/storage/inventory.rs:49:17 + | +49 | pub(crate) enum Disposition { + | ^^^^^^^^^^^ + +warning: struct `InventoryEntry` is never constructed + --> src/storage/inventory.rs:56:19 + | +56 | pub(crate) struct InventoryEntry { + | ^^^^^^^^^^^^^^ + +warning: struct `InventoryTotals` is never constructed + --> src/storage/inventory.rs:69:19 + | +69 | pub(crate) struct InventoryTotals { + | ^^^^^^^^^^^^^^^ + +warning: struct `StorageInventory` is never constructed + --> src/storage/inventory.rs:81:19 + | +81 | pub(crate) struct StorageInventory { + | ^^^^^^^^^^^^^^^^ + +warning: struct `Stamp` is never constructed + --> src/storage/inventory.rs:92:8 + | +92 | struct Stamp { + | ^^^^^ + +warning: function `stamp` is never used + --> src/storage/inventory.rs:99:4 + | +99 | fn stamp(m: &Metadata) -> Stamp { + | ^^^^^ + +warning: function `changed` is never used + --> src/storage/inventory.rs:115:4 + | +115 | fn changed(m: &Metadata) -> Option<(i64, i64)> { + | ^^^^^^^ + +warning: function `allocation` is never used + --> src/storage/inventory.rs:133:4 + | +133 | fn allocation(m: &Metadata) -> Option { + | ^^^^^^^^^^ + +warning: function `real_path` is never used + --> src/storage/inventory.rs:143:15 + | +143 | pub(crate) fn real_path(path: &Path) -> bool { + | ^^^^^^^^^ + +warning: function `read_observed` is never used + --> src/storage/inventory.rs:165:4 + | +165 | fn read_observed(path: &Path) -> Option { + | ^^^^^^^^^^^^^ + +warning: function `read_json` is never used + --> src/storage/inventory.rs:184:4 + | +184 | fn read_json(path: &Path) -> Option { + | ^^^^^^^^^ + +warning: struct `ManifestKeyView` is never constructed + --> src/storage/inventory.rs:188:8 + | +188 | struct ManifestKeyView { + | ^^^^^^^^^^^^^^^ + +warning: struct `ArtifactKeyView` is never constructed + --> src/storage/inventory.rs:192:8 + | +192 | struct ArtifactKeyView { + | ^^^^^^^^^^^^^^^ + +warning: function `read_manifest_keys` is never used + --> src/storage/inventory.rs:196:4 + | +196 | fn read_manifest_keys(path: &Path) -> Option> { + | ^^^^^^^^^^^^^^^^^^ + +warning: struct `ObservedBinding` is never constructed + --> src/storage/inventory.rs:210:19 + | +210 | pub(crate) struct ObservedBinding { + | ^^^^^^^^^^^^^^^ + +warning: function `default_direct` is never used + --> src/storage/inventory.rs:215:15 + | +215 | pub(crate) fn default_direct(target: &MaintenanceTarget) -> DirectPair { + | ^^^^^^^^^^^^^^ + +warning: function `physically_within` is never used + --> src/storage/inventory.rs:233:4 + | +233 | fn physically_within(path: &Path, root: &Path) -> bool { + | ^^^^^^^^^^^^^^^^^ + +warning: function `reconcile_observation_roots` is never used + --> src/storage/inventory.rs:237:4 + | +237 | fn reconcile_observation_roots(mut roots: Vec) -> Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `managed_direct_roles_overlap` is never used + --> src/storage/inventory.rs:253:4 + | +253 | fn managed_direct_roles_overlap( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `bound_roles_overlap` is never used + --> src/storage/inventory.rs:265:15 + | +265 | pub(crate) fn bound_roles_overlap(target: &MaintenanceTarget, binding: &ObservedBinding) -> bool { + | ^^^^^^^^^^^^^^^^^^^ + +warning: function `observed_relative` is never used + --> src/storage/inventory.rs:277:4 + | +277 | fn observed_relative(path: &Path, root: &Path) -> Option { + | ^^^^^^^^^^^^^^^^^ + +warning: function `config_raw_path` is never used + --> src/storage/inventory.rs:286:4 + | +286 | fn config_raw_path( + | ^^^^^^^^^^^^^^^ + +warning: function `config_value_path` is never used + --> src/storage/inventory.rs:311:4 + | +311 | fn config_value_path( + | ^^^^^^^^^^^^^^^^^ + +warning: function `observe_binding` is never used + --> src/storage/inventory.rs:331:15 + | +331 | pub(crate) fn observe_binding(target: &MaintenanceTarget) -> Result { + | ^^^^^^^^^^^^^^^ + +warning: function `classify_artifact` is never used + --> src/storage/inventory.rs:422:4 + | +422 | fn classify_artifact( + | ^^^^^^^^^^^^^^^^^ + +warning: struct `Scan` is never constructed + --> src/storage/inventory.rs:465:8 + | +465 | struct Scan { + | ^^^^ + +warning: associated items `new` and `walk` are never used + --> src/storage/inventory.rs:473:8 + | +472 | impl Scan { + | --------- associated items in this implementation +473 | fn new() -> Self { + | ^^^ +... +482 | fn walk(&mut self, path: &Path) { + | ^^^^ + +warning: function `unknown` is never used + --> src/storage/inventory.rs:552:4 + | +552 | fn unknown(path: &Path, reason: &str) -> InventoryEntry { + | ^^^^^^^ + +warning: function `inspect_storage` is never used + --> src/storage/inventory.rs:567:15 + | +567 | pub(crate) fn inspect_storage(target: &MaintenanceTarget) -> StorageInventory { + | ^^^^^^^^^^^^^^^ + +warning: function `inspect_with_hook` is never used + --> src/storage/inventory.rs:570:4 + | +570 | fn inspect_with_hook(target: &MaintenanceTarget, between_scans: impl FnOnce()) -> StorageInventory { + | ^^^^^^^^^^^^^^^^^ + +warning: function `identity_supported` is never used + --> src/storage/inventory.rs:894:4 + | +894 | fn identity_supported() -> bool { + | ^^^^^^^^^^^^^^^^^^ + +warning: enum `LeaseObservation` is never used + --> src/storage/inventory.rs:897:6 + | +897 | enum LeaseObservation { + | ^^^^^^^^^^^^^^^^ + +warning: function `lease_observation` is never used + --> src/storage/inventory.rs:902:4 + | +902 | fn lease_observation(path: &Path) -> LeaseObservation { + | ^^^^^^^^^^^^^^^^^ + +warning: function `existing_shared_lock` is never used + --> src/storage/inventory.rs:937:4 + | +937 | fn existing_shared_lock(path: &Path) -> std::io::Result { + | ^^^^^^^^^^^^^^^^^^^^ + +warning: function `ambiguous_file_identity` is never used + --> src/storage/inventory.rs:965:4 + | +965 | fn ambiguous_file_identity(metadata: &Metadata) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `repository_name` is never used + --> src/storage/inventory.rs:973:4 + | +973 | fn repository_name(root: &Path) -> String { + | ^^^^^^^^^^^^^^^ + +warning: function `valid_key` is never used + --> src/storage/inventory.rs:994:4 + | +994 | fn valid_key(key: &str) -> bool { + | ^^^^^^^^^ + +warning: function `classify` is never used + --> src/storage/inventory.rs:1000:4 + | +1000 | fn classify<'a>( + | ^^^^^^^^ + +warning: method `path` is never used + --> src/storage/locks.rs:34:19 + | +20 | impl LockedFile { + | --------------- method in this implementation +... +34 | pub(crate) fn path(&self) -> &Path { + | ^^^^ + +warning: struct `MaintenanceOptions` is never constructed + --> src/storage/maintenance.rs:16:19 + | +16 | pub(crate) struct MaintenanceOptions { + | ^^^^^^^^^^^^^^^^^^ + +warning: enum `MaintenanceStatus` is never used + --> src/storage/maintenance.rs:33:17 + | +33 | pub(crate) enum MaintenanceStatus { + | ^^^^^^^^^^^^^^^^^ + +warning: struct `MaintenanceContinuation` is never constructed + --> src/storage/maintenance.rs:42:19 + | +42 | pub(crate) struct MaintenanceContinuation { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: struct `MaintenanceReport` is never constructed + --> src/storage/maintenance.rs:48:19 + | +48 | pub(crate) struct MaintenanceReport { + | ^^^^^^^^^^^^^^^^^ + +warning: function `maintain_storage` is never used + --> src/storage/maintenance.rs:63:15 + | +63 | pub(crate) fn maintain_storage( + | ^^^^^^^^^^^^^^^^ + +warning: function `maintain_storage_with_lease` is never used + --> src/storage/maintenance.rs:97:15 + | +97 | pub(crate) fn maintain_storage_with_lease( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `deferred_reference_report` is never used + --> src/storage/maintenance.rs:105:4 + | +105 | fn deferred_reference_report( + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: struct `WorkBudget` is never constructed + --> src/storage/maintenance.rs:125:8 + | +125 | struct WorkBudget { + | ^^^^^^^^^^ + +warning: struct `AttemptBudget` is never constructed + --> src/storage/maintenance.rs:128:8 + | +128 | struct AttemptBudget { + | ^^^^^^^^^^^^^ + +warning: associated items `new`, `take`, and `available` are never used + --> src/storage/maintenance.rs:134:8 + | +133 | impl WorkBudget { + | --------------- associated items in this implementation +134 | fn new(options: &MaintenanceOptions) -> Self { + | ^^^ +... +143 | fn take(&mut self) -> bool { + | ^^^^ +... +146 | fn available(&self) -> bool { + | ^^^^^^^^^ + +warning: methods `alive` and `take` are never used + --> src/storage/maintenance.rs:153:8 + | +152 | impl AttemptBudget { + | ------------------ methods in this implementation +153 | fn alive(&self) -> bool { + | ^^^^^ +... +156 | fn take(&mut self) -> bool { + | ^^^^ + +warning: struct `ValidationGate` is never constructed + --> src/storage/maintenance.rs:169:19 + | +169 | pub(crate) struct ValidationGate { + | ^^^^^^^^^^^^^^ + +warning: struct `GrantState` is never constructed + --> src/storage/maintenance.rs:173:8 + | +173 | struct GrantState { + | ^^^^^^^^^^ + +warning: methods `grant`, `cancel`, `wait`, `checkpoint`, and `time_checkpoint` are never used + --> src/storage/maintenance.rs:180:8 + | +179 | impl ValidationGate { + | ------------------- methods in this implementation +180 | fn grant(&self, budget: &WorkBudget) { + | ^^^^^ +... +200 | pub(crate) fn cancel(&self) { + | ^^^^^^ +... +208 | fn wait(&self, charge: bool) -> std::io::Result { + | ^^^^ +... +237 | pub(crate) fn checkpoint(&self) -> std::io::Result<()> { + | ^^^^^^^^^^ +... +240 | fn time_checkpoint(&self) -> std::io::Result { + | ^^^^^^^^^^^^^^^ + +warning: constant `VALIDATION_CHUNK_BYTES` is never used + --> src/storage/maintenance.rs:245:7 + | +245 | const VALIDATION_CHUNK_BYTES: usize = 8 * 1024; + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: struct `GatedReader` is never constructed + --> src/storage/maintenance.rs:249:19 + | +249 | pub(crate) struct GatedReader { + | ^^^^^^^^^^^ + +warning: associated function `new` is never used + --> src/storage/maintenance.rs:256:19 + | +255 | impl GatedReader { + | ---------------------- associated function in this implementation +256 | pub(crate) fn new(inner: R, gate: ValidationGate) -> Self { + | ^^^ + +warning: struct `GatedWriter` is never constructed + --> src/storage/maintenance.rs:285:19 + | +285 | pub(crate) struct GatedWriter { + | ^^^^^^^^^^^ + +warning: associated items `new` and `into_inner` are never used + --> src/storage/maintenance.rs:292:19 + | +291 | impl GatedWriter { + | ---------------------- associated items in this implementation +292 | pub(crate) fn new(inner: W, gate: ValidationGate) -> Self { + | ^^^ +... +300 | pub(crate) fn into_inner(self) -> W { + | ^^^^^^^^^^ + +warning: struct `ArtifactCursor` is never constructed + --> src/storage/maintenance.rs:329:8 + | +329 | struct ArtifactCursor { + | ^^^^^^^^^^^^^^ + +warning: enum `CursorStep` is never used + --> src/storage/maintenance.rs:336:6 + | +336 | enum CursorStep { + | ^^^^^^^^^^ + +warning: associated items `open` and `next` are never used + --> src/storage/maintenance.rs:342:8 + | +341 | impl ArtifactCursor { + | ------------------- associated items in this implementation +342 | fn open(root: &Path) -> Result { + | ^^^^ +... +354 | fn next(&mut self, budget: &mut WorkBudget) -> Result { + | ^^^^ + +warning: struct `PendingValidation` is never constructed + --> src/storage/maintenance.rs:399:8 + | +399 | struct PendingValidation { + | ^^^^^^^^^^^^^^^^^ + +warning: static `VALIDATION_WORKERS` is never used + --> src/storage/maintenance.rs:406:8 + | +406 | static VALIDATION_WORKERS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + | ^^^^^^^^^^^^^^^^^^ + +warning: struct `ValidationSlot` is never constructed + --> src/storage/maintenance.rs:407:8 + | +407 | struct ValidationSlot; + | ^^^^^^^^^^^^^^ + +warning: associated function `acquire` is never used + --> src/storage/maintenance.rs:409:8 + | +408 | impl ValidationSlot { + | ------------------- associated function in this implementation +409 | fn acquire() -> Option { + | ^^^^^^^ + +warning: struct `AdvisoryArtifactJob` is never constructed + --> src/storage/maintenance.rs:427:8 + | +427 | struct AdvisoryArtifactJob { + | ^^^^^^^^^^^^^^^^^^^ + +warning: associated items `start_owned`, `grant`, and `current_under` are never used + --> src/storage/maintenance.rs:438:8 + | +437 | impl AdvisoryArtifactJob { + | ------------------------ associated items in this implementation +438 | fn start_owned( + | ^^^^^^^^^^^ +... +540 | fn grant(&self, budget: &WorkBudget) { + | ^^^^^ +... +543 | fn current_under( + | ^^^^^^^^^^^^^ + +warning: struct `Retirement` is never constructed + --> src/storage/maintenance.rs:574:8 + | +574 | struct Retirement { + | ^^^^^^^^^^ + +warning: multiple associated items are never used + --> src/storage/maintenance.rs:587:8 + | +586 | impl Retirement { + | --------------- associated items in this implementation +587 | fn prepare(root: &Path, original: &Path) -> Result<(Self, PathBuf), NativeError> { + | ^^^^^^^ +... +637 | fn validate(&self, root: &Path) -> Result<(), NativeError> { + | ^^^^^^^^ +... +662 | fn journal_path(&self, root: &Path) -> PathBuf { + | ^^^^^^^^^^^^ +... +665 | fn stage_path(&self, root: &Path) -> PathBuf { + | ^^^^^^^^^^ +... +669 | fn stage_temp_path(&self, root: &Path) -> PathBuf { + | ^^^^^^^^^^^^^^^ +... +673 | fn verify_journal(&self, root: &Path, supplied: &Path) -> Result<(), NativeError> { + | ^^^^^^^^^^^^^^ +... +708 | fn detach( + | ^^^^^^ + +warning: struct `ObjectStamp` is never constructed + --> src/storage/maintenance.rs:751:8 + | +751 | struct ObjectStamp { + | ^^^^^^^^^^^ + +warning: associated function `capture` is never used + --> src/storage/maintenance.rs:758:8 + | +757 | impl ObjectStamp { + | ---------------- associated function in this implementation +758 | fn capture(path: &Path) -> Result { + | ^^^^^^^ + +warning: function `optional_stamp` is never used + --> src/storage/maintenance.rs:782:4 + | +782 | fn optional_stamp(path: &Path) -> Result, NativeError> { + | ^^^^^^^^^^^^^^ + +warning: function `safe_relative` is never used + --> src/storage/maintenance.rs:790:4 + | +790 | fn safe_relative(path: &Path) -> Result<(), NativeError> { + | ^^^^^^^^^^^^^ + +warning: function `invalid` is never used + --> src/storage/maintenance.rs:800:4 + | +800 | fn invalid(reason: &str) -> NativeError { + | ^^^^^^^ + +warning: function `artifact_directory_shape` is never used + --> src/storage/maintenance.rs:806:4 + | +806 | fn artifact_directory_shape(path: &Path) -> Result<(), NativeError> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `delete_retirement_step` is never used + --> src/storage/maintenance.rs:831:4 + | +831 | fn delete_retirement_step( + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `read_unlinked_stage` is never used + --> src/storage/maintenance.rs:911:4 + | +911 | fn read_unlinked_stage( + | ^^^^^^^^^^^^^^^^^^^ + +warning: enum `StageFault` is never used + --> src/storage/maintenance.rs:941:6 + | +941 | enum StageFault { + | ^^^^^^^^^^ + +warning: function `stage_fault` is never used + --> src/storage/maintenance.rs:950:4 + | +950 | fn stage_fault(actual: StageFault, expected: StageFault) -> Result<(), NativeError> { + | ^^^^^^^^^^^ + +warning: function `publish_unlinked_stage` is never used + --> src/storage/maintenance.rs:961:4 + | +961 | fn publish_unlinked_stage( + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `remove_retirement_journal` is never used + --> src/storage/maintenance.rs:1020:4 + | +1020 | fn remove_retirement_journal( + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `allocated_bytes` is never used + --> src/storage/maintenance.rs:1091:4 + | +1091 | fn allocated_bytes(metadata: &fs::Metadata) -> Option { + | ^^^^^^^^^^^^^^^ + +warning: function `empty_report` is never used + --> src/storage/maintenance.rs:1100:4 + | +1100 | fn empty_report() -> MaintenanceReport { + | ^^^^^^^^^^^^ + +warning: method `artifact_root` is never used + --> src/storage/maintenance_ownership.rs:169:19 + | + 51 | impl ReferenceDomain { + | -------------------- method in this implementation +... +169 | pub(crate) fn artifact_root(&self) -> &Path { + | ^^^^^^^^^^^^^ + +warning: method `counter` is never used + --> src/storage/maintenance_ownership.rs:193:19 + | +192 | impl ReferenceEpoch { + | ------------------- method in this implementation +193 | pub(crate) fn counter(&self) -> u64 { + | ^^^^^^^ + +warning: enum `EpochObservation` is never used + --> src/storage/maintenance_ownership.rs:198:17 + | +198 | pub(crate) enum EpochObservation { + | ^^^^^^^^^^^^^^^^ + +warning: function `observe_reference_epoch` is never used + --> src/storage/maintenance_ownership.rs:337:15 + | +337 | pub(crate) fn observe_reference_epoch( + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: field `reason` is never read + --> src/storage/maintenance_ownership.rs:379:5 + | +377 | struct ReferenceActivity { + | ----------------- field in this struct +378 | guard: Option, +379 | reason: Option, + | ^^^^^^ + | + = note: `ReferenceActivity` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis + +warning: methods `tracked` and `pending_reason` are never used + --> src/storage/maintenance_ownership.rs:557:19 + | +556 | impl ReferenceMutation<'_> { + | -------------------------- methods in this implementation +557 | pub(crate) fn tracked(&self) -> bool { + | ^^^^^^^ +... +560 | pub(crate) fn pending_reason(&self) -> Option<&str> { + | ^^^^^^^^^^^^^^ + +warning: enum `MaintenanceAdmission` is never used + --> src/storage/maintenance_ownership.rs:566:17 + | +566 | pub(crate) enum MaintenanceAdmission<'a> { + | ^^^^^^^^^^^^^^^^^^^^ + +warning: enum `DestinationWriter` is never used + --> src/storage/maintenance_ownership.rs:573:6 + | +573 | enum DestinationWriter<'a> { + | ^^^^^^^^^^^^^^^^^ + +warning: method `lease` is never used + --> src/storage/maintenance_ownership.rs:578:8 + | +577 | impl DestinationWriter<'_> { + | -------------------------- method in this implementation +578 | fn lease(&self) -> &WriterLease { + | ^^^^^ + +warning: struct `MaintenanceLease` is never constructed + --> src/storage/maintenance_ownership.rs:591:19 + | +591 | pub(crate) struct MaintenanceLease<'a> { + | ^^^^^^^^^^^^^^^^ + +warning: multiple methods are never used + --> src/storage/maintenance_ownership.rs:603:19 + | +602 | impl MaintenanceLease<'_> { + | ------------------------- methods in this implementation +603 | pub(crate) fn begin_reference_mutation(&self) -> Result, NativeError> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +... +614 | pub(crate) fn target(&self) -> &MaintenanceTarget { + | ^^^^^^ +... +618 | pub(crate) fn revalidate(&self) -> Result<(), NativeError> { + | ^^^^^^^^^^ +... +644 | pub(crate) fn artifact_cohort_verified(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +... +649 | pub(crate) fn cohort_verified(&self) -> Result { + | ^^^^^^^^^^^^^^^ +... +671 | pub(crate) fn with_state_lock( + | ^^^^^^^^^^^^^^^ +... +689 | pub(crate) fn cohort_record(&self) -> Result { + | ^^^^^^^^^^^^^ +... +730 | pub(crate) fn publication_cohort_record( + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +796 | pub(crate) fn direct_publication_cohort_record( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +810 | pub(crate) fn publication_cohort_verified( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +819 | pub(crate) fn direct_publication_cohort_verified( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +826 | pub(crate) fn cohort_record_path(&self) -> PathBuf { + | ^^^^^^^^^^^^^^^^^^ + +warning: struct `PublicationCohortRecord` is never constructed + --> src/storage/maintenance_ownership.rs:832:19 + | +832 | pub(crate) struct PublicationCohortRecord { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: enum `PublicationCohortOrigin` is never used + --> src/storage/maintenance_ownership.rs:846:6 + | +846 | enum PublicationCohortOrigin { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: enum `PublicationScope` is never used + --> src/storage/maintenance_ownership.rs:851:6 + | +851 | enum PublicationScope { + | ^^^^^^^^^^^^^^^^ + +warning: struct `DestinationCohortRecord` is never constructed + --> src/storage/maintenance_ownership.rs:864:19 + | +864 | pub(crate) struct DestinationCohortRecord { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: enum `DestinationCohortOrigin` is never used + --> src/storage/maintenance_ownership.rs:880:6 + | +880 | enum DestinationCohortOrigin { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: method `cohort_verified` is never used + --> src/storage/maintenance_ownership.rs:1042:8 + | + 920 | impl ArtifactUseLease { + | --------------------- method in this implementation +... +1042 | fn cohort_verified(&self) -> Result { + | ^^^^^^^^^^^^^^^ + +warning: associated items `try_exclusive`, `try_exclusive_many`, and `exclusive_proof` are never used + --> src/storage/maintenance_ownership.rs:1105:19 + | +1086 | impl DatabaseUseLease { + | --------------------- associated items in this implementation +... +1105 | pub(crate) fn try_exclusive(database: &Path) -> Result, NativeError> { + | ^^^^^^^^^^^^^ +... +1112 | pub(crate) fn try_exclusive_many( + | ^^^^^^^^^^^^^^^^^^ +... +1187 | pub(crate) fn exclusive_proof(&self) -> Result { + | ^^^^^^^^^^^^^^^ + +warning: struct `DatabaseUseProof` is never constructed + --> src/storage/maintenance_ownership.rs:1216:19 + | +1216 | pub(crate) struct DatabaseUseProof { + | ^^^^^^^^^^^^^^^^ + +warning: function `observe_artifact_cohort` is never used + --> src/storage/maintenance_ownership.rs:1286:15 + | +1286 | pub(crate) fn observe_artifact_cohort( + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `claim_maintenance` is never used + --> src/storage/maintenance_ownership.rs:1307:15 + | +1307 | pub(crate) fn claim_maintenance( + | ^^^^^^^^^^^^^^^^^ + +warning: function `claim_maintenance_with_owned` is never used + --> src/storage/maintenance_ownership.rs:1312:15 + | +1312 | pub(crate) fn claim_maintenance_with_owned<'a>( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `claim` is never used + --> src/storage/maintenance_ownership.rs:1319:4 + | +1319 | fn claim<'a>( + | ^^^^^ + +warning: function `guard_registry` is never used + --> src/storage/maintenance_ownership.rs:1504:4 + | +1504 | fn guard_registry(root: &Path, guards: &mut Vec) -> Result<(), String> { + | ^^^^^^^^^^^^^^ + +warning: function `overlapping_pairs` is never used + --> src/storage/maintenance_ownership.rs:1588:4 + | +1588 | fn overlapping_pairs( + | ^^^^^^^^^^^^^^^^^ + +warning: function `resources_overlap` is never used + --> src/storage/maintenance_ownership.rs:1633:4 + | +1633 | fn resources_overlap(a: &Path, b: &Path) -> bool { + | ^^^^^^^^^^^^^^^^^ + +warning: constant `IDLE_INTERVAL` is never used + --> src/storage/maintenance_scheduler.rs:12:7 + | +12 | const IDLE_INTERVAL: Duration = Duration::from_secs(60); + | ^^^^^^^^^^^^^ + +warning: trait `MaintenanceClock` is never used + --> src/storage/maintenance_scheduler.rs:14:18 + | +14 | pub(crate) trait MaintenanceClock { + | ^^^^^^^^^^^^^^^^ + +warning: struct `SystemMaintenanceClock` is never constructed + --> src/storage/maintenance_scheduler.rs:19:19 + | +19 | pub(crate) struct SystemMaintenanceClock; + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: struct `MaintenanceSnapshot` is never constructed + --> src/storage/maintenance_scheduler.rs:35:19 + | +35 | pub(crate) struct MaintenanceSnapshot { + | ^^^^^^^^^^^^^^^^^^^ + +warning: enum `MaintenanceDispatch` is never used + --> src/storage/maintenance_scheduler.rs:48:17 + | +48 | pub(crate) enum MaintenanceDispatch { + | ^^^^^^^^^^^^^^^^^^^ + +warning: struct `ScheduleState` is never constructed + --> src/storage/maintenance_scheduler.rs:56:8 + | +56 | struct ScheduleState { + | ^^^^^^^^^^^^^ + +warning: struct `MaintenanceScheduler` is never constructed + --> src/storage/maintenance_scheduler.rs:71:19 + | +71 | pub(crate) struct MaintenanceScheduler { + | ^^^^^^^^^^^^^^^^^^^^ + +warning: associated items `new`, `request`, `shutdown`, `snapshot`, and `run_if_due` are never used + --> src/storage/maintenance_scheduler.rs:76:12 + | + 75 | impl MaintenanceScheduler { + | ------------------------- associated items in this implementation + 76 | pub fn new(now: Instant) -> Self { + | ^^^ +... + 93 | pub fn request(&self) { + | ^^^^^^^ +... +102 | pub fn shutdown(&self) { + | ^^^^^^^^ +... +108 | pub fn snapshot(&self, clock: &impl MaintenanceClock) -> MaintenanceSnapshot { + | ^^^^^^^^ +... +133 | pub fn run_if_due( + | ^^^^^^^^^^ + +warning: function `failed_report` is never used + --> src/storage/maintenance_scheduler.rs:209:4 + | +209 | fn failed_report(observed_at_unix_ms: u64, error: String) -> MaintenanceReport { + | ^^^^^^^^^^^^^ + +warning: method `writer_lease` is never used + --> src/storage/managed.rs:155:19 + | +154 | impl ManagedWriteSession { + | ------------------------ method in this implementation +155 | pub(crate) fn writer_lease(&self) -> Result<&WriterLease, NativeError> { + | ^^^^^^^^^^^^ + +warning: `codebase-graph` (lib) generated 138 warnings + Finished `test` profile [unoptimized + debuginfo] target(s) in 2.58s diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout new file mode 100644 index 00000000..37f5c221 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout @@ -0,0 +1 @@ +{"rust-build-meta":{"target-directory":"/Users/rabii/Projects/Repositories/codebaseGraph/target","build-directory":"/Users/rabii/Projects/Repositories/codebaseGraph/target","base-output-directories":["debug"],"non-test-binaries":{"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4":[{"name":"codebase-graph","kind":"bin-exe","path":"debug/codebase-graph"}]},"build-script-out-dirs":{"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4":"debug/build/codebase-graph-85a00d40af0fbc91/out"},"build-script-info":{},"linked-paths":["debug/build/codebase-graph-85a00d40af0fbc91/out","debug/build/cxx-73984724934a7db4/out","debug/build/lbug-3bd878011e7cf72d/out","debug/build/libmimalloc-sys-daecde77e05167a8/out","debug/build/link-cplusplus-133b8a5d45c5d1c8/out","debug/build/tree-sitter-659746aa2cbac4d2/out","debug/build/tree-sitter-c-d742716439a639df/out","debug/build/tree-sitter-cpp-30401f96f9a816ee/out","debug/build/tree-sitter-css-ab5a6aad9cb4769e/out","debug/build/tree-sitter-fortran-d1d9c449ecbe0f71/out","debug/build/tree-sitter-go-e5f0ca018d62ab82/out","debug/build/tree-sitter-html-10983a9819bc81f9/out","debug/build/tree-sitter-javascript-e96d93b32ecff8fd/out","debug/build/tree-sitter-python-b531e90ba31c4199/out","debug/build/tree-sitter-rust-bc00d90a99c05f69/out","debug/build/tree-sitter-typescript-dc071d12a223b642/out"],"platforms":{"host":{"platform":{"triple":"aarch64-apple-darwin","target-features":"unknown"},"libdir":{"status":"available","path":"/Users/rabii/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib"}},"targets":[]},"target-platforms":[{"triple":"aarch64-apple-darwin","target-features":"unknown"}],"target-platform":null},"rust-binaries":{"codebase-graph::storage_maintenance":{"binary-id":"codebase-graph::storage_maintenance","binary-name":"storage_maintenance","package-id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","kind":"test","binary-path":"/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_maintenance-926da32abefb62ce","build-platform":"target"}}} \ No newline at end of file diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json new file mode 100644 index 00000000..14ea3182 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json @@ -0,0 +1,4 @@ +{ + "status": "actual2testexecutioncaptured; before-after source/binaryidentical", + "run_id": "5f3b8e10-9d6f-414b-a4ec-6a910655d899" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.json b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.json new file mode 100644 index 00000000..cbb4a463 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.json @@ -0,0 +1 @@ +{"packages":[{"name":"adler2","version":"2.0.1","id":"registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1","license":"0BSD OR MIT OR Apache-2.0","license_file":null,"description":"A simple clean-room implementation of the Adler-32 checksum","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"adler2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/adler2-2.0.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/adler2-2.0.1/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"core":["dep:core"],"default":["std"],"rustc-dep-of-std":["core"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/adler2-2.0.1/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--cfg=docsrs"]}},"release":{"no-dev-version":true,"pre-release-commit-message":"Release {{version}}","tag-message":"{{version}}","pre-release-replacements":[{"file":"CHANGELOG.md","replace":"## Unreleased\n\nNo changes.\n\n## [{{version}} - {{date}}](https://github.com/jonas-schievink/adler/releases/tag/v{{version}})\n","search":"## Unreleased\n"},{"file":"README.md","replace":"adler = \"{{version}}\"","search":"adler = \"[a-z0-9\\\\.-]+\""},{"file":"src/lib.rs","replace":"https://docs.rs/adler/{{version}}","search":"https://docs.rs/adler/[a-z0-9\\.-]+"}]}},"publish":null,"authors":["Jonas Schievink ","oyvindln "],"categories":["algorithms"],"keywords":["checksum","integrity","hash","adler32","zlib"],"readme":"README.md","repository":"https://github.com/oyvindln/adler2","homepage":null,"documentation":"https://docs.rs/adler2/","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"aho-corasick","version":"1.1.4","id":"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","license":"Unlicense OR MIT","license_file":null,"description":"Fast multiple substring searching.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.17","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"aho_corasick","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/aho-corasick-1.1.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"default":["std","perf-literal"],"logging":["dep:log"],"perf-literal":["dep:memchr"],"std":["memchr?/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/aho-corasick-1.1.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--generate-link-to-definition"]}}},"publish":null,"authors":["Andrew Gallant "],"categories":["text-processing"],"keywords":["string","search","text","pattern","multi"],"readme":"README.md","repository":"https://github.com/BurntSushi/aho-corasick","homepage":"https://github.com/BurntSushi/aho-corasick","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60.0"},{"name":"ammonia","version":"4.1.8","id":"registry+https://github.com/rust-lang/crates.io-index#ammonia@4.1.8","license":"MIT OR Apache-2.0","license_file":null,"description":"HTML Sanitization","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cssparser","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.37.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"html5ever","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.39","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"maplit","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"url","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"version-sync","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"ammonia","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ammonia-4.1.8/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"ammonia-cat","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ammonia-4.1.8/examples/ammonia-cat.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"version-numbers","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ammonia-4.1.8/tests/version-numbers.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ammonia-4.1.8/Cargo.toml","metadata":null,"publish":null,"authors":["Michael Howell "],"categories":["web-programming","text-processing"],"keywords":["sanitization","html","security","xss"],"readme":"README.md","repository":"https://github.com/rust-ammonia/ammonia","homepage":null,"documentation":"https://docs.rs/ammonia/","edition":"2021","links":null,"default_run":null,"rust_version":"1.80"},{"name":"arrayvec","version":"0.7.6","id":"registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6","license":"MIT OR Apache-2.0","license_file":null,"description":"A vector with fixed capacity, backed by an array (it can be stored on the stack too). Implements fixed capacity ArrayVec and ArrayString.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"borsh","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zeroize","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bencher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"matches","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"arrayvec","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"borsh","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/tests/borsh.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/tests/serde.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/tests/tests.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"arraystring","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/benches/arraystring.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"extend","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/benches/extend.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"borsh":["dep:borsh"],"default":["std"],"serde":["dep:serde"],"std":[],"zeroize":["dep:zeroize"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/Cargo.toml","metadata":{"docs":{"rs":{"features":["borsh","serde","zeroize"]}},"release":{"no-dev-version":true,"tag-name":"{{version}}"}},"publish":null,"authors":["bluss"],"categories":["data-structures","no-std"],"keywords":["stack","vector","array","data-structure","no_std"],"readme":"README.md","repository":"https://github.com/bluss/arrayvec","homepage":null,"documentation":"https://docs.rs/arrayvec/","edition":"2018","links":null,"default_run":null,"rust_version":"1.51"},{"name":"atomic-waker","version":"1.1.2","id":"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2","license":"Apache-2.0 OR MIT","license_file":null,"description":"A synchronization primitive for task wakeup","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"portable-atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["cargo_bench_support"],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.7.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"atomic_waker","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic-waker-1.1.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"basic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic-waker-1.1.2/tests/basic.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"waker","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic-waker-1.1.2/benches/waker.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"portable-atomic":["dep:portable-atomic"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic-waker-1.1.2/Cargo.toml","metadata":null,"publish":null,"authors":["Stjepan Glavina ","Contributors to futures-rs"],"categories":["asynchronous","concurrency"],"keywords":["waker","notify","wake","futures","async"],"readme":"README.md","repository":"https://github.com/smol-rs/atomic-waker","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.36"},{"name":"autocfg","version":"1.5.1","id":"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1","license":"Apache-2.0 OR MIT","license_file":null,"description":"Automatic cfg for Rust compiler features","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"autocfg","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"integers","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/integers.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"nightly","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/nightly.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"paths","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/paths.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"traits","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/traits.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"versions","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/versions.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/tests/no_std.rs","edition":"2015","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rustflags","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/tests/rustflags.rs","edition":"2015","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/tests/tests.rs","edition":"2015","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"wrappers","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/tests/wrappers.rs","edition":"2015","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/Cargo.toml","metadata":null,"publish":null,"authors":["Josh Stone "],"categories":["development-tools::build-utils"],"keywords":["rustc","build","autoconf"],"readme":"README.md","repository":"https://github.com/cuviper/autocfg","homepage":null,"documentation":"https://docs.rs/autocfg/","edition":"2015","links":null,"default_run":null,"rust_version":"1.0"},{"name":"axum","version":"0.8.9","id":"registry+https://github.com/rust-lang/crates.io-index#axum@0.8.9","license":"MIT","license_file":null,"description":"HTTP routing and request handling library that focuses on ergonomics and modularity","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"axum-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"axum-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"base64","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.22.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"form_urlencoded","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["tokio","server","service"],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"matchit","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.8.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mime","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.16","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"multer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"percent-encoding","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"reqwest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["json","stream","multipart"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.211","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.221","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["raw_value"],"target":null,"registry":null},{"name":"serde_path_to_error","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.8","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_urlencoded","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sha1","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sync_wrapper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.44","kind":null,"rename":"tokio","optional":true,"uses_default_features":true,"features":["time"],"target":null,"registry":null},{"name":"tokio-tungstenite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.29.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["util"],"target":null,"registry":null},{"name":"tower-http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.8","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["add-extension","auth","catch-panic","compression-br","compression-deflate","compression-gzip","cors","decompression-br","decompression-deflate","decompression-gzip","follow-redirect","fs","limit","map-request-body","map-response-body","metrics","normalize-path","propagate-header","redirect","request-id","sensitive-headers","set-header","set-status","timeout","trace","util","validate-request"],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-service","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["client"],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"reqwest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["json","stream","multipart"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.221","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["raw_value"],"target":null,"registry":null},{"name":"time","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["serde-human-readable"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.44.2","kind":"dev","rename":"tokio","optional":false,"uses_default_features":true,"features":["macros","rt","rt-multi-thread","net","test-util"],"target":null,"registry":null},{"name":"tokio-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-tungstenite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.29.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.2","kind":"dev","rename":"tower","optional":false,"uses_default_features":true,"features":["util","timeout","limit","load-shed","steer","filter"],"target":null,"registry":null},{"name":"tower-http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["add-extension","auth","catch-panic","compression-br","compression-deflate","compression-gzip","cors","decompression-br","decompression-deflate","decompression-gzip","follow-redirect","fs","limit","map-request-body","map-response-body","metrics","normalize-path","propagate-header","redirect","request-id","sensitive-headers","set-header","set-status","timeout","trace","util","validate-request"],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing-subscriber","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["json"],"target":null,"registry":null},{"name":"uuid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["serde","v4"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"axum","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"panic_location","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/tests/panic_location.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benches","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/benches/benches.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"__private":["tokio","http1","dep:reqwest"],"__private_docs":["axum-core/__private_docs","tower/full","dep:serde","dep:tower-http"],"default":["form","http1","json","matched-path","original-uri","query","tokio","tower-log","tracing"],"form":["dep:form_urlencoded","dep:serde_urlencoded","dep:serde_path_to_error"],"http1":["dep:hyper","hyper?/http1","hyper-util?/http1"],"http2":["dep:hyper","hyper?/http2","hyper-util?/http2"],"json":["dep:serde_json","dep:serde_path_to_error"],"macros":["dep:axum-macros"],"matched-path":[],"multipart":["dep:multer"],"original-uri":[],"query":["dep:form_urlencoded","dep:serde_urlencoded","dep:serde_path_to_error"],"tokio":["dep:hyper-util","dep:tokio","tokio/net","tokio/rt","tower/make","tokio/macros"],"tower-log":["tower/log"],"tracing":["dep:tracing","axum-core/tracing"],"ws":["dep:hyper","tokio","dep:tokio-tungstenite","dep:sha1","dep:base64"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"playground":{"features":["http1","http2","json","multipart","ws"]},"cargo-public-api-crates":{"allowed":["axum_core","axum_macros","futures_core","futures_sink","futures_util","pin_project_lite","tower_layer","tower_service","bytes","http","http_body","serde_core","tokio","reqwest"]}},"publish":null,"authors":[],"categories":["asynchronous","network-programming","web-programming::http-server"],"keywords":["http","web","routing"],"readme":"README.md","repository":"https://github.com/tokio-rs/axum","homepage":"https://github.com/tokio-rs/axum","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.80"},{"name":"axum-core","version":"0.5.6","id":"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.6","license":"MIT","license_file":null,"description":"Core types and traits for axum","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mime","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.16","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sync_wrapper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["limit"],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-service","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.37","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.25.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros"],"target":null,"registry":null},{"name":"tower-http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["limit"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"axum_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-core-0.5.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"__private_docs":["dep:tower-http"],"tracing":["dep:tracing"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-core-0.5.6/Cargo.toml","metadata":{"cargo_check_external_types":{"allowed_external_types":["futures_core::*","tower_layer::*","bytes::*","http::*","http_body::*"]},"cargo-machete":{"ignored":["tower-http"]},"docs":{"rs":{"all-features":true}}},"publish":null,"authors":[],"categories":["asynchronous","network-programming","web-programming"],"keywords":["http","web","framework"],"readme":"README.md","repository":"https://github.com/tokio-rs/axum","homepage":"https://github.com/tokio-rs/axum","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.78"},{"name":"bitflags","version":"2.13.0","id":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A macro to generate structures which behave like bitflags.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bytemuck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.228","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"bytemuck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.103","kind":"dev","rename":"serde_lib","optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.19","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.18","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"zerocopy","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"bitflags","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"custom_bits_type","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/custom_bits_type.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"custom_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/custom_derive.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"fmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/fmt.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"macro_free","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/macro_free.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/serde.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"parse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/benches/parse.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"arbitrary":["dep:arbitrary"],"bytemuck":["dep:bytemuck"],"example_generated":[],"serde":["serde_core"],"serde_core":["dep:serde_core"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/Cargo.toml","metadata":{"docs":{"rs":{"features":["example_generated"]}}},"publish":null,"authors":["The Rust Project Developers"],"categories":["no-std"],"keywords":["bit","bitmask","bitflags","flags"],"readme":"README.md","repository":"https://github.com/bitflags/bitflags","homepage":"https://github.com/bitflags/bitflags","documentation":"https://docs.rs/bitflags","edition":"2021","links":null,"default_run":null,"rust_version":"1.56.0"},{"name":"block-buffer","version":"0.10.4","id":"registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4","license":"MIT OR Apache-2.0","license_file":null,"description":"Buffer type for block processing of data","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"generic-array","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.14","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"block_buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/block-buffer-0.10.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/block-buffer-0.10.4/tests/mod.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/block-buffer-0.10.4/Cargo.toml","metadata":null,"publish":null,"authors":["RustCrypto Developers"],"categories":["cryptography","no-std"],"keywords":["block","buffer"],"readme":"README.md","repository":"https://github.com/RustCrypto/utils","homepage":null,"documentation":"https://docs.rs/block-buffer","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"bumpalo","version":"3.20.3","id":"registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.20.3","license":"MIT OR Apache-2.0","license_file":null,"description":"A fast bump allocation arena for Rust.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"allocator-api2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.171","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"blink-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.10.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rayon-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.12.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.197","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.115","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"bumpalo","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bumpalo-3.20.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"allocator-api2":["dep:allocator-api2"],"allocator_api":[],"bench_allocator_api":["allocator_api","blink-alloc/nightly"],"boxed":[],"collections":[],"default":[],"serde":["dep:serde"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bumpalo-3.20.3/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["Nick Fitzgerald "],"categories":["memory-management","rust-patterns","no-std"],"keywords":["arena","bump","allocator"],"readme":"README.md","repository":"https://github.com/fitzgen/bumpalo","homepage":null,"documentation":"https://docs.rs/bumpalo","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.1"},{"name":"byteorder","version":"1.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0","license":"Unlicense OR MIT","license_file":null,"description":"Library for reading/writing numbers in big-endian and little-endian.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"byteorder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/byteorder-1.5.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/byteorder-1.5.0/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"i128":[],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/byteorder-1.5.0/Cargo.toml","metadata":null,"publish":null,"authors":["Andrew Gallant "],"categories":["encoding","parsing","no-std"],"keywords":["byte","endian","big-endian","little-endian","binary"],"readme":"README.md","repository":"https://github.com/BurntSushi/byteorder","homepage":"https://github.com/BurntSushi/byteorder","documentation":"https://docs.rs/byteorder","edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"bytes","version":"1.12.1","id":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","license":"MIT","license_file":null,"description":"Types and traits for working with bytes","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"portable-atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3","kind":null,"rename":"extra-platforms","optional":true,"uses_default_features":false,"features":["require-cas"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.60","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"loom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(loom)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"bytes","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_buf_mut","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_buf_mut.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_bytes","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_bytes.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_bytes_odd_alloc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_bytes_odd_alloc.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_bytes_vec_alloc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_bytes_vec_alloc.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_chain","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_chain.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_debug","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_debug.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_iter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_iter.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_limit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_limit.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_reader","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_reader.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_serde.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_take","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_take.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"extra-platforms":["dep:extra-platforms"],"serde":["dep:serde"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Carl Lerche ","Sean McArthur "],"categories":["network-programming","data-structures"],"keywords":["buffers","zero-copy","io"],"readme":"README.md","repository":"https://github.com/tokio-rs/bytes","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.57"},{"name":"cc","version":"1.2.64","id":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","license":"MIT OR Apache-2.0","license_file":null,"description":"A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"find-msvc-tools","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"jobserver","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.30","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"shlex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.62","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(unix)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cc-1.2.64/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"jobserver":[],"parallel":["dep:libc","dep:jobserver"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cc-1.2.64/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":["development-tools::build-utils"],"keywords":["build-dependencies"],"readme":"README.md","repository":"https://github.com/rust-lang/cc-rs","homepage":"https://github.com/rust-lang/cc-rs","documentation":"https://docs.rs/cc","edition":"2018","links":null,"default_run":null,"rust_version":"1.63"},{"name":"cfg-if","version":"1.0.4","id":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","license":"MIT OR Apache-2.0","license_file":null,"description":"A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cfg_if","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cfg-if-1.0.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"xcrate","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cfg-if-1.0.4/tests/xcrate.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"core":["dep:core"],"rustc-dep-of-std":["core"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cfg-if-1.0.4/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/cfg-if","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.32"},{"name":"cmake","version":"0.1.58","id":"registry+https://github.com/rust-lang/crates.io-index#cmake@0.1.58","license":"MIT OR Apache-2.0","license_file":null,"description":"A build dependency for running `cmake` to build a native library\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.46","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cmake","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cmake-0.1.58/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cmake-0.1.58/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":["development-tools::build-utils"],"keywords":["build-dependencies"],"readme":"README.md","repository":"https://github.com/rust-lang/cmake-rs","homepage":"https://github.com/rust-lang/cmake-rs","documentation":"https://docs.rs/cmake","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"codebase-graph","version":"3.0.4","id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","license":"MIT","license_file":null,"description":"Native codebaseGraph CLI and MCP server for local code knowledge graphs.","source":null,"dependencies":[{"name":"fs2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lbug","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.19.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libmimalloc-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.49","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["extended"],"target":null,"registry":null},{"name":"lzma-rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mimalloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.52","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["extended"],"target":null,"registry":null},{"name":"notify","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^8.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sha2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-c","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-cpp","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.23.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-css","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-fortran","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-go","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-html","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.23.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-javascript","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-python","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-rust","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-typescript","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.23.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_System_ProcessStatus","Win32_System_Threading","Win32_System_JobObjects","Win32_System_SystemServices","Win32_Security","Win32_Security_Authorization","Win32_System_Memory","Win32_Storage_FileSystem","Win32_System_Pipes","Win32_System_Console","Win32_System_Diagnostics_ToolHelp"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"codebase_graph","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"codebase-graph","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/src/bin/codebase-graph.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"agent_hooks_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/agent_hooks_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"auto_repo_root","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/auto_repo_root.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"coordinator_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/coordinator_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"generation_storage","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/generation_storage.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"http_admission_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/http_admission_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"http_daemon_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/http_daemon_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"native_request_identity","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/native_request_identity.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"refresh_recovery_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/refresh_recovery_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"storage_foundation","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/src/storage/test_harness.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"storage_maintenance","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/storage_maintenance.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"bundled-windows-extensions":[]},"manifest_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["command-line-utilities","development-tools"],"keywords":["codebase","graph","mcp","cli","analysis"],"readme":"README.md","repository":"https://github.com/rabii-chaarani/codebaseGraph","homepage":"https://github.com/rabii-chaarani/codebaseGraph","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"codespan-reporting","version":"0.11.1","id":"registry+https://github.com/rust-lang/crates.io-index#codespan-reporting@0.11.1","license":"Apache-2.0","license_file":null,"description":"Beautiful diagnostic reporting for text-based programming languages","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"termcolor","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"unicode-width","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lazy_static","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"peg","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustyline","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"structopt","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"unindent","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"codespan_reporting","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"custom_files","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/custom_files.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"peg_calculator","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/peg_calculator.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"readme_preview","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/readme_preview.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"reusable_diagnostic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/reusable_diagnostic.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"term","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/term.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"term","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/tests/term.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"ascii-only":[],"serde":["dep:serde"],"serialization":["serde","serde/rc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/Cargo.toml","metadata":null,"publish":null,"authors":["Brendan Zabarauskas "],"categories":[],"keywords":[],"readme":"../README.md","repository":"https://github.com/brendanzab/codespan","homepage":"https://github.com/brendanzab/codespan","documentation":"https://docs.rs/codespan-reporting","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"cpufeatures","version":"0.2.17","id":"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17","license":"MIT OR Apache-2.0","license_file":null,"description":"Lightweight runtime CPU feature detection for aarch64, loongarch64, and x86/x86_64 targets, \nwith no_std support and support for mobile targets including Android and iOS\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.155","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"aarch64-linux-android","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.155","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.155","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.155","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"loongarch64\", target_os = \"linux\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cpufeatures","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"aarch64","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/tests/aarch64.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"loongarch64","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/tests/loongarch64.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"x86","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/tests/x86.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/Cargo.toml","metadata":null,"publish":null,"authors":["RustCrypto Developers"],"categories":["hardware-support","no-std"],"keywords":["cpuid","target-feature"],"readme":"README.md","repository":"https://github.com/RustCrypto/utils","homepage":null,"documentation":"https://docs.rs/cpufeatures","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"crc","version":"3.4.0","id":"registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Rust implementation of CRC with support of various standards","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"crc-catalog","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"crc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"crc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/tests/crc.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/Cargo.toml","metadata":null,"publish":null,"authors":["Rui Hu ","Akhil Velagapudi <4@4khil.com>"],"categories":["algorithms","no-std"],"keywords":["crc","crc16","crc32","crc64","hash"],"readme":"README.md","repository":"https://github.com/mrhooray/crc-rs.git","homepage":null,"documentation":"https://docs.rs/crc","edition":"2021","links":null,"default_run":null,"rust_version":"1.83"},{"name":"crc-catalog","version":"2.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.5.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Catalog of CRC algorithms (generated from http://reveng.sourceforge.net/crc-catalogue) expressed as simple Rust structs.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"crc_catalog","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-catalog-2.5.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-catalog-2.5.0/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["Akhil Velagapudi "],"categories":["no-std","network-programming"],"keywords":["crc"],"readme":"README.md","repository":"https://github.com/akhilles/crc-catalog.git","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"crc32fast","version":"1.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Fast, SIMD-accelerated CRC32 (IEEE) checksum computation","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bencher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"crc32fast","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.0/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"nightly":[],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.0/Cargo.toml","metadata":null,"publish":null,"authors":["Sam Rijs ","Alex Crichton "],"categories":["algorithms","no-std"],"keywords":["hash","crc","crc32","simd","fast"],"readme":"README.md","repository":"https://github.com/srijs/rust-crc32fast","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"crypto-common","version":"0.1.7","id":"registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.7","license":"MIT OR Apache-2.0","license_file":null,"description":"Common cryptographic traits","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"generic-array","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.14.7","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["more_lengths"],"target":null,"registry":null},{"name":"rand_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"typenum","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.14","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"crypto_common","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crypto-common-0.1.7/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"getrandom":["rand_core/getrandom"],"rand_core":["dep:rand_core"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crypto-common-0.1.7/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["RustCrypto Developers"],"categories":["cryptography","no-std"],"keywords":["crypto","traits"],"readme":"README.md","repository":"https://github.com/RustCrypto/traits","homepage":null,"documentation":"https://docs.rs/crypto-common","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"cssparser","version":"0.37.0","id":"registry+https://github.com/rust-lang/crates.io-index#cssparser@0.37.0","license":"MPL-2.0","license_file":null,"description":"Rust implementation of CSS Syntax Level 3","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cssparser-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"dtoa-short","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"malloc_size_of","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"phf","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["macros"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"difference","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"encoding_rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.25","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cssparser","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cssparser-0.37.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"bench":[],"default":["fast_match_byte","fast_match_color"],"fast_match_byte":["dep:cssparser-macros"],"fast_match_color":["dep:phf"],"malloc_size_of":["dep:malloc_size_of"],"serde":["dep:serde"],"skip_long_tests":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cssparser-0.37.0/Cargo.toml","metadata":null,"publish":null,"authors":["Simon Sapin "],"categories":[],"keywords":["css","syntax","parser"],"readme":"README.md","repository":"https://github.com/servo/rust-cssparser","homepage":null,"documentation":"https://docs.rs/cssparser/","edition":"2018","links":null,"default_run":null,"rust_version":"1.71"},{"name":"cty","version":"0.2.2","id":"registry+https://github.com/rust-lang/crates.io-index#cty@0.2.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Type aliases to C types like c_int for use with bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cty","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cty-0.2.2/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cty-0.2.2/Cargo.toml","metadata":null,"publish":null,"authors":["Jorge Aparicio "],"categories":["embedded","external-ffi-bindings","no-std"],"keywords":["c","types","bindgen","ffi"],"readme":"README.md","repository":"https://github.com/japaric/cty","homepage":null,"documentation":"https://docs.rs/cty","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"cxx","version":"1.0.138","id":"registry+https://github.com/rust-lang/crates.io-index#cxx@1.0.138","license":"MIT OR Apache-2.0","license_file":null,"description":"Safe interop between Rust and C++","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cxxbridge-macro","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.138","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"foldhash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"link-cplusplus","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-build","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.138","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-gen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-test-suite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.81","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["diff"],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.83","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxxbridge-flags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.138","kind":"build","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"cxxbridge-cmd","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.138","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any())","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cxx","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/compiletest.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cxx_gen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/cxx_gen.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cxx_string","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/cxx_string.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cxx_vector","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/cxx_vector.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unique_ptr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/unique_ptr.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"c++14":["cxxbridge-flags/c++14"],"c++17":["cxxbridge-flags/c++17"],"c++20":["cxxbridge-flags/c++20"],"default":["std","cxxbridge-flags/default"],"std":["alloc","foldhash/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/Cargo.toml","metadata":{"bazel":{"additive_build_file_content":"cc_library(\n name = \"cxx_cc\",\n srcs = [\"src/cxx.cc\"],\n hdrs = [\"include/cxx.h\"],\n include_prefix = \"rust\",\n includes = [\"include\"],\n linkstatic = True,\n strip_include_prefix = \"include\",\n visibility = [\"//visibility:public\"],\n)\n","deps":[":cxx_cc"],"gen_build_script":false,"extra_aliased_targets":{"cxx_cc":"cxx_cc"}},"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::ffi","api-bindings","no-std"],"keywords":["ffi","c++"],"readme":"README.md","repository":"https://github.com/dtolnay/cxx","homepage":"https://cxx.rs","documentation":"https://docs.rs/cxx","edition":"2021","links":"cxxbridge1","default_run":null,"rust_version":"1.73"},{"name":"cxx-build","version":"1.0.138","id":"registry+https://github.com/rust-lang/crates.io-index#cxx-build@1.0.138","license":"MIT OR Apache-2.0","license_file":null,"description":"C++ code generator for integrating `cxx` crate into a Cargo build.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.83","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"codespan-reporting","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.74","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["span-locations"],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"scratch","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.46","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["clone-impls","full","parsing","printing"],"target":null,"registry":null},{"name":"cxx","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-gen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pkg-config","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.27","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cxx_build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-build-1.0.138/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"experimental-async-fn":[],"parallel":["cc/parallel"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-build-1.0.138/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::build-utils","development-tools::ffi"],"keywords":["ffi","build-dependencies"],"readme":null,"repository":"https://github.com/dtolnay/cxx","homepage":"https://cxx.rs","documentation":"https://docs.rs/cxx-build","edition":"2021","links":null,"default_run":null,"rust_version":"1.73"},{"name":"cxxbridge-flags","version":"1.0.138","id":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-flags@1.0.138","license":"MIT OR Apache-2.0","license_file":null,"description":"Compiler configuration of the `cxx` crate (implementation detail)","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cxxbridge_flags","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxxbridge-flags-1.0.138/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"c++14":[],"c++17":[],"c++20":[],"default":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxxbridge-flags-1.0.138/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::ffi","compilers"],"keywords":[],"readme":null,"repository":"https://github.com/dtolnay/cxx","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.73"},{"name":"cxxbridge-macro","version":"1.0.138","id":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-macro@1.0.138","license":"MIT OR Apache-2.0","license_file":null,"description":"Implementation detail of the `cxx` crate.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"clang-ast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.18","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.26","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memmap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.74","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.166","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.166","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.100","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.46","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"cxx","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"cxxbridge_macro","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxxbridge-macro-1.0.138/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"clang-ast":["dep:clang-ast"],"experimental-async-fn":[],"experimental-enum-variants-from-header":["clang-ast","flate2","memmap","serde","serde_derive","serde_json"],"flate2":["dep:flate2"],"memmap":["dep:memmap"],"serde":["dep:serde"],"serde_derive":["dep:serde_derive"],"serde_json":["dep:serde_json"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxxbridge-macro-1.0.138/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::ffi"],"keywords":["ffi"],"readme":"README.md","repository":"https://github.com/dtolnay/cxx","homepage":"https://cxx.rs","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.73"},{"name":"deranged","version":"0.3.11","id":"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11","license":"MIT OR Apache-2.0","license_file":null,"description":"Ranged integers","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"num-traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.15","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"powerfmt","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.126","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.86","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"deranged","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/deranged-0.3.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["std"],"num":["dep:num-traits"],"powerfmt":["dep:powerfmt"],"quickcheck":["dep:quickcheck","alloc"],"rand":["dep:rand"],"serde":["dep:serde"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/deranged-0.3.11/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docs_rs"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["Jacob Pratt "],"categories":[],"keywords":["integer","int","range"],"readme":"README.md","repository":"https://github.com/jhpratt/deranged","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"digest","version":"0.10.7","id":"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7","license":"MIT OR Apache-2.0","license_file":null,"description":"Traits for cryptographic hash functions and message authentication codes","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"blobby","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"block-buffer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"const-oid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"crypto-common","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"subtle","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"digest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/digest-0.10.7/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"blobby":["dep:blobby"],"block-buffer":["dep:block-buffer"],"const-oid":["dep:const-oid"],"core-api":["block-buffer"],"default":["core-api"],"dev":["blobby"],"mac":["subtle"],"oid":["const-oid"],"rand_core":["crypto-common/rand_core"],"std":["alloc","crypto-common/std"],"subtle":["dep:subtle"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/digest-0.10.7/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["RustCrypto Developers"],"categories":["cryptography","no-std"],"keywords":["digest","crypto","hash"],"readme":"README.md","repository":"https://github.com/RustCrypto/traits","homepage":null,"documentation":"https://docs.rs/digest","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"displaydoc","version":"0.2.7","id":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","license":"MIT OR Apache-2.0","license_file":null,"description":"A derive macro for implementing the display Trait via a doc comment and string interpolation\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"pretty_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"static_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"thiserror","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.24","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"displaydoc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compile_tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/compile_tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"happy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/happy.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/no_std.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"num_in_field","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/num_in_field.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"variantless","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/variantless.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}},"release":{"no-dev-version":true,"pre-release-hook":["./update-readme.sh"],"pre-release-replacements":[{"file":"CHANGELOG.md","search":"Unreleased","replace":"{{version}}"},{"file":"src/lib.rs","search":"#!\\[doc\\(html_root_url.*","replace":"#![doc(html_root_url = \"https://docs.rs/{{crate_name}}/{{version}}\")]","exactly":1},{"file":"CHANGELOG.md","search":"ReleaseDate","replace":"{{date}}"},{"file":"CHANGELOG.md","search":"","replace":"\n\n# [Unreleased] - ReleaseDate","exactly":1},{"file":"CHANGELOG.md","search":"\\.\\.\\.HEAD","replace":"...{{tag_name}}","exactly":1},{"file":"CHANGELOG.md","search":"","replace":"\n[Unreleased]: https://github.com/yaahc/{{crate_name}}/compare/{{tag_name}}...HEAD","exactly":1}]}},"publish":null,"authors":["Jane Lusby "],"categories":[],"keywords":["display","derive"],"readme":"README.md","repository":"https://github.com/yaahc/displaydoc","homepage":"https://github.com/yaahc/displaydoc","documentation":"https://docs.rs/displaydoc","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"dtoa","version":"1.0.11","id":"registry+https://github.com/rust-lang/crates.io-index#dtoa@1.0.11","license":"MIT OR Apache-2.0","license_file":null,"description":"Fast floating point primitive to string conversion","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"no-panic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"dtoa","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"no-panic":["dep:no-panic"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["value-formatting","no-std","no-std::no-alloc"],"keywords":["float"],"readme":"README.md","repository":"https://github.com/dtolnay/dtoa","homepage":null,"documentation":"https://docs.rs/dtoa","edition":"2021","links":null,"default_run":null,"rust_version":"1.68"},{"name":"dtoa-short","version":"0.3.5","id":"registry+https://github.com/rust-lang/crates.io-index#dtoa-short@0.3.5","license":"MPL-2.0","license_file":null,"description":"Serialize float number and truncate to certain precision","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"dtoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"float-cmp","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"dtoa_short","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-short-0.3.5/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-short-0.3.5/Cargo.toml","metadata":null,"publish":null,"authors":["Xidorn Quan "],"categories":["value-formatting"],"keywords":["float"],"readme":"README.md","repository":"https://github.com/upsuper/dtoa-short","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"equivalent","version":"1.0.2","id":"registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","license":"Apache-2.0 OR MIT","license_file":null,"description":"Traits for key comparison in maps.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"equivalent","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/equivalent-1.0.2/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/equivalent-1.0.2/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["data-structures","no-std"],"keywords":["hashmap","no_std"],"readme":"README.md","repository":"https://github.com/indexmap-rs/equivalent","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":"1.6"},{"name":"errno","version":"0.3.14","id":"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","license":"MIT OR Apache-2.0","license_file":null,"description":"Cross-platform interface to the `errno` variable.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"hermit\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"wasi\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(unix)","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.52, <0.62","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_System_Diagnostics_Debug"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"errno","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/errno-0.3.14/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"default":["std"],"std":["libc/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/errno-0.3.14/Cargo.toml","metadata":null,"publish":null,"authors":["Chris Wong ","Dan Gohman "],"categories":["no-std","os"],"keywords":[],"readme":"README.md","repository":"https://github.com/lambda-fairy/rust-errno","homepage":null,"documentation":"https://docs.rs/errno","edition":"2018","links":null,"default_run":null,"rust_version":"1.56"},{"name":"fastrand","version":"2.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.5.0","license":"Apache-2.0 OR MIT","license_file":null,"description":"A simple and fast random number generator","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wyhash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["wasm_js"],"target":"cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))","registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["wasm_js"],"target":"cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))","registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"fastrand","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"char","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/tests/char.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"smoke","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/tests/smoke.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/benches/bench.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"default":["std"],"getrandom":["dep:getrandom"],"js":["std","getrandom"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Stjepan Glavina "],"categories":["algorithms"],"keywords":["simple","fast","rand","random","wyrand"],"readme":"README.md","repository":"https://github.com/smol-rs/fastrand","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.63"},{"name":"filetime","version":"0.2.29","id":"registry+https://github.com/rust-lang/crates.io-index#filetime@0.2.29","license":"MIT/Apache-2.0","license_file":null,"description":"Platform-agnostic accessors of timestamps in File metadata\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.27","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"filetime","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/filetime-0.2.29/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/filetime-0.2.29/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":[],"keywords":["timestamp","mtime"],"readme":"README.md","repository":"https://github.com/alexcrichton/filetime","homepage":"https://github.com/alexcrichton/filetime","documentation":"https://docs.rs/filetime","edition":"2018","links":null,"default_run":null,"rust_version":"1.75.0"},{"name":"find-msvc-tools","version":"0.1.9","id":"registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9","license":"MIT OR Apache-2.0","license_file":null,"description":"Find windows-specific tools, read MSVC versions from the registry and from COM interfaces","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"find_msvc_tools","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/find-msvc-tools-0.1.9/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/find-msvc-tools-0.1.9/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["development-tools::build-utils"],"keywords":["build-dependencies"],"readme":"README.md","repository":"https://github.com/rust-lang/cc-rs","homepage":null,"documentation":"https://docs.rs/find-msvc-tools","edition":"2018","links":null,"default_run":null,"rust_version":"1.63"},{"name":"flate2","version":"1.1.10","id":"registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.10","license":"MIT OR Apache-2.0","license_file":null,"description":"DEFLATE compression and decompression exposed as Read/BufRead/Write streams.\nSupports miniz_oxide and multiple zlib implementations. Supports zlib, gzip,\nand raw deflate streams.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"crc32fast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"document-features","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libz-ng-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.16","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libz-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.20","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"miniz_oxide","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["simd"],"target":null,"registry":null},{"name":"zlib-rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["rust-allocator"],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"flate2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"compress_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/compress_file.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"decompress_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/decompress_file.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflatedecoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflatedecoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflatedecoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflatedecoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflatedecoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflatedecoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflateencoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflateencoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflateencoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflateencoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflateencoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflateencoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzbuilder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzbuilder.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzdecoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzdecoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzdecoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzdecoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzdecoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzdecoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzencoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzencoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzencoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzencoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzencoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzencoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzmultidecoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzmultidecoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzmultidecoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzmultidecoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibdecoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibdecoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibdecoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibdecoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibdecoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibdecoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibencoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibencoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibencoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibencoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibencoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibencoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"capabilities","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/capabilities.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"early-flush","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/early-flush.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"empty-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/empty-read.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"gunzip","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/gunzip.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"zero-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/zero-write.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"any_c_zlib":["any_zlib"],"any_impl":[],"any_zlib":["any_impl"],"cloudflare_zlib":["zlib"],"default":["rust_backend","runtime_detection"],"document-features":["dep:document-features"],"libz-ng-sys":["dep:libz-ng-sys"],"libz-sys":["dep:libz-sys"],"miniz-sys":["rust_backend"],"miniz_oxide":["any_impl","dep:miniz_oxide","dep:crc32fast"],"runtime_detection":["zlib-rs?/std","crc32fast?/std"],"rust_backend":["miniz_oxide","any_impl"],"zlib":["any_c_zlib","libz-sys","dep:crc32fast"],"zlib-default":["any_c_zlib","libz-sys/default","dep:crc32fast"],"zlib-ng":["any_c_zlib","libz-ng-sys","dep:crc32fast"],"zlib-ng-compat":["zlib","libz-sys/zlib-ng","dep:crc32fast"],"zlib-rs":["any_zlib","dep:zlib-rs"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Alex Crichton ","Josh Triplett "],"categories":["compression","api-bindings"],"keywords":["gzip","deflate","zlib","zlib-ng","encoding"],"readme":"README.md","repository":"https://github.com/rust-lang/flate2-rs","homepage":"https://github.com/rust-lang/flate2-rs","documentation":"https://docs.rs/flate2","edition":"2018","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"foldhash","version":"0.1.5","id":"registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5","license":"Zlib","license_file":null,"description":"A fast, non-cryptographic, minimally DoS-resistant hashing algorithm.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"ahash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"chrono","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"fxhash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hashbrown","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.14","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uuid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"foldhash","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/foldhash-0.1.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/foldhash-0.1.5/Cargo.toml","metadata":null,"publish":null,"authors":["Orson Peters "],"categories":["algorithms","no-std"],"keywords":["hash","hasher","no-std"],"readme":"README.md","repository":"https://github.com/orlp/foldhash","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"form_urlencoded","version":"1.2.2","id":"registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Parser and serializer for the application/x-www-form-urlencoded syntax, as used by HTML forms.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"percent-encoding","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"form_urlencoded","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/form_urlencoded-1.2.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":false}],"features":{"alloc":["percent-encoding/alloc"],"default":["std"],"std":["alloc","percent-encoding/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/form_urlencoded-1.2.2/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["The rust-url developers"],"categories":["no-std"],"keywords":[],"readme":null,"repository":"https://github.com/servo/rust-url","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.51"},{"name":"fs2","version":"0.4.3","id":"registry+https://github.com/rust-lang/crates.io-index#fs2@0.4.3","license":"MIT/Apache-2.0","license_file":null,"description":"Cross-platform file locks and file duplication.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tempdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.30","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"winapi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["handleapi","processthreadsapi","winerror","fileapi","winbase","std"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"fs2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fs2-0.4.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fs2-0.4.3/Cargo.toml","metadata":null,"publish":null,"authors":["Dan Burkert "],"categories":[],"keywords":["file","file-system","lock","duplicate","flock"],"readme":"README.md","repository":"https://github.com/danburkert/fs2-rs","homepage":null,"documentation":"https://docs.rs/fs2","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"fsevent-sys","version":"4.1.0","id":"registry+https://github.com/rust-lang/crates.io-index#fsevent-sys@4.1.0","license":"MIT","license_file":null,"description":"Rust bindings to the fsevent macOS API for file changes notifications","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.68","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"fsevent_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fsevent-sys-4.1.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fsevent-sys-4.1.0/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-apple-darwin"]}}},"publish":null,"authors":["Pierre Baillet "],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/octplane/fsevent-rust/tree/master/fsevent-sys","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"futures-channel","version":"0.3.32","id":"registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.32","license":"MIT OR Apache-2.0","license_file":null,"description":"Channels for asynchronous communication using futures-rs.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-sink","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"futures_channel","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"channel","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/channel.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mpsc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/mpsc.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mpsc-close","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/mpsc-close.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mpsc-size_hint","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/mpsc-size_hint.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"oneshot","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/oneshot.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"sync_mpsc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/benches/sync_mpsc.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"alloc":["futures-core/alloc"],"cfg-target-has-atomic":[],"default":["std"],"futures-sink":["dep:futures-sink"],"sink":["futures-sink"],"std":["alloc","futures-core/std"],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/futures-rs","homepage":"https://rust-lang.github.io/futures-rs","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.71"},{"name":"futures-core","version":"0.3.32","id":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","license":"MIT OR Apache-2.0","license_file":null,"description":"The core traits and types in for the `futures` library.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"portable-atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["require-cas"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"futures_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-core-0.3.32/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"cfg-target-has-atomic":[],"default":["std"],"portable-atomic":["dep:portable-atomic"],"std":["alloc"],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-core-0.3.32/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/futures-rs","homepage":"https://rust-lang.github.io/futures-rs","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.36"},{"name":"futures-task","version":"0.3.32","id":"registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.32","license":"MIT OR Apache-2.0","license_file":null,"description":"Tools for working with tasks.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"futures_task","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-task-0.3.32/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"cfg-target-has-atomic":[],"default":["std"],"std":["alloc"],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-task-0.3.32/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/futures-rs","homepage":"https://rust-lang.github.io/futures-rs","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.71"},{"name":"futures-util","version":"0.3.32","id":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","license":"MIT OR Apache-2.0","license_file":null,"description":"Common utilities and extension traits for the futures-rs library.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-io","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"futures-macro","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-sink","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-task","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.25","kind":null,"rename":"futures_01","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.26","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.6","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"slab","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.7","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"spin","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-io","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.11","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"futures_util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bilock","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/benches/bilock.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"flatten_unordered","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/benches/flatten_unordered.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"futures_unordered","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/benches/futures_unordered.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"select","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/benches/select.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"alloc":["futures-core/alloc","futures-task/alloc","slab"],"async-await":[],"async-await-macro":["async-await","futures-macro"],"bilock":[],"cfg-target-has-atomic":[],"channel":["std","futures-channel"],"compat":["std","futures_01","libc"],"default":["std","async-await","async-await-macro"],"futures-channel":["dep:futures-channel"],"futures-io":["dep:futures-io"],"futures-macro":["dep:futures-macro"],"futures-sink":["dep:futures-sink"],"futures_01":["dep:futures_01"],"io":["std","futures-io","memchr"],"io-compat":["io","compat","tokio-io","libc"],"libc":["dep:libc"],"memchr":["dep:memchr"],"portable-atomic":["futures-core/portable-atomic"],"sink":["futures-sink"],"slab":["dep:slab"],"spin":["dep:spin"],"std":["alloc","futures-core/std","futures-task/std","slab/std"],"tokio-io":["dep:tokio-io"],"unstable":["futures-core/unstable","futures-task/unstable"],"write-all-vectored":["io"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/futures-rs","homepage":"https://rust-lang.github.io/futures-rs","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.71"},{"name":"generic-array","version":"0.14.7","id":"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","license":"MIT","license_file":null,"description":"Generic types implementing functionality of arrays","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"typenum","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"zeroize","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"version_check","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"generic_array","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/generic-array-0.14.7/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/generic-array-0.14.7/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{"more_lengths":[],"serde":["dep:serde"],"zeroize":["dep:zeroize"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/generic-array-0.14.7/Cargo.toml","metadata":{"docs":{"rs":{"features":["serde","zeroize"]}}},"publish":null,"authors":["Bartłomiej Kamiński ","Aaron Trent "],"categories":["data-structures","no-std"],"keywords":["generic","array"],"readme":"README.md","repository":"https://github.com/fizyk20/generic-array.git","homepage":null,"documentation":"http://fizyk20.github.io/generic-array/generic_array/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"getrandom","version":"0.3.4","id":"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4","license":"MIT OR Apache-2.0","license_file":null,"description":"A small cross-platform library for retrieving random data from system source","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(any(target_os = \"linux\", target_os = \"android\"), not(any(all(target_os = \"linux\", target_env = \"\"), getrandom_backend = \"custom\", getrandom_backend = \"linux_raw\", getrandom_backend = \"rdrand\", getrandom_backend = \"rndr\"))))","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.98","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"js-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.77","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"), target_feature = \"atomics\"))","registry":null},{"name":"wasip2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"wasm32\", target_os = \"wasi\", target_env = \"p2\"))","registry":null},{"name":"r-efi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^5.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_os = \"uefi\", getrandom_backend = \"efi_rng\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"hurd\", target_os = \"illumos\", target_os = \"cygwin\", all(target_os = \"horizon\", target_arch = \"arm\")))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"haiku\", target_os = \"redox\", target_os = \"nto\", target_os = \"aix\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"ios\", target_os = \"visionos\", target_os = \"watchos\", target_os = \"tvos\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"macos\", target_os = \"openbsd\", target_os = \"vita\", target_os = \"emscripten\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"netbsd\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"solaris\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"vxworks\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"getrandom","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/tests/mod.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/benches/buffer.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"std":[],"wasm_js":["dep:wasm-bindgen","dep:js-sys"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/Cargo.toml","metadata":{"docs":{"rs":{"features":["std"]}},"cross":{"target":{"x86_64-unknown-netbsd":{"pre-build":["mkdir -p /tmp/netbsd","curl -fO https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.3/amd64/binary/sets/base.tar.xz","tar -C /tmp/netbsd -xJf base.tar.xz","cp /tmp/netbsd/usr/lib/libexecinfo.so /usr/local/x86_64-unknown-netbsd/lib","rm base.tar.xz","rm -rf /tmp/netbsd"]}}}},"publish":null,"authors":["The Rand Project Developers"],"categories":["os","no-std"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-random/getrandom","homepage":null,"documentation":"https://docs.rs/getrandom","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"hashbrown","version":"0.15.5","id":"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.5","license":"MIT OR Apache-2.0","license_file":null,"description":"A Rust port of Google's SwissTable hash map","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"alloc","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"allocator-api2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.9","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"equivalent","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"foldhash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.25","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bumpalo","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.13.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["allocator-api2"],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"fnv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lazy_static","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"hashbrown","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"equivalent_trait","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/equivalent_trait.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"hasher","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/hasher.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rayon","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/rayon.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/serde.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"set","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/set.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"insert_unique_unchecked","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/benches/insert_unique_unchecked.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"set_ops","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/benches/set_ops.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["dep:alloc"],"allocator-api2":["dep:allocator-api2"],"core":["dep:core"],"default":["default-hasher","inline-more","allocator-api2","equivalent","raw-entry"],"default-hasher":["dep:foldhash"],"equivalent":["dep:equivalent"],"inline-more":[],"nightly":["bumpalo/allocator_api"],"raw-entry":[],"rayon":["dep:rayon"],"rustc-dep-of-std":["nightly","core","alloc","rustc-internal-api"],"rustc-internal-api":[],"serde":["dep:serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/Cargo.toml","metadata":{"docs":{"rs":{"features":["nightly","rayon","serde","raw-entry"],"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Amanieu d'Antras "],"categories":["data-structures","no-std"],"keywords":["hash","no_std","hashmap","swisstable"],"readme":"README.md","repository":"https://github.com/rust-lang/hashbrown","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.65.0"},{"name":"html5ever","version":"0.39.0","id":"registry+https://github.com/rust-lang/crates.io-index#html5ever@0.39.0","license":"MIT OR Apache-2.0","license_file":null,"description":"High-performance browser-grade HTML5 parser","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"markup5ever","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.39","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"typed-arena","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"html5ever","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"arena","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/arena.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"noop-tokenize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/noop-tokenize.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"noop-tree-builder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/noop-tree-builder.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"print-tree-actions","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/print-tree-actions.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"tokenize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/tokenize.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"driver","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/tests/driver.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"html5ever","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/benches/html5ever.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"serde":["markup5ever/serde"],"trace_tokenizer":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/Cargo.toml","metadata":null,"publish":null,"authors":["The html5ever Project Developers"],"categories":["parser-implementations","web-programming"],"keywords":["html","html5","parser","parsing"],"readme":"README.md","repository":"https://github.com/servo/html5ever","homepage":null,"documentation":"https://docs.rs/html5ever","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"http","version":"1.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A set of types for representing HTTP requests and responses.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"http","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"header_map","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/tests/header_map.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"header_map_fuzz","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/tests/header_map_fuzz.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"status_code","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/tests/status_code.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton ","Carl Lerche ","Sean McArthur "],"categories":["web-programming"],"keywords":["http"],"readme":"README.md","repository":"https://github.com/hyperium/http","homepage":null,"documentation":"https://docs.rs/http","edition":"2021","links":null,"default_run":null,"rust_version":"1.57.0"},{"name":"http-body","version":"1.1.0","id":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","license":"MIT","license_file":null,"description":"Trait representing an asynchronous, streaming, HTTP request or response body.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"http_body","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-1.1.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"is_end_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-1.1.0/tests/is_end_stream.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-1.1.0/Cargo.toml","metadata":null,"publish":null,"authors":["Carl Lerche ","Lucio Franco ","Sean McArthur "],"categories":["web-programming"],"keywords":["http"],"readme":"README.md","repository":"https://github.com/hyperium/http-body","homepage":null,"documentation":"https://docs.rs/http-body","edition":"2018","links":null,"default_run":null,"rust_version":"1.61"},{"name":"http-body-util","version":"0.1.4","id":"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","license":"MIT","license_file":null,"description":"Combinators and adapters for HTTP request or response bodies.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["sync"],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","rt","sync","rt-multi-thread"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"http_body_util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-util-0.1.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"channel":["dep:tokio"],"default":[],"full":["channel"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-util-0.1.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Carl Lerche ","Lucio Franco ","Sean McArthur "],"categories":["web-programming"],"keywords":["http"],"readme":"README.md","repository":"https://github.com/hyperium/http-body","homepage":null,"documentation":"https://docs.rs/http-body-util","edition":"2018","links":null,"default_run":null,"rust_version":"1.61"},{"name":"httparse","version":"1.10.1","id":"registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1","license":"MIT OR Apache-2.0","license_file":null,"description":"A tiny, safe, speedy, zero-copy HTTP/1.x parser.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"httparse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uri","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/tests/uri.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"parse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/benches/parse.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/Cargo.toml","metadata":null,"publish":null,"authors":["Sean McArthur "],"categories":["network-programming","no-std","parser-implementations","web-programming"],"keywords":["http","parser","no_std"],"readme":"README.md","repository":"https://github.com/seanmonstar/httparse","homepage":null,"documentation":"https://docs.rs/httparse","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"httpdate","version":"1.0.3","id":"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3","license":"MIT OR Apache-2.0","license_file":null,"description":"HTTP date parsing and formatting","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"httpdate","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httpdate-1.0.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benchmarks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httpdate-1.0.3/benches/benchmarks.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httpdate-1.0.3/Cargo.toml","metadata":null,"publish":null,"authors":["Pyfisch "],"categories":[],"keywords":["http","date","time","simple","timestamp"],"readme":"README.md","repository":"https://github.com/pyfisch/httpdate","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.56"},{"name":"hyper","version":"1.11.0","id":"registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","license":"MIT","license_file":null,"description":"A protective and efficient HTTP library for all.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"atomic-waker","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.31","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"h2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.14","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"httparse","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"httpdate","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["const_generics","const_new"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["sync"],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"want","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"form_urlencoded","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["sink"],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc","sink"],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pretty_env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"spmc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["fs","macros","net","io-std","io-util","rt","rt-multi-thread","sync","time","test-util"],"target":null,"registry":null},{"name":"tokio-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing-subscriber","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"hyper","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-1.11.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"capi":[],"client":["dep:want","dep:pin-project-lite","dep:smallvec"],"default":[],"ffi":["dep:http-body-util","dep:futures-util"],"full":["client","http1","http2","server"],"http1":["dep:atomic-waker","dep:futures-channel","dep:futures-core","dep:httparse","dep:itoa"],"http2":["dep:atomic-waker","dep:futures-channel","dep:futures-core","dep:h2"],"nightly":[],"server":["dep:httpdate","dep:pin-project-lite","dep:smallvec"],"tracing":["dep:tracing"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-1.11.0/Cargo.toml","metadata":{"docs":{"rs":{"features":["ffi","full","tracing"],"rustdoc-args":["--cfg","hyper_unstable_ffi","--cfg","hyper_unstable_tracing"]}},"playground":{"features":["full"]},"capi":{"header":{"generation":false,"subdirectory":false},"install":{"include":{"asset":[{"from":"capi/include/hyper.h"}]}}}},"publish":null,"authors":["Sean McArthur "],"categories":["network-programming","web-programming::http-client","web-programming::http-server"],"keywords":["http","hyper","hyperium"],"readme":"README.md","repository":"https://github.com/hyperium/hyper","homepage":"https://hyper.rs","documentation":"https://docs.rs/hyper","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"hyper-util","version":"0.1.20","id":"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.20","license":"MIT","license_file":null,"description":"hyper utilities","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"base64","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.22","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.7.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.16","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ipnet","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"percent-encoding","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"socket2","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.5.9, <0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["all"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-service","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.16","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"pretty_env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","test-util","signal"],"target":null,"registry":null},{"name":"tokio-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pnet_datalink","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.35.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(target_os = \"linux\", target_os = \"macos\"))","registry":null},{"name":"system-configuration","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"windows-registry","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.3, <0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"hyper_util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"client","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/examples/client.rs","edition":"2021","required-features":["client-legacy","http1","tokio"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"server","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/examples/server.rs","edition":"2021","required-features":["server","http1","tokio"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"server_graceful","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/examples/server_graceful.rs","edition":"2021","required-features":["tokio","server-graceful","server-auto"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"legacy_client","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/tests/legacy_client.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"proxy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/tests/proxy.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"__internal_happy_eyeballs_tests":[],"client":["hyper/client","tokio/net","dep:tracing","dep:futures-channel","dep:tower-service"],"client-legacy":["client","dep:socket2","tokio/sync","dep:libc","dep:futures-util"],"client-pool":["client","dep:futures-util","dep:tower-layer"],"client-proxy":["client","dep:base64","dep:ipnet","dep:percent-encoding"],"client-proxy-system":["dep:system-configuration","dep:windows-registry"],"default":[],"full":["client","client-legacy","client-pool","client-proxy","client-proxy-system","server","server-auto","server-graceful","service","http1","http2","tokio","tracing"],"http1":["hyper/http1"],"http2":["hyper/http2"],"server":["hyper/server"],"server-auto":["server","http1","http2"],"server-graceful":["server","tokio/sync"],"service":["dep:tower-service"],"tokio":["dep:tokio","tokio/rt","tokio/time"],"tracing":["dep:tracing"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/Cargo.toml","metadata":{"docs":{"rs":{"features":["full"],"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Sean McArthur "],"categories":["network-programming","web-programming::http-client","web-programming::http-server"],"keywords":["http","hyper","hyperium"],"readme":"README.md","repository":"https://github.com/hyperium/hyper-util","homepage":"https://hyper.rs","documentation":"https://docs.rs/hyper-util","edition":"2021","links":null,"default_run":null,"rust_version":"1.64"},{"name":"icu_collections","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Collection of API for use in ICU libraries.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"potential_utf","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["zerovec"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"utf8_iter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive","yoke"],"target":null,"registry":null},{"name":"iai","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"toml","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["parse"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_collections","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"char16trie","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/tests/char16trie.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cpt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/tests/cpt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"codepointtrie","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/benches/codepointtrie.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"iai_cpt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/benches/iai_cpt.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"inv_list","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/benches/inv_list.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde?/alloc","zerovec/alloc"],"databake":["dep:databake","zerovec/databake"],"serde":["dep:serde","zerovec/serde","potential_utf/serde","alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["unicode","data-structures"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_locale_core","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","license":"Unicode-3.0","license_file":null,"description":"API for managing Unicode Language and Locale Identifiers","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"litemap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tinystr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"writeable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"iai","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"litemap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["testing"],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["use-std"],"target":null,"registry":null},{"name":"potential_utf","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_locale_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"langid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/tests/langid.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"locale","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/tests/locale.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"iai_langid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/benches/iai_langid.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"langid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/benches/langid.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"locale","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/benches/locale.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"subtags","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/benches/subtags.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["litemap/alloc","tinystr/alloc","writeable/alloc","serde?/alloc"],"databake":["dep:databake","alloc"],"serde":["dep:serde","tinystr/serde"],"zerovec":["dep:zerovec","tinystr/zerovec"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-semver-checks":{"lints":{"workspace":true}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["unicode","language-tags","bcp47"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_normalizer","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@2.2.0","license":"Unicode-3.0","license_file":null,"description":"API for normalizing text into Unicode Normalization Forms","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"harfbuzz-traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_collections","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_normalizer_data","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_properties","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_provider","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive","alloc"],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.10.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"utf16_iter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"utf8_iter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"write16","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"arraystring","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"arrayvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"atoi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"detone","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"write16","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["arrayvec","smallvec"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_normalizer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/tests/tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/bench.rs","edition":"2021","required-features":["utf16_iter","utf8_iter"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"canonical_composition","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/canonical_composition.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"canonical_decomposition","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/canonical_decomposition.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"composing_normalizer_nfc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/composing_normalizer_nfc.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"composing_normalizer_nfkc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/composing_normalizer_nfkc.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"decomposing_normalizer_nfd","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/decomposing_normalizer_nfd.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"decomposing_normalizer_nfkd","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/decomposing_normalizer_nfkd.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"utf16_throughput","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/utf16_throughput.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"compiled_data":["dep:icu_normalizer_data","icu_properties?/compiled_data","icu_provider/baked"],"datagen":["serde","dep:databake","icu_properties","icu_collections/databake","zerovec/databake","icu_properties?/datagen","icu_provider/export"],"default":["compiled_data","utf8_iter","utf16_iter"],"harfbuzz_traits":["dep:harfbuzz-traits"],"icu_properties":["dep:icu_properties"],"serde":["dep:serde","icu_collections/serde","zerovec/serde","icu_properties?/serde","icu_provider/serde"],"utf16_iter":["dep:utf16_iter","dep:write16"],"utf8_iter":["dep:utf8_iter"],"write16":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["unicode","normalization","text-processing"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_normalizer_data","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Data for the icu_normalizer crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_normalizer_data","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer_data-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer_data-2.2.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer_data-2.2.0/Cargo.toml","metadata":{"sources":{"cldr":{"tagged":"48.2.0"},"icuexport":{"tagged":"release-78.1rc"},"segmenter_lstm":{"tagged":"v0.1.0"}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["data","unicode"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_properties","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Definitions for Unicode properties","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"harfbuzz-traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_collections","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_locale_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["zerovec"],"target":null,"registry":null},{"name":"icu_properties_data","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_provider","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"unicode-bidi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.11","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerotrie","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["yoke","zerofrom"],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive","yoke"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_properties","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":["zerovec/alloc","icu_collections/alloc","serde?/alloc"],"compiled_data":["dep:icu_properties_data","icu_provider/baked"],"datagen":["serde","dep:databake","zerovec/databake","icu_collections/databake","icu_locale_core/databake","zerotrie/databake","icu_provider/export"],"default":["compiled_data"],"harfbuzz_traits":["dep:harfbuzz-traits"],"serde":["dep:serde","icu_locale_core/serde","zerovec/serde","icu_collections/serde","icu_provider/serde","zerotrie/serde"],"unicode_bidi":["dep:unicode-bidi"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3},"cargo-semver-checks":{"lints":{"workspace":true}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["character-properties","unicode"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_properties_data","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Data for the icu_properties crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_properties_data","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties_data-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties_data-2.2.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties_data-2.2.0/Cargo.toml","metadata":{"sources":{"cldr":{"tagged":"48.2.0"},"icuexport":{"tagged":"release-78.1rc"},"segmenter_lstm":{"tagged":"v0.1.0"}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["data","unicode"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_provider","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Trait and struct definitions for the ICU data provider","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"erased-serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"icu_locale_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.17","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"stable_deref_trait","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"writeable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerotrie","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_provider","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"data_locale_bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.2.0/benches/data_locale_bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["icu_locale_core/alloc","serde?/alloc","yoke/alloc","zerofrom/alloc","zerovec/alloc","zerotrie?/alloc","dep:stable_deref_trait","dep:writeable"],"baked":["dep:zerotrie","dep:writeable"],"deserialize_bincode_1":["serde","dep:bincode","std"],"deserialize_json":["serde","dep:serde_json"],"deserialize_postcard_1":["serde","dep:postcard"],"export":["serde","dep:erased-serde","dep:databake","std","sync","dep:postcard","zerovec/databake"],"logging":["dep:log"],"serde":["dep:serde","yoke/serde"],"std":["alloc"],"sync":[],"zerotrie":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"denylist":["macros"],"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["data-loading","data"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"idna","version":"1.1.0","id":"registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0","license":"MIT OR Apache-2.0","license_file":null,"description":"IDNA (Internationalizing Domain Names in Applications) and Punycode.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"idna_adapter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["const_generics"],"target":null,"registry":null},{"name":"utf8_iter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"assert_matches","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bencher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tester","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"idna","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/src/lib.rs","edition":"2018","doc":true,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/tests/tests.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/tests/unit.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unitbis","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/tests/unitbis.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"all","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/benches/all.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"compiled_data":["idna_adapter/compiled_data"],"default":["std","compiled_data"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["The rust-url developers"],"categories":[],"keywords":["no-std","web","http"],"readme":"README.md","repository":"https://github.com/servo/rust-url/","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.57"},{"name":"idna_adapter","version":"1.2.2","id":"registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.2","license":"Apache-2.0 OR MIT","license_file":null,"description":"Back end adapter for idna","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"icu_normalizer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_properties","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"idna_adapter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna_adapter-1.2.2/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true}],"features":{"compiled_data":["icu_normalizer/compiled_data","icu_properties/compiled_data"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna_adapter-1.2.2/Cargo.toml","metadata":null,"publish":null,"authors":["The rust-url developers"],"categories":["no-std","internationalization"],"keywords":["unicode","dns","idna"],"readme":"README.md","repository":"https://github.com/hsivonen/idna_adapter","homepage":"https://docs.rs/crate/idna_adapter/latest","documentation":"https://docs.rs/idna_adapter/latest/idna_adapter/","edition":"2024","links":null,"default_run":null,"rust_version":"1.86"},{"name":"indexmap","version":"2.7.1","id":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","license":"Apache-2.0 OR MIT","license_file":null,"description":"A hash table with consistent order and fast iteration.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"borsh","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"equivalent","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"hashbrown","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.15.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":null,"rename":"rustc-rayon","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"fnv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itertools","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lazy_static","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"indexmap","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"equivalent_trait","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/tests/equivalent_trait.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_full_path","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/tests/macros_full_path.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"quick","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/tests/quick.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/tests/tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"faststring","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/benches/faststring.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"arbitrary":["dep:arbitrary"],"borsh":["dep:borsh"],"default":["std"],"quickcheck":["dep:quickcheck"],"rayon":["dep:rayon"],"rustc-rayon":["dep:rustc-rayon"],"serde":["dep:serde"],"std":[],"test_debug":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/Cargo.toml","metadata":{"docs":{"rs":{"features":["arbitrary","quickcheck","serde","borsh","rayon"],"rustdoc-args":["--cfg","docsrs"]}},"release":{"allow-branch":["master"],"sign-tag":true,"tag-name":"{{version}}"}},"publish":null,"authors":[],"categories":["data-structures","no-std"],"keywords":["hashmap","no_std"],"readme":"README.md","repository":"https://github.com/indexmap-rs/indexmap","homepage":null,"documentation":"https://docs.rs/indexmap/","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"inotify","version":"0.11.2","id":"registry+https://github.com/rust-lang/crates.io-index#inotify@0.11.2","license":"ISC","license_file":null,"description":"Idiomatic wrapper for inotify","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.30","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"inotify-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.40.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["net"],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.30","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"maplit","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.12.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.40.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","rt-multi-thread","time"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"inotify","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/examples/stream.rs","edition":"2018","required-features":["stream"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"watch","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/examples/watch.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"main","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/tests/main.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"default":["stream"],"futures-util":["dep:futures-util"],"stream":["futures-util","tokio"],"tokio":["dep:tokio"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["api-bindings","filesystem"],"keywords":["inotify","linux"],"readme":"README.md","repository":"https://github.com/hannobraun/inotify-rs","homepage":null,"documentation":"https://docs.rs/inotify","edition":"2018","links":null,"default_run":null,"rust_version":"1.70"},{"name":"inotify-sys","version":"0.1.5","id":"registry+https://github.com/rust-lang/crates.io-index#inotify-sys@0.1.5","license":"ISC","license_file":null,"description":"inotify bindings for the Rust programming language","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"inotify_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-sys-0.1.5/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-sys-0.1.5/Cargo.toml","metadata":null,"publish":null,"authors":["Hanno Braun "],"categories":["external-ffi-bindings","filesystem"],"keywords":["inotify","linux"],"readme":"README.md","repository":"https://github.com/hannobraun/inotify-sys","homepage":null,"documentation":"https://docs.rs/inotify-sys","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"itoa","version":"1.0.18","id":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","license":"MIT OR Apache-2.0","license_file":null,"description":"Fast integer primitive to string conversion","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"no-panic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"itoa","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"no-panic":["dep:no-panic"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["value-formatting","no-std","no-std::no-alloc"],"keywords":["integer"],"readme":"README.md","repository":"https://github.com/dtolnay/itoa","homepage":null,"documentation":"https://docs.rs/itoa","edition":"2021","links":null,"default_run":null,"rust_version":"1.68"},{"name":"js-sys","version":"0.3.102","id":"registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.102","license":"MIT OR Apache-2.0","license_file":null,"description":"Bindings for all JS global objects and functions in all JS environments like\nNode.js and browsers, built on `#[wasm_bindgen]` using the `wasm-bindgen` crate.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.31","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"futures-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"half","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"js_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/js-sys-0.3.102/src/lib.rs","edition":"2021","doc":true,"doctest":false,"test":false}],"features":{"default":["std","unsafe-eval"],"futures-core-03-stream":["dep:futures-util","dep:futures-core"],"std":["wasm-bindgen/std","dep:futures-util"],"unsafe-eval":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/js-sys-0.3.102/Cargo.toml","metadata":null,"publish":null,"authors":["The wasm-bindgen Developers"],"categories":["wasm"],"keywords":[],"readme":"README.md","repository":"https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/js-sys","homepage":"https://wasm-bindgen.github.io/wasm-bindgen/","documentation":"https://docs.rs/js-sys","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"k-wiki","version":"3.0.4","id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki#3.0.4","license":"MIT","license_file":null,"description":"OKF knowledge wiki, projection builder, and MCP service.","source":null,"dependencies":[{"name":"ammonia","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~4.1.8","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"axum","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["http1","json","tokio"],"target":null,"registry":null},{"name":"codebase-graph","source":null,"req":"^3.0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null,"path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo"},{"name":"pulldown-cmark","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["html"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sha2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["io-util","macros","net","rt-multi-thread","signal","sync","time"],"target":null,"registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"yaml_serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"k_wiki","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"k-wiki","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/src/bin/k-wiki.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"api_integration","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/api_integration.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"authoring","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/authoring.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"bundle_reader","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/bundle_reader.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compilation","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/compilation.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"conformance","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/conformance.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"diagnostic_transports","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/diagnostic_transports.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"diagnostics","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/diagnostics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"memory","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/memory.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"projection","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/projection.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"refresh","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/refresh.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rendering","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/rendering.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"search","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/search.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"transports","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/transports.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"bundled-windows-extensions":["codebase-graph/bundled-windows-extensions"],"default":[],"graph-context":[]},"manifest_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/Cargo.toml","metadata":null,"publish":[],"authors":[],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/rabii-chaarani/codebaseGraph","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"kqueue","version":"1.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#kqueue@1.2.0","license":"MIT","license_file":null,"description":"kqueue interface for BSDs","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"kqueue-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.17","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["html_reports"],"target":null,"registry":null},{"name":"dhat","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"kqueue","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/examples/file.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"pid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/examples/pid.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"dhat_add_hundred","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_add_hundred.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_add_single","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_add_single.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_add_ten","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_add_ten.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_rm_hundred","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_rm_hundred.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_rm_single","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_rm_single.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_rm_ten","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_rm_ten.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"criterion_scaling","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/benches/criterion_scaling.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-netbsd","x86_64-unknown-freebsd","i686-unknown-freebsd","x86_64-unknown-dragonfly","x86_64-unknown-openbsd","aarch64-apple-darwin","x86_64-apple-darwin","aarch64-apple-ios","x86_64-apple-ios"]}}},"publish":null,"authors":["William Orr "],"categories":["os::unix-apis","filesystem"],"keywords":["kqueue","kevent","bsd"],"readme":"README.md","repository":"https://gitlab.com/rust-kqueue/rust-kqueue","homepage":null,"documentation":"https://docs.worrbase.com/rust/kqueue/","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"kqueue-sys","version":"1.1.2","id":"registry+https://github.com/rust-lang/crates.io-index#kqueue-sys@1.1.2","license":"MIT","license_file":null,"description":"Low-level kqueue interface for BSDs","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.11.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.74","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"kqueue_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-sys-1.1.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-sys-1.1.2/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-netbsd","x86_64-unknown-freebsd","i686-unknown-freebsd","x86_64-unknown-dragonfly","x86_64-unknown-openbsd","aarch64-apple-darwin","x86_64-apple-darwin","aarch64-apple-ios","x86_64-apple-ios"]}}},"publish":null,"authors":["William Orr ","Daniel (dmilith) Dettlaff "],"categories":["external-ffi-bindings","no-std","os::unix-apis","filesystem"],"keywords":["kqueue","kevent","bsd","darwin","macos"],"readme":"README.md","repository":"https://gitlab.com/rust-kqueue/rust-kqueue-sys","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"lbug","version":"0.19.0","id":"registry+https://github.com/rust-lang/crates.io-index#lbug@0.19.0","license":"MIT","license_file":null,"description":"An in-process property graph database management system built for query speed and scalability","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arrow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^55","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["ffi"],"target":null,"registry":null},{"name":"cxx","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.138","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rust_decimal","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.37","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"time","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uuid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rust_decimal_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.37","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"time","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros"],"target":null,"registry":null},{"name":"cmake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-build","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.138","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"lbug","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"arrow":["dep:arrow"],"default":[],"extension_tests":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":[],"categories":["database"],"keywords":["database","graph","ffi"],"readme":"lbug-src/README.md","repository":"https://github.com/LadybugDB/ladybug-rust","homepage":"https://ladybugdb.com/","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.81"},{"name":"libc","version":"0.2.189","id":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","license":"MIT OR Apache-2.0","license_file":null,"description":"Raw FFI bindings to platform libraries like libc.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"libc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.189/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"const_fn","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.189/tests/const_fn.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.189/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"align":[],"const-extern-fn":[],"default":["std"],"extra_traits":[],"rustc-dep-of-std":["align","rustc-std-workspace-core"],"rustc-std-workspace-core":["dep:rustc-std-workspace-core"],"std":[],"use_std":["std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.189/Cargo.toml","metadata":{"docs":{"rs":{"features":["extra_traits"],"default-target":"x86_64-unknown-linux-gnu","targets":["aarch64-apple-darwin","aarch64-pc-windows-msvc","aarch64-unknown-linux-gnu","i686-pc-windows-msvc","i686-unknown-linux-gnu","x86_64-pc-windows-gnu","x86_64-pc-windows-msvc","x86_64-unknown-linux-gnu","aarch64-pc-windows-gnullvm","aarch64-unknown-linux-musl","aarch64-unknown-linux-ohos","arm-unknown-linux-gnueabi","arm-unknown-linux-gnueabihf","armv7-unknown-linux-gnueabihf","armv7-unknown-linux-ohos","i686-pc-windows-gnu","loongarch64-unknown-linux-gnu","loongarch64-unknown-linux-musl","powerpc-unknown-linux-gnu","powerpc64-unknown-linux-gnu","powerpc64-unknown-linux-musl","powerpc64le-unknown-linux-gnu","powerpc64le-unknown-linux-musl","riscv64gc-unknown-linux-gnu","s390x-unknown-linux-gnu","sparcv9-sun-solaris","x86_64-apple-darwin","x86_64-pc-solaris","x86_64-pc-windows-gnullvm","x86_64-unknown-freebsd","x86_64-unknown-illumos","x86_64-unknown-linux-musl","x86_64-unknown-linux-ohos","x86_64-unknown-netbsd","aarch64-apple-ios","aarch64-apple-tvos","aarch64-apple-visionos","aarch64-apple-watchos","aarch64-linux-android","aarch64-unknown-fuchsia","arm-linux-androideabi","arm-unknown-linux-musleabi","arm-unknown-linux-musleabihf","arm64ec-pc-windows-msvc","armv5te-unknown-linux-gnueabi","armv5te-unknown-linux-musleabi","armv7-linux-androideabi","armv7-unknown-linux-musleabihf","i586-unknown-linux-gnu","i586-unknown-linux-musl","i686-linux-android","i686-unknown-freebsd","i686-unknown-linux-musl","nvptx64-nvidia-cuda","riscv64gc-unknown-linux-musl","sparc64-unknown-linux-gnu","thumbv7neon-linux-androideabi","thumbv7neon-unknown-linux-gnueabihf","wasm32-unknown-emscripten","wasm32-unknown-unknown","wasm32-wasip1","wasm32-wasip2","x86_64-fortanix-unknown-sgx","x86_64-linux-android","x86_64-unknown-fuchsia","x86_64-unknown-linux-gnux32","x86_64-unknown-redox","aarch64-unknown-freebsd","aarch64-unknown-hermit","aarch64-unknown-illumos","aarch64-unknown-netbsd","aarch64-unknown-qnx","aarch64-unknown-openbsd","aarch64-unknown-redox","aarch64-wrs-vxworks","aarch64_be-unknown-linux-gnu","aarch64_be-unknown-linux-musl","armebv7r-none-eabi","armebv7r-none-eabihf","armv7-linux-androideabi","armv7-wrs-vxworks-eabihf","armv7r-none-eabi","armv7r-none-eabihf","hexagon-unknown-linux-musl","i686-unknown-haiku","i686-unknown-netbsd","i686-unknown-openbsd","i686-wrs-vxworks","mips-unknown-linux-gnu","mips-unknown-linux-musl","mips64-unknown-linux-gnuabi64","mips64-unknown-linux-muslabi64","mips64el-unknown-linux-gnuabi64","mips64el-unknown-linux-muslabi64","mipsel-sony-psp","mipsel-unknown-linux-gnu","mipsel-unknown-linux-musl","powerpc-unknown-linux-gnuspe","powerpc-unknown-netbsd","powerpc-wrs-vxworks","powerpc-wrs-vxworks-spe","powerpc64-ibm-aix","powerpc64-unknown-freebsd","powerpc64-unknown-linux-gnu","powerpc64-wrs-vxworks","riscv32-wrs-vxworks","riscv32gc-unknown-linux-musl","riscv32i-unknown-none-elf","riscv32imac-unknown-none-elf","riscv32imc-unknown-none-elf","riscv64-wrs-vxworks","riscv64gc-unknown-freebsd","riscv64gc-unknown-hermit","riscv64gc-unknown-none-elf","riscv64imac-unknown-none-elf","s390x-unknown-linux-musl","sparc-unknown-linux-gnu","sparc64-unknown-netbsd","thumbv6m-none-eabi","thumbv7em-none-eabi","thumbv7em-none-eabihf","thumbv7m-none-eabi","wasm32-wasip3","x86_64-apple-ios","x86_64-pc-cygwin","x86_64-unknown-dragonfly","x86_64-unknown-haiku","x86_64-unknown-hermit","x86_64-unknown-hurd-gnu","x86_64-unknown-l4re-uclibc","x86_64-unknown-openbsd","x86_64-wrs-vxworks"],"cargo-args":["-Zbuild-std=core"]}},"cargo-semver-checks":{"lints":{"repr_align_removed":"warn","global_value_marked_deprecated":"warn"}}},"publish":null,"authors":[],"categories":["external-ffi-bindings","no-std","os"],"keywords":["libc","ffi","bindings","operating","system"],"readme":"README.md","repository":"https://github.com/rust-lang/libc","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"libmimalloc-sys","version":"0.1.49","id":"registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","license":"MIT","license_file":null,"description":"Sys crate wrapping the mimalloc allocator","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cty","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"libmimalloc_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libmimalloc-sys-0.1.49/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libmimalloc-sys-0.1.49/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"arena":[],"cty":["dep:cty"],"debug":[],"debug_in_debug":[],"extended":["cty"],"local_dynamic_tls":[],"no_thp":[],"override":[],"secure":[],"v2":[],"win_direct_tls":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libmimalloc-sys-0.1.49/Cargo.toml","metadata":{"docs":{"rs":{"features":["extended"]}}},"publish":null,"authors":["Octavian Oncescu "],"categories":["memory-management","api-bindings"],"keywords":["allocator","encrypted-heap","performance"],"readme":null,"repository":"https://github.com/purpleprotocol/mimalloc_rust/tree/master/libmimalloc-sys","homepage":null,"documentation":null,"edition":"2018","links":"mimalloc","default_run":null,"rust_version":null},{"name":"libyaml-rs","version":"0.3.0","id":"registry+https://github.com/rust-lang/crates.io-index#libyaml-rs@0.3.0","license":"MIT","license_file":null,"description":"libyaml transpiled to rust by c2rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"pretty_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"libyaml_rs","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"run-emitter-test-suite","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/src/bin/run-emitter-test-suite.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"run-parser-test-suite","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/src/bin/run-parser-test-suite.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_emitter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/tests/test_emitter.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parser","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/tests/test_parser.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parser_error","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/tests/test_parser_error.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["David Tolnay ","YAML Organization "],"categories":["encoding","parser-implementations","no-std"],"keywords":["yaml"],"readme":"README.md","repository":"https://github.com/yaml/libyaml-rs","homepage":null,"documentation":"https://docs.rs/libyaml-rs","edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"link-cplusplus","version":"1.0.12","id":"registry+https://github.com/rust-lang/crates.io-index#link-cplusplus@1.0.12","license":"MIT OR Apache-2.0","license_file":null,"description":"Link libstdc++ or libc++ automatically or manually","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"link_cplusplus","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/link-cplusplus-1.0.12/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/link-cplusplus-1.0.12/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"default":[],"libc++":[],"libcxx":["libc++"],"libstdc++":[],"libstdcxx":["libstdc++"],"nothing":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/link-cplusplus-1.0.12/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["external-ffi-bindings","development-tools::ffi","compilers","no-std","no-std::no-alloc"],"keywords":["linkage","c++"],"readme":"README.md","repository":"https://github.com/dtolnay/link-cplusplus","homepage":null,"documentation":"https://docs.rs/link-cplusplus","edition":"2018","links":"cplusplus","default_run":null,"rust_version":"1.68"},{"name":"linux-raw-sys","version":"0.12.1","id":"registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.12.1","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"Generated bindings for Linux's userspace API","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.100","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"static_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"linux_raw_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/linux-raw-sys-0.12.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"auxvec":[],"bootparam":[],"btrfs":[],"core":["dep:core"],"default":["std","general","errno"],"elf":[],"elf_uapi":[],"errno":[],"general":[],"if_arp":[],"if_ether":[],"if_packet":[],"if_tun":[],"image":[],"io_uring":[],"ioctl":[],"landlock":[],"loop_device":[],"mempolicy":[],"net":[],"netlink":[],"no_std":[],"prctl":[],"ptrace":[],"rustc-dep-of-std":["core","no_std"],"std":[],"system":[],"vm_sockets":[],"xdp":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/linux-raw-sys-0.12.1/Cargo.toml","metadata":{"docs":{"rs":{"features":["default","bootparam","btrfs","elf_uapi","image","ioctl","landlock","netlink","io_uring","if_arp","if_ether","if_packet","net","ptrace","prctl","elf","xdp","mempolicy","system","loop_device"],"targets":["x86_64-unknown-linux-gnu","i686-unknown-linux-gnu"]}}},"publish":null,"authors":["Dan Gohman "],"categories":["external-ffi-bindings"],"keywords":["linux","uapi","ffi"],"readme":"README.md","repository":"https://github.com/sunfishcode/linux-raw-sys","homepage":null,"documentation":"https://docs.rs/linux-raw-sys","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"litemap","version":"0.8.2","id":"registry+https://github.com/rust-lang/crates.io-index#litemap@0.8.2","license":"Unicode-3.0","license_file":null,"description":"A key-value Map implementation based on a flat, sorted Vec.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["use-std"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rkyv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["validation"],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"litemap","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"language_names_hash_map","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/examples/language_names_hash_map.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"language_names_lite_map","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/examples/language_names_lite_map.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"litemap_bincode","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/examples/litemap_bincode.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"litemap_postcard","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/examples/litemap_postcard.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"rkyv","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/tests/rkyv.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/tests/serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"store","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/tests/store.rs","edition":"2021","required-features":["testing"],"doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"litemap","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/benches/litemap.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"databake":["dep:databake"],"default":["alloc"],"serde":["dep:serde_core","alloc"],"testing":["alloc"],"yoke":["dep:yoke"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":[],"keywords":["data-structures","memory-efficiency","sorted","vec","map"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":"https://docs.rs/litemap","edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"lock_api","version":"0.4.14","id":"registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.14","license":"MIT OR Apache-2.0","license_file":null,"description":"Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"owning_ref","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"scopeguard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.126","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"lock_api","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lock_api-0.4.14/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"arc_lock":[],"atomic_usize":[],"default":["atomic_usize"],"nightly":[],"owning_ref":["dep:owning_ref"],"serde":["dep:serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lock_api-0.4.14/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--generate-link-to-definition"]}}},"publish":null,"authors":["Amanieu d'Antras "],"categories":["concurrency","no-std"],"keywords":["mutex","rwlock","lock","no_std"],"readme":null,"repository":"https://github.com/Amanieu/parking_lot","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"log","version":"0.4.32","id":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","license":"MIT OR Apache-2.0","license_file":null,"description":"A lightweight logging facade for Rust\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"sval","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.16","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"sval_ref","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.16","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"value-bag","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["inline-i128"],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.63","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sval","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.16","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sval_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.16","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"value-bag","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["test"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"log","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"integration","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/tests/integration.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/tests/macros.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"value","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/benches/value.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"kv":[],"kv_serde":["kv_std","value-bag/serde","serde"],"kv_std":["std","kv","value-bag/error"],"kv_sval":["kv","value-bag/sval","sval","sval_ref"],"kv_unstable":["kv","value-bag"],"kv_unstable_serde":["kv_serde","kv_unstable_std"],"kv_unstable_std":["kv_std","kv_unstable"],"kv_unstable_sval":["kv_sval","kv_unstable"],"max_level_debug":[],"max_level_error":[],"max_level_info":[],"max_level_off":[],"max_level_trace":[],"max_level_warn":[],"release_max_level_debug":[],"release_max_level_error":[],"release_max_level_info":[],"release_max_level_off":[],"release_max_level_trace":[],"release_max_level_warn":[],"serde":["serde_core"],"serde_core":["dep:serde_core"],"std":[],"sval":["dep:sval"],"sval_ref":["dep:sval_ref"],"value-bag":["dep:value-bag"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/Cargo.toml","metadata":{"docs":{"rs":{"features":["std","serde","kv_std","kv_sval","kv_serde"]}}},"publish":null,"authors":["The Rust Project Developers"],"categories":["development-tools::debugging"],"keywords":["logging"],"readme":"README.md","repository":"https://github.com/rust-lang/log","homepage":null,"documentation":"https://docs.rs/log","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"lzma-rs","version":"0.3.0","id":"registry+https://github.com/rust-lang/crates.io-index#lzma-rs@0.3.0","license":"MIT","license_file":null,"description":"A codec for LZMA, LZMA2 and XZ written in pure Rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"byteorder","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"crc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.17","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rust-lzma","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"lzma_rs","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lzma-rs-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"enable_logging":["env_logger","log"],"env_logger":["dep:env_logger"],"log":["dep:log"],"raw_decoder":[],"stream":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lzma-rs-0.3.0/Cargo.toml","metadata":{"docs":{"rs":{"features":["stream","raw_decoder"],"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Guillaume Endignoux "],"categories":["compression"],"keywords":["lzma","compression","decompression"],"readme":"README.md","repository":"https://github.com/gendx/lzma-rs","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"maplit","version":"1.0.2","id":"registry+https://github.com/rust-lang/crates.io-index#maplit@1.0.2","license":"MIT/Apache-2.0","license_file":null,"description":"Collection “literal” macros for HashMap, HashSet, BTreeMap, and BTreeSet.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"maplit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/maplit-1.0.2/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/maplit-1.0.2/tests/tests.rs","edition":"2015","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/maplit-1.0.2/Cargo.toml","metadata":{"release":{"no-dev-version":true}},"publish":null,"authors":["bluss"],"categories":["rust-patterns"],"keywords":["literal","data-structure","hashmap","macro"],"readme":null,"repository":"https://github.com/bluss/maplit","homepage":null,"documentation":"https://docs.rs/maplit/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"markup5ever","version":"0.39.0","id":"registry+https://github.com/rust-lang/crates.io-index#markup5ever@0.39.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Common code for xml5ever and html5ever","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tendril","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"web_atoms","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"markup5ever","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/markup5ever-0.39.0/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"serde":["web_atoms/serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/markup5ever-0.39.0/Cargo.toml","metadata":null,"publish":null,"authors":["The html5ever Project Developers"],"categories":["parser-implementations","web-programming"],"keywords":[],"readme":null,"repository":"https://github.com/servo/html5ever","homepage":null,"documentation":"https://docs.rs/markup5ever","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"matchit","version":"0.8.4","id":"registry+https://github.com/rust-lang/crates.io-index#matchit@0.8.4","license":"MIT AND BSD-3-Clause","license_file":null,"description":"A high performance, zero-copy URL router.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"actix-router","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"gonzales","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.0.3-beta","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.14","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"path-tree","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"regex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.5.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"route-recognizer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"routefinder","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"tower","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["make","util"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"matchit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"hyper","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/examples/hyper.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"insert","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/tests/insert.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"match","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/tests/match.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"remove","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/tests/remove.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"__test_helpers":[],"default":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/Cargo.toml","metadata":null,"publish":null,"authors":["Ibraheem Ahmed "],"categories":["network-programming","algorithms"],"keywords":["router","path","tree","match","url"],"readme":"README.md","repository":"https://github.com/ibraheemdev/matchit","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"memchr","version":"2.8.2","id":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","license":"Unlicense OR MIT","license_file":null,"description":"Provides extremely fast (uses SIMD on x86_64, aarch64 and wasm32) routines for\n1, 2 or 3 byte search and single substring search.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.20","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"memchr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/memchr-2.8.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"core":["dep:core"],"default":["std"],"libc":[],"logging":["dep:log"],"rustc-dep-of-std":["core"],"std":["alloc"],"use_std":["std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/memchr-2.8.2/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Andrew Gallant ","bluss"],"categories":[],"keywords":["memchr","memmem","substring","find","search"],"readme":"README.md","repository":"https://github.com/BurntSushi/memchr","homepage":"https://github.com/BurntSushi/memchr","documentation":"https://docs.rs/memchr/","edition":"2021","links":null,"default_run":null,"rust_version":"1.61"},{"name":"mimalloc","version":"0.1.52","id":"registry+https://github.com/rust-lang/crates.io-index#mimalloc@0.1.52","license":"MIT","license_file":null,"description":"Performance and security oriented drop-in allocator","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libmimalloc-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.49","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"mimalloc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mimalloc-0.1.52/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"debug":["libmimalloc-sys/debug"],"debug_in_debug":["libmimalloc-sys/debug_in_debug"],"default":[],"extended":["libmimalloc-sys/extended"],"local_dynamic_tls":["libmimalloc-sys/local_dynamic_tls"],"no_thp":["libmimalloc-sys/no_thp"],"override":["libmimalloc-sys/override"],"secure":["libmimalloc-sys/secure"],"v2":["libmimalloc-sys/v2"],"win_direct_tls":["libmimalloc-sys/win_direct_tls"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mimalloc-0.1.52/Cargo.toml","metadata":null,"publish":null,"authors":["Octavian Oncescu ","Vincent Rouillé ","Thom Chiovoloni "],"categories":["memory-management","api-bindings"],"keywords":["mimalloc","allocator","encrypted-heap","performance"],"readme":"README.md","repository":"https://github.com/purpleprotocol/mimalloc_rust","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"mime","version":"0.3.17","id":"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","license":"MIT OR Apache-2.0","license_file":null,"description":"Strongly Typed Mimes","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"mime","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"cmp","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/benches/cmp.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"fmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/benches/fmt.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"parse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/benches/parse.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/Cargo.toml","metadata":null,"publish":null,"authors":["Sean McArthur "],"categories":[],"keywords":["mime","media-extensions","media-types"],"readme":"README.md","repository":"https://github.com/hyperium/mime","homepage":null,"documentation":"https://docs.rs/mime","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"miniz_oxide","version":"0.9.1","id":"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.9.1","license":"MIT OR Zlib OR Apache-2.0","license_file":null,"description":"DEFLATE compression and decompression library rewritten in Rust based on miniz","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"adler2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"alloc","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive","alloc"],"target":null,"registry":null},{"name":"simd-adler32","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"miniz_oxide","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/miniz_oxide-0.9.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":["dep:alloc"],"block-boundary":[],"core":["dep:core"],"default":["with-alloc"],"rustc-dep-of-std":["core","alloc","adler2/rustc-dep-of-std"],"serde":["dep:serde"],"simd":["simd-adler32"],"simd-adler32":["dep:simd-adler32"],"std":["serde?/std"],"with-alloc":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/miniz_oxide-0.9.1/Cargo.toml","metadata":null,"publish":null,"authors":["Frommi ","oyvindln ","Rich Geldreich richgel99@gmail.com"],"categories":["compression"],"keywords":["zlib","miniz","deflate","encoding"],"readme":"Readme.md","repository":"https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide","homepage":"https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide","documentation":"https://docs.rs/miniz_oxide","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"mio","version":"1.2.1","id":"registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","license":"MIT","license_file":null,"description":"Lightweight non-blocking I/O.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.8","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.183","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(unix, target_os = \"hermit\", target_os = \"wasi\"))","registry":null},{"name":"wasi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"wasi\")","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Wdk_Foundation","Wdk_Storage_FileSystem","Wdk_System_IO","Win32_Foundation","Win32_Networking_WinSock","Win32_Storage_FileSystem","Win32_Security","Win32_System_IO","Win32_System_WindowsProgramming"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"mio","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"tcp_listenfd_server","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/examples/tcp_listenfd_server.rs","edition":"2021","required-features":["os-poll","net"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"tcp_server","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/examples/tcp_server.rs","edition":"2021","required-features":["os-poll","net"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"udp_server","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/examples/udp_server.rs","edition":"2021","required-features":["os-poll","net"],"doc":false,"doctest":false,"test":false}],"features":{"default":["log"],"log":["dep:log"],"net":[],"os-ext":["os-poll","windows-sys/Win32_System_Pipes","windows-sys/Win32_Security"],"os-poll":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--generate-link-to-definition"],"targets":["aarch64-apple-ios","aarch64-linux-android","wasm32-wasip1","x86_64-apple-darwin","x86_64-pc-windows-gnu","x86_64-pc-windows-msvc","x86_64-unknown-dragonfly","x86_64-unknown-freebsd","x86_64-unknown-illumos","x86_64-unknown-linux-gnu","x86_64-unknown-netbsd","x86_64-unknown-openbsd","x86_64-unknown-hermit"]}},"playground":{"features":["os-poll","os-ext","net"]}},"publish":null,"authors":["Carl Lerche ","Thomas de Zeeuw ","Tokio Contributors "],"categories":["asynchronous"],"keywords":["io","async","non-blocking"],"readme":"README.md","repository":"https://github.com/tokio-rs/mio","homepage":"https://github.com/tokio-rs/mio","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"new_debug_unreachable","version":"1.0.6","id":"registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","license":"MIT","license_file":null,"description":"panic in debug, intrinsics::unreachable() in release (fork of debug_unreachable)","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"debug_unreachable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/new_debug_unreachable-1.0.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"simple","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/new_debug_unreachable-1.0.6/examples/simple.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"check","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/new_debug_unreachable-1.0.6/tests/check.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/new_debug_unreachable-1.0.6/Cargo.toml","metadata":null,"publish":null,"authors":["Matt Brubeck ","Jonathan Reem "],"categories":["rust-patterns","no-std"],"keywords":["optimization","macro"],"readme":"README.md","repository":"https://github.com/mbrubeck/rust-debug-unreachable","homepage":null,"documentation":"https://docs.rs/new_debug_unreachable","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"notify","version":"8.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#notify@8.2.0","license":"CC0-1.0","license_file":null,"description":"Cross-platform filesystem notification library","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"crossbeam-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flume","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.17","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"notify-types","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.34.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"nix","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.29.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.39","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.10.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"kqueue","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonflybsd\", target_os = \"ios\"))","registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["os-ext"],"target":"cfg(any(target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonflybsd\", target_os = \"ios\"))","registry":null},{"name":"inotify","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"linux\", target_os = \"android\"))","registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["os-ext"],"target":"cfg(any(target_os = \"linux\", target_os = \"android\"))","registry":null},{"name":"trash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^5.2.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"windows\")","registry":null},{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.7.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"fsevent-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^4.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"kqueue","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["os-ext"],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.60.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_System_Threading","Win32_Foundation","Win32_Storage_FileSystem","Win32_Security","Win32_System_WindowsProgramming","Win32_System_IO"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"notify","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/notify-8.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"crossbeam-channel":["dep:crossbeam-channel"],"default":["macos_fsevent"],"flume":["dep:flume"],"fsevent-sys":["dep:fsevent-sys"],"kqueue":["dep:kqueue"],"macos_fsevent":["fsevent-sys"],"macos_kqueue":["kqueue","mio"],"mio":["dep:mio"],"serde":["notify-types/serde"],"serialization-compat-6":["notify-types/serialization-compat-6"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/notify-8.2.0/Cargo.toml","metadata":null,"publish":null,"authors":["Félix Saparelli ","Daniel Faust ","Aron Heinecke "],"categories":["filesystem"],"keywords":["events","filesystem","notify","watch"],"readme":"README.md","repository":"https://github.com/notify-rs/notify.git","homepage":"https://github.com/notify-rs/notify","documentation":"https://docs.rs/notify","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"notify-types","version":"2.0.0","id":"registry+https://github.com/rust-lang/crates.io-index#notify-types@2.0.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Types used by the notify crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.89","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"web-time","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.34.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rstest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.39","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"notify_types","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/notify-types-2.0.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"serde":["dep:serde"],"serialization-compat-6":[],"web-time":["dep:web-time"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/notify-types-2.0.0/Cargo.toml","metadata":null,"publish":null,"authors":["Daniel Faust "],"categories":["filesystem"],"keywords":["events","filesystem","notify","watch"],"readme":"README.md","repository":"https://github.com/notify-rs/notify.git","homepage":"https://github.com/notify-rs/notify","documentation":"https://docs.rs/notify-types","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"num-conv","version":"0.1.0","id":"registry+https://github.com/rust-lang/crates.io-index#num-conv@0.1.0","license":"MIT OR Apache-2.0","license_file":null,"description":"`num_conv` is a crate to convert between integer types without using `as` casts. This provides\nbetter certainty when refactoring, makes the exact behavior of code more explicit, and allows using\nturbofish syntax.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"num_conv","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-conv-0.1.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-conv-0.1.0/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Jacob Pratt "],"categories":["no-std","no-std::no-alloc","rust-patterns"],"keywords":["cast","extend","truncate","convert","integer"],"readme":"README.md","repository":"https://github.com/jhpratt/num-conv","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.57.0"},{"name":"num-traits","version":"0.2.19","id":"registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19","license":"MIT OR Apache-2.0","license_file":null,"description":"Numeric traits for generic mathematics","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"autocfg","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"num_traits","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-traits-0.2.19/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cast","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-traits-0.2.19/tests/cast.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-traits-0.2.19/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"i128":[],"libm":["dep:libm"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-traits-0.2.19/Cargo.toml","metadata":{"docs":{"rs":{"features":["std"],"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["The Rust Project Developers"],"categories":["algorithms","science","no-std"],"keywords":["mathematics","numerics"],"readme":"README.md","repository":"https://github.com/rust-num/num-traits","homepage":"https://github.com/rust-num/num-traits","documentation":"https://docs.rs/num-traits","edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"once_cell","version":"1.21.4","id":"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4","license":"MIT OR Apache-2.0","license_file":null,"description":"Single assignment cells and lazy values.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"critical-section","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"parking_lot_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.10","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"portable-atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"critical-section","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["std"],"target":null,"registry":null},{"name":"regex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.10.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"once_cell","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/bench.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"bench_acquire","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/bench_acquire.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"lazy_static","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/lazy_static.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"reentrant_init_deadlocks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/reentrant_init_deadlocks.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"regex","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/regex.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"test_synchronization","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/test_synchronization.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"it","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/tests/it/main.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"alloc":["race"],"atomic-polyfill":["critical-section"],"critical-section":["dep:critical-section","portable-atomic"],"default":["std"],"parking_lot":["dep:parking_lot_core"],"portable-atomic":["dep:portable-atomic"],"race":[],"std":["alloc"],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Aleksey Kladov "],"categories":["rust-patterns","memory-management"],"keywords":["lazy","static"],"readme":"README.md","repository":"https://github.com/matklad/once_cell","homepage":null,"documentation":"https://docs.rs/once_cell","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"parking_lot","version":"0.12.5","id":"registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5","license":"MIT OR Apache-2.0","license_file":null,"description":"More compact and efficient implementations of the standard synchronization primitives.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"lock_api","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.14","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"parking_lot_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.12","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"parking_lot","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"issue_203","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.5/tests/issue_203.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"issue_392","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.5/tests/issue_392.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"arc_lock":["lock_api/arc_lock"],"deadlock_detection":["parking_lot_core/deadlock_detection"],"default":[],"hardware-lock-elision":[],"nightly":["parking_lot_core/nightly","lock_api/nightly"],"owning_ref":["lock_api/owning_ref"],"send_guard":[],"serde":["lock_api/serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.5/Cargo.toml","metadata":{"docs":{"rs":{"features":["arc_lock","serde","deadlock_detection"],"rustdoc-args":["--generate-link-to-definition"]}},"playground":{"features":["arc_lock","serde","deadlock_detection"]}},"publish":null,"authors":["Amanieu d'Antras "],"categories":["concurrency"],"keywords":["mutex","condvar","rwlock","once","thread"],"readme":"README.md","repository":"https://github.com/Amanieu/parking_lot","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"parking_lot_core","version":"0.9.12","id":"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12","license":"MIT OR Apache-2.0","license_file":null,"description":"An advanced API for creating custom synchronization primitives.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"backtrace","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.60","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"petgraph","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"redox_syscall","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"redox\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.95","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"windows-link","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"parking_lot_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot_core-0.9.12/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot_core-0.9.12/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"backtrace":["dep:backtrace"],"deadlock_detection":["petgraph","backtrace"],"nightly":[],"petgraph":["dep:petgraph"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot_core-0.9.12/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Amanieu d'Antras "],"categories":["concurrency"],"keywords":["mutex","condvar","rwlock","once","thread"],"readme":null,"repository":"https://github.com/Amanieu/parking_lot","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"percent-encoding","version":"2.3.2","id":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Percent encoding and decoding","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"percent_encoding","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/percent-encoding-2.3.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["std"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/percent-encoding-2.3.2/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["The rust-url developers"],"categories":["no-std"],"keywords":[],"readme":null,"repository":"https://github.com/servo/rust-url/","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.51"},{"name":"phf","version":"0.13.1","id":"registry+https://github.com/rust-lang/crates.io-index#phf@0.13.1","license":"MIT","license_file":null,"description":"Runtime support for perfect hash function data structures","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"phf_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"phf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf-0.13.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":false}],"features":{"default":["std"],"macros":["phf_macros"],"phf_macros":["dep:phf_macros"],"serde":["dep:serde"],"std":["phf_shared/std","serde?/std"],"uncased":["phf_macros?/uncased","phf_shared/uncased"],"unicase":["phf_macros?/unicase","phf_shared/unicase"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf-0.13.1/Cargo.toml","metadata":{"docs":{"rs":{"features":["macros"]}},"playground":{"all-features":false,"default-features":true,"features":["macros"]}},"publish":null,"authors":["Steven Fackler "],"categories":["data-structures","no-std"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-phf/rust-phf","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.66"},{"name":"phf_codegen","version":"0.13.1","id":"registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.13.1","license":"MIT","license_file":null,"description":"Codegen library for PHF types","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"phf_generator","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"phf_codegen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_codegen-0.13.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_codegen-0.13.1/Cargo.toml","metadata":null,"publish":null,"authors":["Steven Fackler "],"categories":["data-structures"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-phf/rust-phf","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.66"},{"name":"phf_generator","version":"0.13.1","id":"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","license":"MIT","license_file":null,"description":"PHF generation logic","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"fastrand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"phf_generator","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_generator-0.13.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"gen_hash_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_generator-0.13.1/src/bin/gen_hash_test.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benches","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_generator-0.13.1/benches/benches.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_generator-0.13.1/Cargo.toml","metadata":null,"publish":null,"authors":["Steven Fackler "],"categories":["data-structures"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-phf/rust-phf","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.66"},{"name":"phf_shared","version":"0.13.1","id":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","license":"MIT","license_file":null,"description":"Support code shared by PHF libraries","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"siphasher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uncased","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.9","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unicase","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"phf_shared","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_shared-0.13.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":false}],"features":{"default":["std"],"std":[],"uncased":["dep:uncased"],"unicase":["dep:unicase"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_shared-0.13.1/Cargo.toml","metadata":null,"publish":null,"authors":["Steven Fackler "],"categories":["data-structures"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-phf/rust-phf","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.66"},{"name":"pin-project-lite","version":"0.2.17","id":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","license":"Apache-2.0 OR MIT","license_file":null,"description":"A lightweight version of pin-project written with declarative macros.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"static_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"pin_project_lite","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/compiletest.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"drop_order","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/drop_order.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"expandtest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/expandtest.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"proper_unpin","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/proper_unpin.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/test.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"]}},"cargo_check_external_types":{"allowed_external_types":[]}},"publish":null,"authors":[],"categories":["no-std","no-std::no-alloc","rust-patterns"],"keywords":["pin","macros"],"readme":"README.md","repository":"https://github.com/taiki-e/pin-project-lite","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.37"},{"name":"potential_utf","version":"0.1.5","id":"registry+https://github.com/rust-lang/crates.io-index#potential_utf@0.1.5","license":"Unicode-3.0","license_file":null,"description":"Unvalidated string and character types","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"writeable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"potential_utf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/potential_utf-0.1.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":["serde_core?/alloc","writeable/alloc","zerovec?/alloc"],"databake":["dep:databake"],"default":["alloc"],"serde":["dep:serde_core"],"writeable":["dep:writeable"],"zerovec":["dep:zerovec"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/potential_utf-0.1.5/Cargo.toml","metadata":null,"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["text-processing","encoding"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"powerfmt","version":"0.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","license":"MIT OR Apache-2.0","license_file":null,"description":" `powerfmt` is a library that provides utilities for formatting values. This crate makes it\n significantly easier to support filling to a minimum width with alignment, avoid heap\n allocation, and avoid repetitive calculations.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"powerfmt-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"powerfmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/powerfmt-0.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["std","macros"],"macros":["dep:powerfmt-macros"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/powerfmt-0.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","__powerfmt_docs","--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["Jacob Pratt "],"categories":["no-std","no-std::no-alloc","rust-patterns"],"keywords":["display","format","fmt","formatter","extension"],"readme":"README.md","repository":"https://github.com/jhpratt/powerfmt","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"precomputed-hash","version":"0.1.1","id":"registry+https://github.com/rust-lang/crates.io-index#precomputed-hash@0.1.1","license":"MIT","license_file":null,"description":"A library intending to be a base dependency to expose a precomputed hash","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"precomputed_hash","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/precomputed-hash-0.1.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/precomputed-hash-0.1.1/Cargo.toml","metadata":null,"publish":null,"authors":["Emilio Cobos Álvarez "],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/emilio/precomputed-hash","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"proc-macro2","version":"1.0.106","id":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","license":"MIT OR Apache-2.0","license_file":null,"description":"A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"unicode-ident","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"proc_macro2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"comments","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/comments.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"features","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/features.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"marker","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/marker.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_fmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/test_fmt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_size","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/test_size.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["proc-macro"],"nightly":[],"proc-macro":[],"span-locations":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/Cargo.toml","metadata":{"docs":{"rs":{"rustc-args":["--cfg=procmacro2_semver_exempt"],"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--cfg=procmacro2_semver_exempt","--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}},"playground":{"features":["span-locations"]}},"publish":null,"authors":["David Tolnay ","Alex Crichton "],"categories":["development-tools::procedural-macro-helpers"],"keywords":["macros","syn"],"readme":"README.md","repository":"https://github.com/dtolnay/proc-macro2","homepage":null,"documentation":"https://docs.rs/proc-macro2","edition":"2021","links":null,"default_run":null,"rust_version":"1.68"},{"name":"pulldown-cmark","version":"0.13.4","id":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark@0.13.4","license":"MIT","license_file":null,"description":"A pull parser for CommonMark","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"getopts","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pulldown-cmark-escape","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"unicase","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.6","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lazy_static","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"regex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.61","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"pulldown_cmark","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"pulldown-cmark","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/src/main.rs","edition":"2021","required-features":["getopts"],"doc":false,"doctest":false,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"broken-link-callbacks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/broken-link-callbacks.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"event-filter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/event-filter.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"events","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/events.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"footnote-rewrite","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/footnote-rewrite.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"normalize-wikilink","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/normalize-wikilink.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"parser-map-event-print","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/parser-map-event-print.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"parser-map-tag-print","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/parser-map-tag-print.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"string-to-string","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/string-to-string.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"errors","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/tests/errors.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"html","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/tests/html.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"lib","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/tests/lib.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/tests/serde.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["getopts","html"],"gen-tests":[],"getopts":["dep:getopts"],"html":["pulldown-cmark-escape"],"pulldown-cmark-escape":["dep:pulldown-cmark-escape"],"serde":["dep:serde"],"simd":["pulldown-cmark-escape?/simd"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/Cargo.toml","metadata":null,"publish":null,"authors":["Raph Levien ","Marcus Klaas de Vries "],"categories":["text-processing"],"keywords":["markdown","commonmark"],"readme":"README.md","repository":"https://github.com/raphlinus/pulldown-cmark","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.1"},{"name":"pulldown-cmark-escape","version":"0.11.0","id":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark-escape@0.11.0","license":"MIT","license_file":null,"description":"An escape library for HTML created in the pulldown-cmark project","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"pulldown_cmark_escape","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-escape-0.11.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"simd":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-escape-0.11.0/Cargo.toml","metadata":null,"publish":null,"authors":["Raph Levien ","Marcus Klaas de Vries "],"categories":["text-processing"],"keywords":["escape","html","markdown","commonmark"],"readme":"./README.md","repository":"https://github.com/raphlinus/pulldown-cmark","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.70"},{"name":"quote","version":"1.0.45","id":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","license":"MIT OR Apache-2.0","license_file":null,"description":"Quasi-quoting macro quote!(...)","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.80","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.108","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["diff"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"quote","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/tests/compiletest.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["proc-macro"],"proc-macro":["proc-macro2/proc-macro"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::procedural-macro-helpers"],"keywords":["macros","syn"],"readme":"README.md","repository":"https://github.com/dtolnay/quote","homepage":null,"documentation":"https://docs.rs/quote/","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"r-efi","version":"5.3.0","id":"registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0","license":"MIT OR Apache-2.0 OR LGPL-2.1-or-later","license_file":null,"description":"UEFI Reference Specification Protocol Constants and Definitions","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"r_efi","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"freestanding","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/examples/freestanding.rs","edition":"2018","required-features":["native"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gop-query","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/examples/gop-query.rs","edition":"2018","required-features":["native"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"hello-world","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/examples/hello-world.rs","edition":"2018","required-features":["native"],"doc":false,"doctest":false,"test":false}],"features":{"core":["dep:core"],"efiapi":[],"examples":["native"],"native":[],"rustc-dep-of-std":["core"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["embedded","hardware-support","no-std","os"],"keywords":["boot","efi","firmware","specification","uefi"],"readme":"README.md","repository":"https://github.com/r-efi/r-efi","homepage":"https://github.com/r-efi/r-efi/wiki","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.68"},{"name":"redox_syscall","version":"0.5.18","id":"registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.5.18","license":"MIT","license_file":null,"description":"A Rust library to access raw Redox system calls","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"loom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(loom)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"syscall","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/redox_syscall-0.5.18/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"core":["dep:core"],"default":["userspace"],"rustc-dep-of-std":["core","bitflags/rustc-dep-of-std"],"std":[],"userspace":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/redox_syscall-0.5.18/Cargo.toml","metadata":null,"publish":null,"authors":["Jeremy Soller "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://gitlab.redox-os.org/redox-os/syscall","homepage":null,"documentation":"https://docs.rs/redox_syscall","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"regex","version":"1.12.4","id":"registry+https://github.com/rust-lang/crates.io-index#regex@1.12.4","license":"MIT OR Apache-2.0","license_file":null,"description":"An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"aho-corasick","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"regex-automata","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.12","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc","syntax","meta","nfa-pikevm"],"target":null,"registry":null},{"name":"regex-syntax","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.11","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.69","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["atty","humantime","termcolor"],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"regex-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"regex","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.12.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"integration","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.12.4/tests/lib.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std","perf","unicode","regex-syntax/default"],"logging":["aho-corasick?/logging","memchr?/logging","regex-automata/logging"],"pattern":[],"perf":["perf-cache","perf-dfa","perf-onepass","perf-backtrack","perf-inline","perf-literal"],"perf-backtrack":["regex-automata/nfa-backtrack"],"perf-cache":[],"perf-dfa":["regex-automata/hybrid"],"perf-dfa-full":["regex-automata/dfa-build","regex-automata/dfa-search"],"perf-inline":["regex-automata/perf-inline"],"perf-literal":["dep:aho-corasick","dep:memchr","regex-automata/perf-literal"],"perf-onepass":["regex-automata/dfa-onepass"],"std":["aho-corasick?/std","memchr?/std","regex-automata/std","regex-syntax/std"],"unicode":["unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment","regex-automata/unicode","regex-syntax/unicode"],"unicode-age":["regex-automata/unicode-age","regex-syntax/unicode-age"],"unicode-bool":["regex-automata/unicode-bool","regex-syntax/unicode-bool"],"unicode-case":["regex-automata/unicode-case","regex-syntax/unicode-case"],"unicode-gencat":["regex-automata/unicode-gencat","regex-syntax/unicode-gencat"],"unicode-perl":["regex-automata/unicode-perl","regex-automata/unicode-word-boundary","regex-syntax/unicode-perl"],"unicode-script":["regex-automata/unicode-script","regex-syntax/unicode-script"],"unicode-segment":["regex-automata/unicode-segment","regex-syntax/unicode-segment"],"unstable":["pattern"],"use_std":["std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.12.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs_regex"]}}},"publish":null,"authors":["The Rust Project Developers","Andrew Gallant "],"categories":["text-processing"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/regex","homepage":"https://github.com/rust-lang/regex","documentation":"https://docs.rs/regex","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"regex-automata","version":"0.4.14","id":"registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.14","license":"MIT OR Apache-2.0","license_file":null,"description":"Automata construction and matching using regular expressions.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"aho-corasick","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.14","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"regex-syntax","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.5","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.69","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bstr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["atty","humantime","termcolor"],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"regex-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"regex_automata","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-automata-0.4.14/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"integration","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-automata-0.4.14/tests/lib.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"alloc":[],"default":["std","syntax","perf","unicode","meta","nfa","dfa","hybrid"],"dfa":["dfa-build","dfa-search","dfa-onepass"],"dfa-build":["nfa-thompson","dfa-search"],"dfa-onepass":["nfa-thompson"],"dfa-search":[],"hybrid":["alloc","nfa-thompson"],"internal-instrument":["internal-instrument-pikevm"],"internal-instrument-pikevm":["logging","std"],"logging":["dep:log","aho-corasick?/logging","memchr?/logging"],"meta":["syntax","nfa-pikevm"],"nfa":["nfa-thompson","nfa-pikevm","nfa-backtrack"],"nfa-backtrack":["nfa-thompson"],"nfa-pikevm":["nfa-thompson"],"nfa-thompson":["alloc"],"perf":["perf-inline","perf-literal"],"perf-inline":[],"perf-literal":["perf-literal-substring","perf-literal-multisubstring"],"perf-literal-multisubstring":["dep:aho-corasick"],"perf-literal-substring":["aho-corasick?/perf-literal","dep:memchr"],"std":["regex-syntax?/std","memchr?/std","aho-corasick?/std","alloc"],"syntax":["dep:regex-syntax","alloc"],"unicode":["unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment","unicode-word-boundary","regex-syntax?/unicode"],"unicode-age":["regex-syntax?/unicode-age"],"unicode-bool":["regex-syntax?/unicode-bool"],"unicode-case":["regex-syntax?/unicode-case"],"unicode-gencat":["regex-syntax?/unicode-gencat"],"unicode-perl":["regex-syntax?/unicode-perl"],"unicode-script":["regex-syntax?/unicode-script"],"unicode-segment":["regex-syntax?/unicode-segment"],"unicode-word-boundary":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-automata-0.4.14/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs_regex"]}}},"publish":null,"authors":["The Rust Project Developers","Andrew Gallant "],"categories":["text-processing"],"keywords":["regex","dfa","automata","automaton","nfa"],"readme":"README.md","repository":"https://github.com/rust-lang/regex","homepage":"https://github.com/rust-lang/regex/tree/master/regex-automata","documentation":"https://docs.rs/regex-automata","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"regex-syntax","version":"0.8.11","id":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","license":"MIT OR Apache-2.0","license_file":null,"description":"A regular expression parser.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"regex_syntax","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-syntax-0.8.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-syntax-0.8.11/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"arbitrary":["dep:arbitrary"],"default":["std","unicode"],"std":[],"unicode":["unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment"],"unicode-age":[],"unicode-bool":[],"unicode-case":[],"unicode-gencat":[],"unicode-perl":[],"unicode-script":[],"unicode-segment":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-syntax-0.8.11/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs_regex"]}}},"publish":null,"authors":["The Rust Project Developers","Andrew Gallant "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/regex","homepage":"https://github.com/rust-lang/regex/tree/master/regex-syntax","documentation":"https://docs.rs/regex-syntax","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"rust_decimal","version":"1.42.1","id":"registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.42.1","license":"MIT","license_file":null,"description":"Decimal number implementation written in pure Rust suitable for financial and fixed-precision calculations.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"arrayvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"borsh","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive","unstable__schema"],"target":null,"registry":null},{"name":"bytemuck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.17.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bytemuck_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.7.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"diesel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"ndarray","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.15.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"num-traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["i128"],"target":null,"registry":null},{"name":"postgres-types","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"proptest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":null,"rename":"rand-0_9","optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rkyv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.46","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["size_32","std"],"target":null,"registry":null},{"name":"rocket","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0-rc.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rust_decimal_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tokio-postgres","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zmij","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"csv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"version-sync","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["html_root_url_updated","markdown_deps_updated"],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"diesel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["mysql","postgres"],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"postgres","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.19","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["getrandom"],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":"rand-0_9","optional":false,"uses_default_features":false,"features":["thread_rng"],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"rkyv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.13","kind":"dev","rename":"rkyv-0_8","optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["macros","rt-multi-thread","test-util"],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"tokio-postgres","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"rust_decimal","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"rkyv-remote","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/examples/rkyv-remote.rs","edition":"2021","required-features":["macros"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"decimal_tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/tests/decimal_tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/tests/macros.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"version-numbers","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/tests/version-numbers.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"wasm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/tests/wasm.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"comparison","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/benches/comparison.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"lib_benches","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/benches/lib_benches.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"align16":[],"borsh":["dep:borsh","std"],"bytemuck":["c-repr","dep:bytemuck","dep:bytemuck_derive"],"c-repr":[],"db-diesel-mysql":["diesel/mysql_backend","std"],"db-diesel-postgres":["diesel/postgres_backend","std"],"db-diesel2-mysql":["db-diesel-mysql"],"db-diesel2-postgres":["db-diesel-postgres"],"db-postgres":["dep:bytes","dep:postgres-types","std"],"db-tokio-postgres":["dep:bytes","dep:postgres-types","std"],"default":["serde","std"],"diesel":["dep:diesel"],"legacy-ops":[],"macros":["dep:rust_decimal_macros"],"maths":[],"maths-nopanic":["maths"],"ndarray":["dep:ndarray"],"proptest":["dep:proptest"],"rand":["dep:rand"],"rand-0_9":["dep:rand-0_9"],"rkyv":["dep:rkyv"],"rkyv-safe":["rkyv/validation"],"rocket-traits":["dep:rocket","std"],"rust-fuzz":["dep:arbitrary"],"serde":["dep:serde","wasm-bindgen?/serde"],"serde-arbitrary-precision":["serde-with-arbitrary-precision"],"serde-bincode":["serde-str"],"serde-float":["serde-with-float"],"serde-str":["serde-with-str"],"serde-with-arbitrary-precision":["serde","serde_json/arbitrary_precision","serde_json/std","zmij"],"serde-with-float":["serde"],"serde-with-str":["serde"],"serde_json":["dep:serde_json"],"std":["arrayvec/std","wasm-bindgen?/std","borsh?/std","bytes?/std","rand?/std","rkyv?/std","serde?/std","serde_json?/std"],"tokio-pg":["db-tokio-postgres"],"tokio-postgres":["dep:tokio-postgres"],"wasm":["dep:wasm-bindgen"],"zmij":["dep:zmij"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"],"targets":["x86_64-unknown-linux-gnu","wasm32-unknown-unknown"]}}},"publish":null,"authors":["Paul Mason "],"categories":["science","mathematics","data-structures"],"keywords":["decimal","financial","fixed","precision","number"],"readme":"README.md","repository":"https://github.com/paupino/rust-decimal","homepage":null,"documentation":"https://docs.rs/rust_decimal/","edition":"2021","links":null,"default_run":null,"rust_version":"1.67.1"},{"name":"rustix","version":"1.1.4","id":"registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.4","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"Safe Rust bindings to POSIX/Unix/Linux/Winsock-like syscalls","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.171","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.10","kind":"dev","rename":"libc_errno","optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"memoffset","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serial_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"static_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"linux-raw-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["general","ioctl","no_std"],"target":"cfg(all(any(target_os = \"linux\", target_os = \"android\"), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))","registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(criterion, not(any(target_os = \"emscripten\", target_os = \"wasi\"))))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.182","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))","registry":null},{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.10","kind":null,"rename":"libc_errno","optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))","registry":null},{"name":"linux-raw-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["auxvec","general","errno","ioctl","no_std","elf"],"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.182","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))","registry":null},{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.10","kind":null,"rename":"libc_errno","optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))","registry":null},{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.10","kind":null,"rename":"libc_errno","optional":false,"uses_default_features":false,"features":[],"target":"cfg(windows)","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.52, <0.62","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_Networking_WinSock"],"target":"cfg(windows)","registry":null},{"name":"once_cell","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.20.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"rustix","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.4/benches/mod.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.4/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"all-apis":["event","fs","io_uring","mm","mount","net","param","pipe","process","pty","rand","runtime","shm","stdio","system","termios","thread","time"],"alloc":[],"core":["dep:core"],"default":["std"],"event":[],"fs":[],"io_uring":["event","fs","net","thread","linux-raw-sys/io_uring"],"libc":["dep:libc"],"libc_errno":["dep:libc_errno"],"linux_4_11":[],"linux_5_1":["linux_4_11"],"linux_5_11":["linux_5_1"],"linux_latest":["linux_5_11"],"mm":[],"mount":[],"net":["linux-raw-sys/net","linux-raw-sys/netlink","linux-raw-sys/if_ether","linux-raw-sys/xdp"],"param":[],"pipe":[],"process":["linux-raw-sys/prctl"],"pty":["fs"],"rand":[],"runtime":["linux-raw-sys/prctl"],"rustc-dep-of-std":["core","rustc-std-workspace-alloc","linux-raw-sys/rustc-dep-of-std","bitflags/rustc-dep-of-std"],"rustc-std-workspace-alloc":["dep:rustc-std-workspace-alloc"],"shm":["fs"],"std":["bitflags/std","alloc","libc?/std","libc_errno?/std"],"stdio":[],"system":["linux-raw-sys/system"],"termios":[],"thread":["linux-raw-sys/prctl"],"time":[],"try_close":[],"use-explicitly-provided-auxv":[],"use-libc":["libc_errno","libc"],"use-libc-auxv":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.4/Cargo.toml","metadata":{"docs":{"rs":{"features":["all-apis"],"targets":["x86_64-unknown-linux-gnu","i686-unknown-linux-gnu","x86_64-apple-darwin","x86_64-pc-windows-msvc","x86_64-unknown-freebsd","x86_64-unknown-openbsd","x86_64-unknown-netbsd","x86_64-unknown-dragonfly","x86_64-unknown-illumos","x86_64-unknown-redox","x86_64-unknown-haiku","wasm32-unknown-emscripten","wasm32-wasip1"]}}},"publish":null,"authors":["Dan Gohman ","Jakub Konka "],"categories":["os::unix-apis","date-and-time","filesystem","network-programming"],"keywords":["api","file","network","safe","syscall"],"readme":"README.md","repository":"https://github.com/bytecodealliance/rustix","homepage":null,"documentation":"https://docs.rs/rustix","edition":"2021","links":null,"default_run":null,"rust_version":"1.63","hints":{"mostly-unused":true}},{"name":"rustversion","version":"1.0.22","id":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","license":"MIT OR Apache-2.0","license_file":null,"description":"Conditional compilation according to rustc compiler version","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.49","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["diff"],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"rustversion","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/tests/compiletest.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_const","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/tests/test_const.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_eval","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/tests/test_eval.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/tests/test_parse.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/build/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::build-utils","no-std","no-std::no-alloc"],"keywords":[],"readme":"README.md","repository":"https://github.com/dtolnay/rustversion","homepage":null,"documentation":"https://docs.rs/rustversion","edition":"2018","links":null,"default_run":null,"rust_version":"1.31"},{"name":"ryu","version":"1.0.23","id":"registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.23","license":"Apache-2.0 OR BSL-1.0","license_file":null,"description":"Fast floating point to string conversion","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"no-panic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num_cpus","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_xorshift","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"ryu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"upstream_benchmark","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/examples/upstream_benchmark.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"common_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/common_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"d2s_intrinsics_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/d2s_intrinsics_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"d2s_table_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/d2s_table_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"d2s_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/d2s_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"exhaustive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/exhaustive.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"f2s_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/f2s_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"s2d_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/s2d_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"s2f_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/s2f_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"no-panic":["dep:no-panic"],"small":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["value-formatting","no-std","no-std::no-alloc"],"keywords":["float"],"readme":"README.md","repository":"https://github.com/dtolnay/ryu","homepage":null,"documentation":"https://docs.rs/ryu","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"same-file","version":"1.0.6","id":"registry+https://github.com/rust-lang/crates.io-index#same-file@1.0.6","license":"Unlicense/MIT","license_file":null,"description":"A simple crate for determining whether two file paths point to the same file.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"winapi-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"same_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/same-file-1.0.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"is_same_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/same-file-1.0.6/examples/is_same_file.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"is_stderr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/same-file-1.0.6/examples/is_stderr.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/same-file-1.0.6/Cargo.toml","metadata":null,"publish":null,"authors":["Andrew Gallant "],"categories":[],"keywords":["same","file","equal","inode"],"readme":"README.md","repository":"https://github.com/BurntSushi/same-file","homepage":"https://github.com/BurntSushi/same-file","documentation":"https://docs.rs/same-file","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"scopeguard","version":"1.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A RAII scope guard that will run a given closure when it goes out of scope,\neven if the code between panics (assuming unwinding panic).\n\nDefines the macros `defer!`, `defer_on_unwind!`, `defer_on_success!` as\nshorthands for guards with one of the implemented strategies.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"scopeguard","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scopeguard-1.2.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"readme","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scopeguard-1.2.0/examples/readme.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{"default":["use_std"],"use_std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scopeguard-1.2.0/Cargo.toml","metadata":{"release":{"no-dev-version":true}},"publish":null,"authors":["bluss"],"categories":["rust-patterns","no-std"],"keywords":["scope-guard","defer","panic","unwind"],"readme":"README.md","repository":"https://github.com/bluss/scopeguard","homepage":null,"documentation":"https://docs.rs/scopeguard/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"scratch","version":"1.0.9","id":"registry+https://github.com/rust-lang/crates.io-index#scratch@1.0.9","license":"MIT OR Apache-2.0","license_file":null,"description":"Compile-time temporary directory shared by multiple crates and erased by `cargo clean`","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"scratch","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scratch-1.0.9/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scratch-1.0.9/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scratch-1.0.9/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::build-utils","filesystem"],"keywords":[],"readme":"README.md","repository":"https://github.com/dtolnay/scratch","homepage":null,"documentation":"https://docs.rs/scratch","edition":"2015","links":null,"default_run":null,"rust_version":"1.0"},{"name":"serde","version":"1.0.228","id":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","license":"MIT OR Apache-2.0","license_file":null,"description":"A generic serialization/deserialization framework","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.228","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["result"],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde_core/alloc"],"default":["std"],"derive":["serde_derive"],"rc":["serde_core/rc"],"serde_derive":["dep:serde_derive"],"std":["serde_core/std"],"unstable":["serde_core/unstable"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/Cargo.toml","metadata":{"playground":{"features":["derive","rc"]},"docs":{"rs":{"features":["derive","rc","unstable"],"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["Erick Tryzelaar ","David Tolnay "],"categories":["encoding","no-std","no-std::no-alloc"],"keywords":["serde","serialization","no_std"],"readme":"crates-io.md","repository":"https://github.com/serde-rs/serde","homepage":"https://serde.rs","documentation":"https://docs.rs/serde","edition":"2021","links":null,"default_run":null,"rust_version":"1.56"},{"name":"serde_core","version":"1.0.228","id":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","license":"MIT OR Apache-2.0","license_file":null,"description":"Serde traits only, with no support for derive -- use the `serde` crate instead","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.228","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any())","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"serde_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"default":["std","result"],"rc":[],"result":[],"std":[],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/Cargo.toml","metadata":{"playground":{"features":["rc","result"]},"docs":{"rs":{"features":["rc","result","unstable"],"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["Erick Tryzelaar ","David Tolnay "],"categories":["encoding","no-std","no-std::no-alloc"],"keywords":["serde","serialization","no_std"],"readme":"README.md","repository":"https://github.com/serde-rs/serde","homepage":"https://serde.rs","documentation":"https://docs.rs/serde_core","edition":"2021","links":null,"default_run":null,"rust_version":"1.56"},{"name":"serde_derive","version":"1.0.228","id":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","license":"MIT OR Apache-2.0","license_file":null,"description":"Macros 1.1 implementation of #[derive(Serialize, Deserialize)]","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.74","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["proc-macro"],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["proc-macro"],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.81","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["clone-impls","derive","parsing","printing","proc-macro"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"serde_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_derive-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"default":[],"deserialize_in_place":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_derive-1.0.228/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}}},"publish":null,"authors":["Erick Tryzelaar ","David Tolnay "],"categories":["no-std","no-std::no-alloc"],"keywords":["serde","serialization","no_std","derive"],"readme":"crates-io.md","repository":"https://github.com/serde-rs/serde","homepage":"https://serde.rs","documentation":"https://serde.rs/derive.html","edition":"2021","links":null,"default_run":null,"rust_version":"1.61"},{"name":"serde_json","version":"1.0.150","id":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","license":"MIT OR Apache-2.0","license_file":null,"description":"A JSON serialization file format","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"indexmap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zmij","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"automod","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.11","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"indoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ref-cast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.18","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.194","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.166","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_stacker","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.108","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["diff"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any())","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"serde_json","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/compiletest.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"debug","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/debug.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"lexical","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/lexical.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"map","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/map.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"regression","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/regression.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde_core/alloc"],"arbitrary_precision":[],"default":["std"],"float_roundtrip":[],"indexmap":["dep:indexmap"],"preserve_order":["indexmap","std"],"raw_value":[],"std":["memchr/std","serde_core/std"],"unbounded_depth":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/Cargo.toml","metadata":{"docs":{"rs":{"features":["preserve_order","raw_value","unbounded_depth"],"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}},"playground":{"features":["float_roundtrip","raw_value","unbounded_depth"]}},"publish":null,"authors":["Erick Tryzelaar ","David Tolnay "],"categories":["encoding","parser-implementations","no-std"],"keywords":["json","serde","serialization"],"readme":"README.md","repository":"https://github.com/serde-rs/json","homepage":null,"documentation":"https://docs.rs/serde_json","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"serde_path_to_error","version":"0.1.20","id":"registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.20","license":"MIT OR Apache-2.0","license_file":null,"description":"Path to the element that failed to deserialize","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.100","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any())","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"serde_path_to_error","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_path_to_error-0.1.20/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"deserialize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_path_to_error-0.1.20/tests/deserialize.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serialize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_path_to_error-0.1.20/tests/serialize.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_path_to_error-0.1.20/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["encoding","no-std"],"keywords":["serde","serialization"],"readme":"README.md","repository":"https://github.com/dtolnay/path-to-error","homepage":null,"documentation":"https://docs.rs/serde_path_to_error","edition":"2021","links":null,"default_run":null,"rust_version":"1.61"},{"name":"sha2","version":"0.10.9","id":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","license":"MIT OR Apache-2.0","license_file":null,"description":"Pure Rust implementation of the SHA-2 hash function family\nincluding SHA-224, SHA-256, SHA-384, and SHA-512.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"digest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.7","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"digest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["dev"],"target":null,"registry":null},{"name":"hex-literal","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cpufeatures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))","registry":null},{"name":"sha2-asm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"sha2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sha2-0.10.9/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sha2-0.10.9/tests/mod.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sha2-0.10.9/benches/mod.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"asm":["sha2-asm"],"asm-aarch64":["asm"],"compress":[],"default":["std"],"force-soft":[],"force-soft-compact":[],"loongarch64_asm":[],"oid":["digest/oid"],"sha2-asm":["dep:sha2-asm"],"std":["digest/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sha2-0.10.9/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["RustCrypto Developers"],"categories":["cryptography","no-std"],"keywords":["crypto","sha2","hash","digest"],"readme":"README.md","repository":"https://github.com/RustCrypto/hashes","homepage":null,"documentation":"https://docs.rs/sha2","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"shlex","version":"2.0.1","id":"registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Split a string into shell words, like Python's shlex.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"shlex","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/shlex-2.0.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/shlex-2.0.1/Cargo.toml","metadata":null,"publish":null,"authors":["comex ","Fenhl ","Adrian Taylor ","Alex Touchet ","Daniel Parks ","Garrett Berg "],"categories":["command-line-interface","parser-implementations"],"keywords":[],"readme":"README.md","repository":"https://github.com/comex/rust-shlex","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.46.0"},{"name":"signal-hook-registry","version":"1.4.8","id":"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.8","license":"MIT OR Apache-2.0","license_file":null,"description":"Backend crate for signal-hook","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.2, <0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"signal-hook","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"signal_hook_registry","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/signal-hook-registry-1.4.8/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unregister_signal","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/signal-hook-registry-1.4.8/tests/unregister_signal.rs","edition":"2015","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/signal-hook-registry-1.4.8/Cargo.toml","metadata":null,"publish":null,"authors":["Michal 'vorner' Vaner ","Masaki Hara "],"categories":[],"keywords":["signal","unix","daemon"],"readme":"README.md","repository":"https://github.com/vorner/signal-hook","homepage":null,"documentation":"https://docs.rs/signal-hook-registry","edition":"2015","links":null,"default_run":null,"rust_version":"1.26"},{"name":"simd-adler32","version":"0.3.10","id":"registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.10","license":"MIT","license_file":null,"description":"A SIMD-accelerated Adler-32 hash algorithm implementation.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"adler2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"adler32","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"simd_adler32","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/simd-adler32-0.3.10/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"const-generics":[],"default":["std","const-generics"],"nightly":[],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/simd-adler32-0.3.10/Cargo.toml","metadata":null,"publish":null,"authors":["Marvin Countryman "],"categories":["algorithms","no-std"],"keywords":["simd","avx2","ssse3","adler","adler32"],"readme":"README.md","repository":"https://github.com/mcountryman/simd-adler32","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"siphasher","version":"1.0.3","id":"registry+https://github.com/rust-lang/crates.io-index#siphasher@1.0.3","license":"MIT/Apache-2.0","license_file":null,"description":"SipHash-2-4, SipHash-1-3 and 128-bit variants in pure Rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"siphasher","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/siphasher-1.0.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"default":["std"],"serde":["dep:serde"],"serde_json":["dep:serde_json"],"serde_no_std":["serde/alloc"],"serde_std":["std","serde/std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/siphasher-1.0.3/Cargo.toml","metadata":null,"publish":null,"authors":["Frank Denis "],"categories":["algorithms","cryptography"],"keywords":["crypto","hash","siphash"],"readme":"README.md","repository":"https://github.com/jedisct1/rust-siphash","homepage":"https://docs.rs/siphasher","documentation":"https://docs.rs/siphasher","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"slab","version":"0.4.12","id":"registry+https://github.com/rust-lang/crates.io-index#slab@0.4.12","license":"MIT","license_file":null,"description":"Pre-allocated storage for a uniform data type","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.95","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"slab","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/slab-0.4.12/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/slab-0.4.12/tests/serde.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"slab","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/slab-0.4.12/tests/slab.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"serde":["dep:serde"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/slab-0.4.12/Cargo.toml","metadata":null,"publish":null,"authors":["Carl Lerche "],"categories":["memory-management","data-structures","no-std"],"keywords":["slab","allocator","no_std"],"readme":"README.md","repository":"https://github.com/tokio-rs/slab","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.51"},{"name":"smallvec","version":"1.15.2","id":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","license":"MIT OR Apache-2.0","license_file":null,"description":"'Small vector' optimization: store up to a small number of items on the stack","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"malloc_size_of","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unty","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.0.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.1","kind":"dev","rename":"bincode1","optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"debugger_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"debugger_test_parser","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"smallvec","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/smallvec-1.15.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"arbitrary":["dep:arbitrary"],"bincode":["dep:bincode"],"const_generics":[],"const_new":["const_generics"],"debugger_visualizer":[],"drain_filter":[],"drain_keep_rest":["drain_filter"],"impl_bincode":["bincode","unty"],"malloc_size_of":["dep:malloc_size_of"],"may_dangle":[],"serde":["dep:serde"],"specialization":[],"union":[],"unty":["dep:unty"],"write":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/smallvec-1.15.2/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--generate-link-to-definition"]}}},"publish":null,"authors":["The Servo Project Developers"],"categories":["data-structures"],"keywords":["small","vec","vector","stack","no_std"],"readme":"README.md","repository":"https://github.com/servo/rust-smallvec","homepage":null,"documentation":"https://docs.rs/smallvec/","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"socket2","version":"0.6.5","id":"registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.5","license":"MIT OR Apache-2.0","license_file":null,"description":"Utilities for handling networking sockets with a maximal amount of configuration\npossible intended.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.172","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(unix, target_os = \"wasi\"))","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.60, <0.62","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_Networking_WinSock","Win32_System_IO","Win32_System_Threading","Win32_System_WindowsProgramming"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"socket2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/socket2-0.6.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"all":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/socket2-0.6.5/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"default-target":"x86_64-unknown-linux-gnu","targets":["aarch64-apple-ios","aarch64-linux-android","armv7-linux-androideabi","i686-linux-android","x86_64-apple-darwin","x86_64-pc-solaris","x86_64-pc-windows-msvc","x86_64-unknown-freebsd","x86_64-unknown-fuchsia","x86_64-unknown-illumos","x86_64-unknown-linux-gnu","x86_64-unknown-linux-musl","x86_64-unknown-netbsd","x86_64-unknown-redox"]}},"playground":{"features":["all"]},"cargo_check_external_types":{"allowed_external_types":["libc::socklen_t","libc::*::socklen_t","libc::sa_family_t","libc::*::sa_family_t","windows_sys::Win32::Networking::WinSock::socklen_t","windows_sys::Win32::Networking::WinSock::ADDRESS_FAMILY"]}},"publish":null,"authors":["Alex Crichton ","Thomas de Zeeuw "],"categories":["api-bindings","network-programming"],"keywords":["io","socket","network"],"readme":"README.md","repository":"https://github.com/rust-lang/socket2","homepage":"https://github.com/rust-lang/socket2","documentation":"https://docs.rs/socket2","edition":"2021","links":null,"default_run":null,"rust_version":"1.70"},{"name":"stable_deref_trait","version":"1.2.1","id":"registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1","license":"MIT OR Apache-2.0","license_file":null,"description":"An unsafe marker trait for types like Box and Rc that dereference to a stable address even when moved, and hence can be used with libraries such as owning_ref and rental.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"stable_deref_trait","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/stable_deref_trait-1.2.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["std"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/stable_deref_trait-1.2.1/Cargo.toml","metadata":null,"publish":null,"authors":["Robert Grosse "],"categories":["memory-management","no-std"],"keywords":[],"readme":"README.md","repository":"https://github.com/storyyeller/stable_deref_trait","homepage":null,"documentation":"https://docs.rs/stable_deref_trait/1.2.1/stable_deref_trait","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"streaming-iterator","version":"0.1.9","id":"registry+https://github.com/rust-lang/crates.io-index#streaming-iterator@0.1.9","license":"MIT OR Apache-2.0","license_file":null,"description":"Streaming iterators","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"streaming_iterator","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/streaming-iterator-0.1.9/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/streaming-iterator-0.1.9/Cargo.toml","metadata":{"docs":{"rs":{"features":["std"]}}},"publish":null,"authors":["Steven Fackler "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/sfackler/streaming-iterator","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.56"},{"name":"string_cache","version":"0.9.0","id":"registry+https://github.com/rust-lang/crates.io-index#string_cache@0.9.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A string interning library for Rust, developed as part of the Servo project.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"malloc_size_of","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"new_debug_unreachable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"parking_lot","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"precomputed-hash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"string_cache","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.9.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"simple","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.9.0/examples/simple.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"small-stack","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.9.0/tests/small-stack.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"default":["serde_support"],"malloc_size_of":["dep:malloc_size_of"],"serde":["dep:serde"],"serde_support":["serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.9.0/Cargo.toml","metadata":null,"publish":null,"authors":["The Servo Project Developers"],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/servo/string-cache","homepage":null,"documentation":"https://docs.rs/string_cache","edition":"2018","links":null,"default_run":null,"rust_version":"1.70.0"},{"name":"string_cache_codegen","version":"0.6.1","id":"registry+https://github.com/rust-lang/crates.io-index#string_cache_codegen@0.6.1","license":"MIT OR Apache-2.0","license_file":null,"description":"A codegen library for string-cache, developed as part of the Servo project.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"phf_generator","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"string_cache_codegen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache_codegen-0.6.1/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache_codegen-0.6.1/Cargo.toml","metadata":null,"publish":null,"authors":["The Servo Project Developers"],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/servo/string-cache","homepage":null,"documentation":"https://docs.rs/string_cache_codegen/","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"syn","version":"2.0.117","id":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","license":"MIT OR Apache-2.0","license_file":null,"description":"Parser for Rust source code","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.91","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unicode-ident","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"automod","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ref-cast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn-test-suite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"termcolor","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"reqwest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["blocking"],"target":"cfg(not(miri))","registry":null},{"name":"tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.16","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"syn","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"regression","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/regression.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_asyncness","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_asyncness.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_attribute","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_attribute.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_derive_input","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_derive_input.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_expr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_expr.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_generics","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_generics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_grouping","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_grouping.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_ident","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_ident.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_item","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_item.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_lit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_lit.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_meta","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_meta.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_parse_buffer.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_quote","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_parse_quote.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_parse_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_pat","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_pat.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_path","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_path.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_precedence","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_precedence.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_punctuated","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_punctuated.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_receiver","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_receiver.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_round_trip","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_round_trip.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_shebang","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_shebang.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_size","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_size.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_stmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_stmt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_token_trees","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_token_trees.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_ty","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_ty.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_unparenthesize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_unparenthesize.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_visibility","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_visibility.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"zzz_stable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/zzz_stable.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/benches/file.rs","edition":"2021","required-features":["full","parsing"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"rust","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/benches/rust.rs","edition":"2021","required-features":["full","parsing"],"doc":false,"doctest":false,"test":false}],"features":{"clone-impls":[],"default":["derive","parsing","printing","clone-impls","proc-macro"],"derive":[],"extra-traits":[],"fold":[],"full":[],"parsing":[],"printing":["dep:quote"],"proc-macro":["proc-macro2/proc-macro","quote?/proc-macro"],"test":["syn-test-suite/all-features"],"visit":[],"visit-mut":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extend-css=src/gen/token.css","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}},"playground":{"features":["full","visit","visit-mut","fold","extra-traits"]}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::procedural-macro-helpers","parser-implementations"],"keywords":["macros","syn"],"readme":"README.md","repository":"https://github.com/dtolnay/syn","homepage":null,"documentation":"https://docs.rs/syn","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"syn","version":"3.0.3","id":"registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3","license":"MIT OR Apache-2.0","license_file":null,"description":"Parser for Rust source code","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.91","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unicode-ident","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"automod","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ref-cast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn-test-suite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"termcolor","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"reqwest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["blocking"],"target":"cfg(not(miri))","registry":null},{"name":"tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.16","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"syn","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"regression","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/regression.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_attribute","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_attribute.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_derive_input","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_derive_input.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_expr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_expr.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_generics","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_generics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_grouping","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_grouping.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_ident","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_ident.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_item","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_item.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_lit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_lit.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_meta","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_meta.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_parse_buffer.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_quote","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_parse_quote.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_parse_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_pat","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_pat.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_path","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_path.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_precedence","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_precedence.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_punctuated","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_punctuated.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_receiver","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_receiver.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_round_trip","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_round_trip.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_shebang","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_shebang.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_size","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_size.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_stmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_stmt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_token_trees","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_token_trees.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_ty","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_ty.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_unparenthesize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_unparenthesize.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_visibility","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_visibility.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"zzz_stable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/zzz_stable.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/benches/file.rs","edition":"2021","required-features":["full","parsing"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"rust","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/benches/rust.rs","edition":"2021","required-features":["full","parsing"],"doc":false,"doctest":false,"test":false}],"features":{"clone-impls":[],"default":["derive","parsing","printing","clone-impls","proc-macro"],"derive":[],"extra-traits":[],"fold":[],"full":[],"parsing":[],"printing":["dep:quote"],"proc-macro":["proc-macro2/proc-macro","quote?/proc-macro"],"test":["syn-test-suite/all-features"],"visit":[],"visit-mut":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extend-css=src/gen/token.css","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}},"playground":{"features":["full","visit","visit-mut","fold","extra-traits"]}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::procedural-macro-helpers","parser-implementations"],"keywords":["macros","syn"],"readme":"README.md","repository":"https://github.com/dtolnay/syn","homepage":null,"documentation":"https://docs.rs/syn","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"sync_wrapper","version":"1.0.2","id":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","license":"Apache-2.0","license_file":null,"description":"A tool for enlisting the compiler's help in proving the absence of concurrency","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"sync_wrapper","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sync_wrapper-1.0.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"futures":["futures-core"],"futures-core":["dep:futures-core"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sync_wrapper-1.0.2/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["Actyx AG "],"categories":["concurrency"],"keywords":["concurrency"],"readme":"README.md","repository":"https://github.com/Actyx/sync_wrapper","homepage":"https://docs.rs/sync_wrapper","documentation":"https://docs.rs/sync_wrapper","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"synstructure","version":"0.13.2","id":"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2","license":"MIT","license_file":null,"description":"Helper methods and macros for custom derives","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.60","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive","parsing","printing","clone-impls","visit","extra-traits"],"target":null,"registry":null},{"name":"synstructure_test_traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"synstructure","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/synstructure-0.13.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"default":["proc-macro"],"proc-macro":["proc-macro2/proc-macro","syn/proc-macro","quote/proc-macro"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/synstructure-0.13.2/Cargo.toml","metadata":null,"publish":null,"authors":["Nika Layzell "],"categories":[],"keywords":["syn","macros","derive","expand_substructure","enum"],"readme":"README.md","repository":"https://github.com/mystor/synstructure","homepage":null,"documentation":"https://docs.rs/synstructure","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"tar","version":"0.4.46","id":"registry+https://github.com/rust-lang/crates.io-index#tar@0.4.46","license":"MIT OR Apache-2.0","license_file":null,"description":"A Rust implementation of a TAR file reader and writer. This library does not\ncurrently handle compression, but it is abstract over all I/O readers and\nwriters. Additionally, great lengths are taken to ensure that the entire\ncontents are never required to be entirely resident in memory all at once.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"filetime","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.8","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"astral-tokio-tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","rt"],"target":null,"registry":null},{"name":"tokio-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"xattr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tar","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"extract_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/examples/extract_file.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"list","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/examples/list.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"raw_list","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/examples/raw_list.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/examples/write.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"all","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/tests/all.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"entry","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/tests/entry.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["xattr"],"xattr":["dep:xattr"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":[],"keywords":["tar","tarfile","encoding"],"readme":"README.md","repository":"https://github.com/composefs/tar-rs","homepage":"https://github.com/composefs/tar-rs","documentation":"https://docs.rs/tar","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"tendril","version":"0.5.1","id":"registry+https://github.com/rust-lang/crates.io-index#tendril@0.5.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Compact buffer/string type for zero-copy parsing","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"encoding_rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.12","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"new_debug_unreachable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tendril","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"fuzz","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/examples/fuzz.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"futf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/benches/futf.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"tendril","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/benches/tendril.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"encoding_rs":["dep:encoding_rs"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/Cargo.toml","metadata":null,"publish":null,"authors":["Keegan McAllister ","Simon Sapin ","Chris Morgan "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/servo/html5ever","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"termcolor","version":"1.4.1","id":"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1","license":"Unlicense OR MIT","license_file":null,"description":"A simple cross platform library for writing colored text to a terminal.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"winapi-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"termcolor","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/termcolor-1.4.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/termcolor-1.4.1/Cargo.toml","metadata":null,"publish":null,"authors":["Andrew Gallant "],"categories":[],"keywords":["windows","win","color","ansi","console"],"readme":"README.md","repository":"https://github.com/BurntSushi/termcolor","homepage":"https://github.com/BurntSushi/termcolor","documentation":"https://docs.rs/termcolor","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"time","version":"0.3.36","id":"registry+https://github.com/rust-lang/crates.io-index#time@0.3.36","license":"MIT OR Apache-2.0","license_file":null,"description":"Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std].","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"deranged","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.9","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["powerfmt"],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num-conv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"powerfmt","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.184","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"time-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.1.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"time-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.18","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num-conv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.4","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rstest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.18.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rstest_reuse","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.184","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.68","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.126","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"time-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.18","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.68","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(__ui_tests)","registry":null},{"name":"js-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.58","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(target_family = \"wasm\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))","registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.1","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(bench)","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.98","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_family = \"unix\")","registry":null},{"name":"num_threads","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_family = \"unix\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"time","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.36/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.36/../tests/main.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benchmarks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.36/../benchmarks/main.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde?/alloc"],"default":["std"],"formatting":["dep:itoa","std","time-macros?/formatting"],"large-dates":["time-macros?/large-dates"],"local-offset":["std","dep:libc","dep:num_threads"],"macros":["dep:time-macros"],"parsing":["time-macros?/parsing"],"quickcheck":["dep:quickcheck","alloc","deranged/quickcheck"],"rand":["dep:rand","deranged/rand"],"serde":["dep:serde","time-macros?/serde","deranged/serde"],"serde-human-readable":["serde","formatting","parsing"],"serde-well-known":["serde","formatting","parsing"],"std":["alloc","deranged/std"],"wasm-bindgen":["dep:js-sys"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.36/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","__time_03_docs","--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["Jacob Pratt ","Time contributors"],"categories":["date-and-time","no-std","parser-implementations","value-formatting"],"keywords":["date","time","calendar","duration"],"readme":"README.md","repository":"https://github.com/time-rs/time","homepage":"https://time-rs.github.io","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"time-core","version":"0.1.2","id":"registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2","license":"MIT OR Apache-2.0","license_file":null,"description":"This crate is an implementation detail and should not be relied upon directly.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"time_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-core-0.1.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-core-0.1.2/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Jacob Pratt ","Time contributors"],"categories":["date-and-time"],"keywords":["date","time","calendar","duration"],"readme":null,"repository":"https://github.com/time-rs/time","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"tinystr","version":"0.8.3","id":"registry+https://github.com/rust-lang/crates.io-index#tinystr@0.8.3","license":"Unicode-3.0","license_file":null,"description":"A small ASCII-only bounded length string representation.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["use-std"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["alloc"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tinystr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/tests/serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"construct","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/benches/construct.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"overview","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/benches/overview.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/benches/read.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/benches/serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde_core?/alloc","zerovec?/alloc"],"databake":["dep:databake"],"default":["alloc"],"serde":["dep:serde_core"],"std":[],"zerovec":["dep:zerovec"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["data-structures"],"keywords":["small-string","ascii","string","str"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"tokio","version":"1.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","license":"MIT","license_file":null,"description":"An event-driven, non-blocking I/O platform for writing asynchronous I/O\nbacked applications.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"parking_lot","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.11","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.7.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"async-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["async-await"],"target":null,"registry":null},{"name":"futures-concurrency","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^7.6.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.31","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mockall","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["rt"],"target":null,"registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_family = \"wasm\", not(target_os = \"wasi\")))","registry":null},{"name":"tracing-mock","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.1.0-beta.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_has_atomic = \"64\"))","registry":null},{"name":"backtrace","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.58","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"io-uring","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.11","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.168","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["os-poll","os-ext"],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"slab","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"backtrace","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.58","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"socket2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["all"],"target":"cfg(any(not(target_family = \"wasm\"), all(target_os = \"wasi\", not(target_env = \"p1\"))))","registry":null},{"name":"loom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["futures","checkpoint"],"target":"cfg(loom)","registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(all(target_family = \"wasm\", target_os = \"unknown\")))","registry":null},{"name":"proptest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_family = \"wasm\"))","registry":null},{"name":"socket2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_family = \"wasm\"))","registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_family = \"wasm\"))","registry":null},{"name":"mio-aio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["tokio"],"target":"cfg(target_os = \"freebsd\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.168","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"wasi\")","registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.29","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":"cfg(tokio_unstable)","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.168","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"signal-hook-registry","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.168","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"nix","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.31.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["aio","fs","socket"],"target":"cfg(unix)","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.61","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.61","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_Security_Authorization"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tokio","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"_require_full","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/_require_full.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"async_send_sync","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/async_send_sync.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"buffered","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/buffered.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"coop_budget","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/coop_budget.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dump","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/dump.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"duplex_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/duplex_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_canonicalize_dir","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_canonicalize_dir.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_copy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_copy.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_dir","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_dir.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_file.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_link","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_link.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_open_options","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_open_options.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_open_options_windows","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_open_options_windows.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_remove_dir_all","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_remove_dir_all.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_remove_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_remove_file.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_rename","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_rename.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_symlink_dir_windows","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_symlink_dir_windows.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_symlink_file_windows","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_symlink_file_windows.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_try_exists","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_try_exists.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_cancel_open","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_cancel_open.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_completed_then_dropped_before_repoll","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_completed_then_dropped_before_repoll.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_file_read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_file_read.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_read.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_rename","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_rename.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_runtime_shutdown","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_runtime_shutdown.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_statx","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_statx.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_statx_fd_leak_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_statx_fd_leak_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_write.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_async_fd","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_async_fd.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_async_fd_memory_leak","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_async_fd_memory_leak.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_async_read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_async_read.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_buf_reader","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_buf_reader.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_buf_writer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_buf_writer.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_chain","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_chain.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_copy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_copy.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_copy_bidirectional","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_copy_bidirectional.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_driver","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_driver.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_driver_drop","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_driver_drop.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_fill_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_fill_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_join","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_join.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_lines","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_lines.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_mem_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_mem_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_poll_aio","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_poll_aio.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_exact","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_exact.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_line","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_line.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_to_end","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_to_end.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_to_string","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_to_string.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_until","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_until.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_repeat","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_repeat.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_sink","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_sink.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_split","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_split.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_take","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_take.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_util_empty","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_util_empty.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write_all","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write_all.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write_all_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write_all_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write_int","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write_int.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"join_handle_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/join_handle_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_join","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_join.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_pin","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_pin.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_rename_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_rename_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_select","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_select.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_try_join","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_try_join.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_bind_resource","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_bind_resource.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_lookup_host","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_lookup_host.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_named_pipe","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_named_pipe.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_quickack","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_quickack.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_unix_pipe","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_unix_pipe.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"no_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/no_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_arg0","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_arg0.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_change_of_runtime","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_change_of_runtime.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_issue_2174","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_issue_2174.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_issue_42","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_issue_42.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_issue_7144","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_issue_7144.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_kill_after_wait","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_kill_after_wait.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_kill_on_drop","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_kill_on_drop.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_raw_handle","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_raw_handle.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_smoke","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_smoke.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_basic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_basic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_common","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_common.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_common_before_park","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_common_before_park.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_handle","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_handle.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_handle_block_on","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_handle_block_on.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_local","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_local.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_metrics","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_metrics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_poll_callbacks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_poll_callbacks.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_shutdown_err","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_shutdown_err.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_spawn_blocking","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_spawn_blocking.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_threaded","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_threaded.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_time_start_paused","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_time_start_paused.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_unstable_eager_driver_handoff","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_unstable_eager_driver_handoff.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_unstable_metrics","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_unstable_metrics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_worker_index","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_worker_index.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_ctrl_c","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_ctrl_c.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_drop_recv","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_drop_recv.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_drop_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_drop_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_drop_signal","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_drop_signal.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_info","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_info.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_multi_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_multi_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_no_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_no_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_notify_both","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_notify_both.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_realtime","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_realtime.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_twice","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_twice.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_usr1","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_usr1.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_barrier","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_barrier.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_broadcast","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_broadcast.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_broadcast_weak","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_broadcast_weak.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_errors","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_errors.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_mpsc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_mpsc.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_mpsc_weak","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_mpsc_weak.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_mutex","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_mutex.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_mutex_owned","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_mutex_owned.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_notify","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_notify.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_notify_owned","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_notify_owned.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_once_cell","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_once_cell.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_oneshot","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_oneshot.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_rwlock","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_rwlock.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_semaphore","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_semaphore.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_semaphore_owned","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_semaphore_owned.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_set_once","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_set_once.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_watch","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_watch.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_abort","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_abort.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_blocking","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_blocking.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_builder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_builder.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_hooks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_hooks.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_id","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_id.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_join_set","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_join_set.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_local","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_local.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_local_set","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_local_set.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_trace_self","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_trace_self.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_yield_now","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_yield_now.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_accept","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_accept.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_connect","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_connect.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_echo","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_echo.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_into_split","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_into_split.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_into_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_into_std.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_peek","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_peek.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_shutdown","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_shutdown.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_socket","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_socket.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_split","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_split.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_clock","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/test_clock.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_alt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_alt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_interval","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_interval.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_pause","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_pause.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_sleep","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_sleep.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_timeout","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_timeout.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_wasm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_wasm.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tracing_sync","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tracing_sync.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tracing_task","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tracing_task.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tracing_time","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tracing_time.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"udp","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/udp.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_cred","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_cred.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_datagram","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_datagram.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_socket","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_socket.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_split","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_split.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unwindsafe","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/unwindsafe.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"bytes":["dep:bytes"],"default":[],"fs":[],"full":["fs","io-util","io-std","macros","net","parking_lot","process","rt","rt-multi-thread","signal","sync","time"],"io-std":[],"io-uring":["dep:io-uring","libc","mio/os-poll","mio/os-ext","dep:slab"],"io-util":["bytes"],"libc":["dep:libc"],"macros":["tokio-macros"],"mio":["dep:mio"],"net":["libc","mio/os-poll","mio/os-ext","mio/net","socket2","windows-sys/Win32_Foundation","windows-sys/Win32_Security","windows-sys/Win32_Storage_FileSystem","windows-sys/Win32_System_Pipes","windows-sys/Win32_System_SystemServices"],"parking_lot":["dep:parking_lot"],"process":["bytes","libc","mio/os-poll","mio/os-ext","mio/net","signal-hook-registry","windows-sys/Win32_Foundation","windows-sys/Win32_System_Threading","windows-sys/Win32_System_WindowsProgramming"],"rt":[],"rt-multi-thread":["rt"],"schedule-latency":[],"signal":["libc","mio/os-poll","mio/net","mio/os-ext","signal-hook-registry","windows-sys/Win32_Foundation","windows-sys/Win32_System_Console"],"signal-hook-registry":["dep:signal-hook-registry"],"socket2":["dep:socket2"],"sync":[],"taskdump":["dep:backtrace"],"test-util":["rt","sync","time"],"time":[],"tokio-macros":["dep:tokio-macros"],"tracing":["dep:tracing"],"windows-sys":["dep:windows-sys"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--cfg","tokio_unstable"],"rustc-args":["--cfg","tokio_unstable"]}},"playground":{"features":["full","test-util"]},"cargo_check_external_types":{"allowed_external_types":["bytes::buf::buf_impl::Buf","bytes::buf::buf_mut::BufMut","tokio_macros::*"]}},"publish":null,"authors":["Tokio Contributors "],"categories":["asynchronous","network-programming"],"keywords":["io","async","non-blocking","futures"],"readme":"README.md","repository":"https://github.com/tokio-rs/tokio","homepage":"https://tokio.rs","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"tokio-macros","version":"2.7.2","id":"registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.7.2","license":"MIT","license_file":null,"description":"Tokio's proc macros.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.60","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["full","test-util"],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"tokio_macros","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-macros-2.7.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-macros-2.7.2/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["Tokio Contributors "],"categories":["asynchronous"],"keywords":[],"readme":"README.md","repository":"https://github.com/tokio-rs/tokio","homepage":"https://tokio.rs","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"tower","version":"0.5.3","id":"registry+https://github.com/rust-lang/crates.io-index#tower@0.5.3","license":"MIT","license_file":null,"description":"Tower is a library of modular and reusable components for building robust\nclients and servers.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"hdrhistogram","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^7.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"indexmap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"slab","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sync_wrapper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-service","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["async-await-macro"],"target":null,"registry":null},{"name":"hdrhistogram","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^7.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","sync","test-util","rt-multi-thread"],"target":null,"registry":null},{"name":"tokio-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"tracing-subscriber","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["fmt","ansi"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tower","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"tower-balance","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/examples/tower-balance.rs","edition":"2018","required-features":["full"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"balance","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/balance/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/buffer/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"builder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/builder.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"hedge","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/hedge/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"limit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/limit/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"load_shed","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/load_shed/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"ready_cache","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/ready_cache/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"retry","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/retry/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"spawn_ready","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/spawn_ready/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"steer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/steer/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"support","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/support.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/util/main.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"balance":["discover","load","ready-cache","make","slab","util"],"buffer":["tokio/sync","tokio/rt","tokio-util","tracing","pin-project-lite"],"discover":["futures-core","pin-project-lite"],"filter":["futures-util","pin-project-lite"],"full":["balance","buffer","discover","filter","hedge","limit","load","load-shed","make","ready-cache","reconnect","retry","spawn-ready","steer","timeout","util"],"futures-core":["dep:futures-core"],"futures-util":["dep:futures-util"],"hdrhistogram":["dep:hdrhistogram"],"hedge":["util","filter","futures-util","hdrhistogram","tokio/time","tracing"],"indexmap":["dep:indexmap"],"limit":["tokio/time","tokio/sync","tokio-util","tracing","pin-project-lite"],"load":["tokio/time","tracing","pin-project-lite"],"load-shed":["pin-project-lite"],"log":["tracing/log"],"make":["pin-project-lite","tokio"],"pin-project-lite":["dep:pin-project-lite"],"ready-cache":["futures-core","futures-util","indexmap","tokio/sync","tracing","pin-project-lite"],"reconnect":["make","tracing"],"retry":["tokio/time","util"],"slab":["dep:slab"],"spawn-ready":["futures-util","tokio/sync","tokio/rt","util","tracing"],"steer":[],"sync_wrapper":["dep:sync_wrapper"],"timeout":["pin-project-lite","tokio/time"],"tokio":["dep:tokio"],"tokio-stream":[],"tokio-util":["dep:tokio-util"],"tracing":["dep:tracing"],"util":["futures-core","futures-util","pin-project-lite","sync_wrapper"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}},"playground":{"features":["full"]}},"publish":null,"authors":["Tower Maintainers "],"categories":["asynchronous","network-programming"],"keywords":["io","async","non-blocking","futures","service"],"readme":"README.md","repository":"https://github.com/tower-rs/tower","homepage":"https://github.com/tower-rs/tower","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.64.0"},{"name":"tower-layer","version":"0.3.3","id":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","license":"MIT","license_file":null,"description":"Decorates a `Service` to allow easy composition between `Service`s.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tower_layer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-layer-0.3.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-layer-0.3.3/Cargo.toml","metadata":null,"publish":null,"authors":["Tower Maintainers "],"categories":["asynchronous","network-programming"],"keywords":[],"readme":"README.md","repository":"https://github.com/tower-rs/tower","homepage":"https://github.com/tower-rs/tower","documentation":"https://docs.rs/tower-layer/0.3.3","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"tower-service","version":"0.3.3","id":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","license":"MIT","license_file":null,"description":"Trait representing an asynchronous, request / response based, client or server.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","time"],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tower_service","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-service-0.3.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-service-0.3.3/Cargo.toml","metadata":null,"publish":null,"authors":["Tower Maintainers "],"categories":["asynchronous","network-programming"],"keywords":[],"readme":"README.md","repository":"https://github.com/tower-rs/tower","homepage":"https://github.com/tower-rs/tower","documentation":"https://docs.rs/tower-service/0.3.3","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter","version":"0.25.10","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter@0.25.10","license":"MIT","license_file":null,"description":"Rust bindings to the Tree-sitter parsing library","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"regex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.11.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["unicode"],"target":null,"registry":null},{"name":"regex-syntax","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.5","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"streaming-iterator","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasmtime-c-api-impl","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^29.0.1","kind":null,"rename":"wasmtime-c-api","optional":true,"uses_default_features":false,"features":["cranelift","gc-drc"],"target":null,"registry":null},{"name":"bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.71.1","kind":"build","rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.10","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.137","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":["preserve_order"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-0.25.10/binding_rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-0.25.10/binding_rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"bindgen":["dep:bindgen"],"default":["std"],"std":["regex/std","regex/perf","regex-syntax/unicode"],"wasm":["std","wasmtime-c-api"],"wasmtime-c-api":["dep:wasmtime-c-api"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-0.25.10/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"],"targets":["x86_64-unknown-linux-gnu","x86_64-pc-windows-gnu"]}}},"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["api-bindings","external-ffi-bindings","parsing","text-editors"],"keywords":["incremental","parsing"],"readme":"binding_rust/README.md","repository":"https://github.com/tree-sitter/tree-sitter","homepage":"https://tree-sitter.github.io/tree-sitter","documentation":null,"edition":"2021","links":"tree-sitter","default_run":null,"rust_version":"1.76"},{"name":"tree-sitter-c","version":"0.24.2","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-c@0.24.2","license":"MIT","license_file":null,"description":"C grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_c","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-c-0.24.2/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-c-0.24.2/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-c-0.24.2/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","c"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-c","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-cpp","version":"0.23.4","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-cpp@0.23.4","license":"MIT","license_file":null,"description":"C++ grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_cpp","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-cpp-0.23.4/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-cpp-0.23.4/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-cpp-0.23.4/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","cpp"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-cpp","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-css","version":"0.25.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-css@0.25.0","license":"MIT","license_file":null,"description":"CSS grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_css","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-css-0.25.0/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-css-0.25.0/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-css-0.25.0/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parser-implementations","parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","css"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-css","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-fortran","version":"0.6.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-fortran@0.6.0","license":"MIT","license_file":null,"description":"fortran grammar for the tree-sitter parsing library","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.26.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_fortran","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-fortran-0.6.0/bindings/rust/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-fortran-0.6.0/bindings/rust/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-fortran-0.6.0/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","fortran"],"readme":"README.md","repository":"https://github.com/stadelmanma/tree-sitter-fortran","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-go","version":"0.25.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-go@0.25.0","license":"MIT","license_file":null,"description":"Go grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_go","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-go-0.25.0/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-go-0.25.0/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-go-0.25.0/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parser-implementations","parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","go"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-go","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-html","version":"0.23.2","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-html@0.23.2","license":"MIT","license_file":null,"description":"HTML grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_html","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-html-0.23.2/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-html-0.23.2/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-html-0.23.2/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","html"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-html","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-javascript","version":"0.25.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-javascript@0.25.0","license":"MIT","license_file":null,"description":"JavaScript grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_javascript","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-javascript-0.25.0/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-javascript-0.25.0/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-javascript-0.25.0/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parser-implementations","parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","javascript"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-javascript","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-language","version":"0.1.8","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","license":"MIT","license_file":null,"description":"The tree-sitter Language type, used by the library and by language implementations","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_language","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-language-0.1.8/src/language.rs","edition":"2024","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-language-0.1.8/build.rs","edition":"2024","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-language-0.1.8/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["api-bindings","development-tools::ffi","parsing"],"keywords":["incremental","parsing"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter","homepage":"https://tree-sitter.github.io/tree-sitter","documentation":"https://docs.rs/tree-sitter-language","edition":"2024","links":"tree-sitter-language","default_run":null,"rust_version":"1.90"},{"name":"tree-sitter-python","version":"0.25.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-python@0.25.0","license":"MIT","license_file":null,"description":"Python grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_python","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-python-0.25.0/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-python-0.25.0/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-python-0.25.0/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parser-implementations","parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","python"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-python","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-rust","version":"0.24.2","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-rust@0.24.2","license":"MIT","license_file":null,"description":"Rust grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_rust","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-rust-0.24.2/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-rust-0.24.2/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-rust-0.24.2/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","rust"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-rust","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-typescript","version":"0.23.2","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-typescript@0.23.2","license":"MIT","license_file":null,"description":"TypeScript and TSX grammars for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_typescript","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-typescript-0.23.2/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-typescript-0.23.2/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-typescript-0.23.2/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","typescript","tsx"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-typescript","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"typenum","version":"1.20.1","id":"registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Typenum is a Rust library for type-level numbers evaluated at\n compile time. It currently supports bits, unsigned integers, and signed\n integers. It also provides a type-level array of type-level numbers, but its\n implementation is incomplete.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"scale-info","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"typenum","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/typenum-1.20.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"generated","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/typenum-1.20.1/tests/generated.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"const-generics":[],"i128":[],"scale-info":["dep:scale-info"],"scale_info":["scale-info/derive"],"strict":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/typenum-1.20.1/Cargo.toml","metadata":{"docs":{"rs":{"features":["i128","const-generics"],"rustdoc-args":["--cfg","docsrs"]}},"playground":{"features":["i128","const-generics"]}},"publish":null,"authors":[],"categories":["no-std"],"keywords":[],"readme":"README.md","repository":"https://github.com/paholg/typenum","homepage":null,"documentation":"https://docs.rs/typenum","edition":"2018","links":null,"default_run":null,"rust_version":"1.41.0"},{"name":"unicase","version":"2.9.0","id":"registry+https://github.com/rust-lang/crates.io-index#unicase@2.9.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A case-insensitive wrapper around strings.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"unicase","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicase-2.9.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"nightly":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicase-2.9.0/Cargo.toml","metadata":null,"publish":null,"authors":["Sean McArthur "],"categories":["internationalization","text-processing","no-std"],"keywords":["lowercase","case","case-insensitive","case-folding","no_std"],"readme":"README.md","repository":"https://github.com/seanmonstar/unicase","homepage":null,"documentation":"https://docs.rs/unicase","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"unicode-ident","version":"1.0.24","id":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","license":"(MIT OR Apache-2.0) AND Unicode-3.0","license_file":null,"description":"Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"fst","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"roaring","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ucd-trie","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unicode-xid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"unicode_ident","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compare","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/tests/compare.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"static_size","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/tests/static_size.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"xid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/benches/xid.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::procedural-macro-helpers","no-std","no-std::no-alloc"],"keywords":["unicode","xid"],"readme":"README.md","repository":"https://github.com/dtolnay/unicode-ident","homepage":null,"documentation":"https://docs.rs/unicode-ident","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"unicode-width","version":"0.1.14","id":"registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.14","license":"MIT OR Apache-2.0","license_file":null,"description":"Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"compiler_builtins","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-std","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"std","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"unicode_width","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-width-0.1.14/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-width-0.1.14/tests/tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benches","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-width-0.1.14/benches/benches.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"cjk":[],"compiler_builtins":["dep:compiler_builtins"],"core":["dep:core"],"default":["cjk"],"no_std":[],"rustc-dep-of-std":["std","core","compiler_builtins"],"std":["dep:std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-width-0.1.14/Cargo.toml","metadata":null,"publish":null,"authors":["kwantam ","Manish Goregaokar "],"categories":["command-line-interface","internationalization","no-std::no-alloc","text-processing"],"keywords":["text","width","unicode"],"readme":"README.md","repository":"https://github.com/unicode-rs/unicode-width","homepage":"https://github.com/unicode-rs/unicode-width","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"url","version":"2.5.8","id":"registry+https://github.com/rust-lang/crates.io-index#url@2.5.8","license":"MIT OR Apache-2.0","license_file":null,"description":"URL library for Rust, based on the WHATWG URL Standard","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"form_urlencoded","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"idna","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc","compiled_data"],"target":null,"registry":null},{"name":"percent-encoding","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bencher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"url","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.8/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.8/tests/unit.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"url_wpt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.8/tests/wpt.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"debugger_visualizer":[],"default":["std"],"expose_internals":[],"serde":["dep:serde","dep:serde_derive"],"std":["idna/std","percent-encoding/std","form_urlencoded/std","serde?/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.8/Cargo.toml","metadata":{"docs":{"rs":{"features":["serde"],"rustdoc-args":["--generate-link-to-definition"]}},"playground":{"features":["serde"]}},"publish":null,"authors":["The rust-url developers"],"categories":["parser-implementations","web-programming","encoding","no-std"],"keywords":["url","parser"],"readme":"README.md","repository":"https://github.com/servo/rust-url","homepage":null,"documentation":"https://docs.rs/url","edition":"2018","links":null,"default_run":null,"rust_version":"1.63"},{"name":"utf8_iter","version":"1.0.4","id":"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","license":"Apache-2.0 OR MIT","license_file":null,"description":"Iterator by char over potentially-invalid UTF-8 in &[u8]","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"utf8_iter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/utf8_iter-1.0.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/utf8_iter-1.0.4/Cargo.toml","metadata":null,"publish":null,"authors":["Henri Sivonen "],"categories":["text-processing","encoding","internationalization"],"keywords":["encoding","UTF-8","unicode","iterator"],"readme":"README.md","repository":"https://github.com/hsivonen/utf8_iter","homepage":"https://docs.rs/utf8_iter/","documentation":"https://docs.rs/utf8_iter/","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"uuid","version":"1.18.1","id":"registry+https://github.com/rust-lang/crates.io-index#uuid@1.18.1","license":"Apache-2.0 OR MIT","license_file":null,"description":"A library to generate and parse UUIDs.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"borsh","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"borsh-derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bytemuck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.18.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"md-5","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.56","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"sha1_smol","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"slog","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uuid-macro-internal","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.18.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"zerocopy","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.79","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.56","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.52","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uuid-rng-internal","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.18.1","kind":null,"rename":"uuid-rng-internal-lib","optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["msrv"],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"js-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"), target_feature = \"atomics\"))","registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(not(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"))))","registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(not(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"))))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"uuid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.18.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"arbitrary":["dep:arbitrary"],"atomic":["dep:atomic"],"borsh":["dep:borsh","dep:borsh-derive"],"bytemuck":["dep:bytemuck"],"default":["std"],"fast-rng":["rng","dep:rand"],"js":["dep:wasm-bindgen","dep:js-sys"],"macro-diagnostics":["dep:uuid-macro-internal"],"md5":["dep:md-5"],"rng":["dep:getrandom"],"rng-getrandom":["rng","dep:getrandom","uuid-rng-internal-lib","uuid-rng-internal-lib/getrandom"],"rng-rand":["rng","dep:rand","uuid-rng-internal-lib","uuid-rng-internal-lib/rand"],"serde":["dep:serde"],"sha1":["dep:sha1_smol"],"slog":["dep:slog"],"std":["wasm-bindgen?/std","js-sys?/std"],"uuid-rng-internal-lib":["dep:uuid-rng-internal-lib"],"v1":["atomic"],"v3":["md5"],"v4":["rng"],"v5":["sha1"],"v6":["atomic"],"v7":["rng"],"v8":[],"zerocopy":["dep:zerocopy"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.18.1/Cargo.toml","metadata":{"docs":{"rs":{"rustc-args":["--cfg","uuid_unstable"],"rustdoc-args":["--cfg","uuid_unstable"],"targets":["x86_64-unknown-linux-gnu"],"features":["serde","arbitrary","slog","borsh","v1","v3","v4","v5","v6","v7","v8"]}},"playground":{"features":["serde","v1","v3","v4","v5","v6","v7","v8"]}},"publish":null,"authors":["Ashley Mannix","Dylan DPC","Hunar Roop Kahlon"],"categories":["data-structures","no-std","parser-implementations","wasm"],"keywords":["guid","unique","uuid"],"readme":"README.md","repository":"https://github.com/uuid-rs/uuid","homepage":"https://github.com/uuid-rs/uuid","documentation":"https://docs.rs/uuid","edition":"2018","links":null,"default_run":null,"rust_version":"1.63.0"},{"name":"version_check","version":"0.9.5","id":"registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5","license":"MIT/Apache-2.0","license_file":null,"description":"Tiny crate to check the version of the installed/running rustc.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"version_check","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/version_check-0.9.5/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/version_check-0.9.5/Cargo.toml","metadata":null,"publish":null,"authors":["Sergio Benitez "],"categories":[],"keywords":["version","rustc","minimum","check"],"readme":"README.md","repository":"https://github.com/SergioBenitez/version_check","homepage":null,"documentation":"https://docs.rs/version_check/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"walkdir","version":"2.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","license":"Unlicense/MIT","license_file":null,"description":"Recursively walk a directory.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"same-file","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"winapi-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"walkdir","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/walkdir-2.5.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/walkdir-2.5.0/Cargo.toml","metadata":null,"publish":null,"authors":["Andrew Gallant "],"categories":["filesystem"],"keywords":["directory","recursive","walk","iterator"],"readme":"README.md","repository":"https://github.com/BurntSushi/walkdir","homepage":"https://github.com/BurntSushi/walkdir","documentation":"https://docs.rs/walkdir/","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"wasi","version":"0.11.1+wasi-snapshot-preview1","id":"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.1+wasi-snapshot-preview1","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"Experimental WASI API bindings for Rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasi","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasi-0.11.1+wasi-snapshot-preview1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"core":["dep:core"],"default":["std"],"rustc-dep-of-std":["core","rustc-std-workspace-alloc"],"rustc-std-workspace-alloc":["dep:rustc-std-workspace-alloc"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasi-0.11.1+wasi-snapshot-preview1/Cargo.toml","metadata":null,"publish":null,"authors":["The Cranelift Project Developers"],"categories":["no-std","wasm"],"keywords":["webassembly","wasm"],"readme":"README.md","repository":"https://github.com/bytecodealliance/wasi","homepage":null,"documentation":"https://docs.rs/wasi","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"wasip2","version":"1.0.4+wasi-0.2.12","id":"registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"WASIp2 API bindings for Rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"alloc","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wit-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.57.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasip2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["cdylib"],"name":"cli-command","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/cli-command.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["cdylib"],"name":"cli-command-no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/cli-command-no_std.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"hello-world","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/hello-world.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"hello-world-no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/hello-world-no_std.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["cdylib"],"name":"http-proxy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/http-proxy.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["cdylib"],"name":"http-proxy-no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/http-proxy-no_std.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["dep:alloc"],"bitflags":["wit-bindgen/bitflags"],"core":["dep:core"],"default":["std","bitflags"],"rustc-dep-of-std":["core","alloc","wit-bindgen/rustc-dep-of-std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["no-std","wasm"],"keywords":["webassembly","wasm"],"readme":"README.md","repository":"https://github.com/bytecodealliance/wasi-rs","homepage":null,"documentation":"https://docs.rs/wasip2","edition":"2021","links":null,"default_run":null,"rust_version":"1.87.0"},{"name":"wasm-bindgen","version":"0.2.125","id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125","license":"MIT OR Apache-2.0","license_file":null,"description":"Easy support for interacting between JS and Rust.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"once_cell","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasm-bindgen-macro","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasm-bindgen-shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"once_cell","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.6","kind":"build","rename":"rustversion-compat","optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"paste","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"paste","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm64\")","registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm64\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasm_bindgen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-0.2.125/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-0.2.125/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"enable-interning":["std"],"gg-alloc":[],"msrv":[],"rustversion":[],"serde":["dep:serde"],"serde-serialize":["serde","serde_json","std"],"serde_json":["dep:serde_json"],"spans":[],"std":[],"strict-macro":["wasm-bindgen-macro/strict-macro"],"xxx_debug_only_print_generated_code":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-0.2.125/Cargo.toml","metadata":{"docs":{"rs":{"features":["serde-serialize"]}}},"publish":null,"authors":["The wasm-bindgen Developers"],"categories":["wasm"],"keywords":[],"readme":"README.md","repository":"https://github.com/wasm-bindgen/wasm-bindgen","homepage":"https://wasm-bindgen.github.io/wasm-bindgen","documentation":"https://docs.rs/wasm-bindgen","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"wasm-bindgen-macro","version":"0.2.125","id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.125","license":"MIT OR Apache-2.0","license_file":null,"description":"Definition of the `#[wasm_bindgen]` attribute, an internal dependency\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasm-bindgen-macro-support","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"wasm_bindgen_macro","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-macro-0.2.125/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"strict-macro":["wasm-bindgen-macro-support/strict-macro"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-macro-0.2.125/Cargo.toml","metadata":null,"publish":null,"authors":["The wasm-bindgen Developers"],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro","homepage":"https://wasm-bindgen.github.io/wasm-bindgen/","documentation":"https://docs.rs/wasm-bindgen","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"wasm-bindgen-macro-support","version":"0.2.125","id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.125","license":"MIT OR Apache-2.0","license_file":null,"description":"Implementation APIs for the `#[wasm_bindgen]` attribute","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bumpalo","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["visit","visit-mut","full","extra-traits"],"target":null,"registry":null},{"name":"wasm-bindgen-shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasm_bindgen_macro_support","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-macro-support-0.2.125/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"extra-traits":["syn/extra-traits"],"strict-macro":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-macro-support-0.2.125/Cargo.toml","metadata":null,"publish":null,"authors":["The wasm-bindgen Developers"],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro-support","homepage":"https://wasm-bindgen.github.io/wasm-bindgen/","documentation":"https://docs.rs/wasm-bindgen","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"wasm-bindgen-shared","version":"0.2.125","id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125","license":"MIT OR Apache-2.0","license_file":null,"description":"Shared support between wasm-bindgen and wasm-bindgen cli, an internal\ndependency.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"unicode-ident","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasm_bindgen_shared","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-shared-0.2.125/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-shared-0.2.125/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-shared-0.2.125/Cargo.toml","metadata":null,"publish":null,"authors":["The wasm-bindgen Developers"],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/shared","homepage":"https://wasm-bindgen.github.io/wasm-bindgen/","documentation":"https://docs.rs/wasm-bindgen-shared","edition":"2021","links":"wasm_bindgen","default_run":null,"rust_version":"1.77"},{"name":"web_atoms","version":"0.2.6","id":"registry+https://github.com/rust-lang/crates.io-index#web_atoms@0.2.6","license":"MIT OR Apache-2.0","license_file":null,"description":"Atoms for xml5ever and html5ever","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"phf","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"string_cache","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"phf_codegen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"string_cache_codegen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"web_atoms","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/web_atoms-0.2.6/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/web_atoms-0.2.6/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"serde":["string_cache/serde_support"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/web_atoms-0.2.6/Cargo.toml","metadata":null,"publish":null,"authors":["The html5ever Project Developers"],"categories":["web-programming"],"keywords":[],"readme":null,"repository":"https://github.com/servo/html5ever","homepage":null,"documentation":"https://docs.rs/web_atoms","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"winapi","version":"0.3.9","id":"registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9","license":"MIT/Apache-2.0","license_file":null,"description":"Raw FFI bindings for all of Windows API.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"winapi-i686-pc-windows-gnu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"i686-pc-windows-gnu","registry":null},{"name":"winapi-x86_64-pc-windows-gnu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"x86_64-pc-windows-gnu","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"winapi","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-0.3.9/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-0.3.9/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{"accctrl":[],"aclapi":[],"activation":[],"adhoc":[],"appmgmt":[],"audioclient":[],"audiosessiontypes":[],"avrt":[],"basetsd":[],"bcrypt":[],"bits":[],"bits10_1":[],"bits1_5":[],"bits2_0":[],"bits2_5":[],"bits3_0":[],"bits4_0":[],"bits5_0":[],"bitscfg":[],"bitsmsg":[],"bluetoothapis":[],"bluetoothleapis":[],"bthdef":[],"bthioctl":[],"bthledef":[],"bthsdpdef":[],"bugcodes":[],"cderr":[],"cfg":[],"cfgmgr32":[],"cguid":[],"combaseapi":[],"coml2api":[],"commapi":[],"commctrl":[],"commdlg":[],"commoncontrols":[],"consoleapi":[],"corecrt":[],"corsym":[],"d2d1":[],"d2d1_1":[],"d2d1_2":[],"d2d1_3":[],"d2d1effectauthor":[],"d2d1effects":[],"d2d1effects_1":[],"d2d1effects_2":[],"d2d1svg":[],"d2dbasetypes":[],"d3d":[],"d3d10":[],"d3d10_1":[],"d3d10_1shader":[],"d3d10effect":[],"d3d10misc":[],"d3d10sdklayers":[],"d3d10shader":[],"d3d11":[],"d3d11_1":[],"d3d11_2":[],"d3d11_3":[],"d3d11_4":[],"d3d11on12":[],"d3d11sdklayers":[],"d3d11shader":[],"d3d11tokenizedprogramformat":[],"d3d12":[],"d3d12sdklayers":[],"d3d12shader":[],"d3d9":[],"d3d9caps":[],"d3d9types":[],"d3dcommon":[],"d3dcompiler":[],"d3dcsx":[],"d3dkmdt":[],"d3dkmthk":[],"d3dukmdt":[],"d3dx10core":[],"d3dx10math":[],"d3dx10mesh":[],"datetimeapi":[],"davclnt":[],"dbghelp":[],"dbt":[],"dcommon":[],"dcomp":[],"dcompanimation":[],"dcomptypes":[],"dde":[],"ddraw":[],"ddrawi":[],"ddrawint":[],"debug":["impl-debug"],"debugapi":[],"devguid":[],"devicetopology":[],"devpkey":[],"devpropdef":[],"dinput":[],"dinputd":[],"dispex":[],"dmksctl":[],"dmusicc":[],"docobj":[],"documenttarget":[],"dot1x":[],"dpa_dsa":[],"dpapi":[],"dsgetdc":[],"dsound":[],"dsrole":[],"dvp":[],"dwmapi":[],"dwrite":[],"dwrite_1":[],"dwrite_2":[],"dwrite_3":[],"dxdiag":[],"dxfile":[],"dxgi":[],"dxgi1_2":[],"dxgi1_3":[],"dxgi1_4":[],"dxgi1_5":[],"dxgi1_6":[],"dxgidebug":[],"dxgiformat":[],"dxgitype":[],"dxva2api":[],"dxvahd":[],"eaptypes":[],"enclaveapi":[],"endpointvolume":[],"errhandlingapi":[],"everything":[],"evntcons":[],"evntprov":[],"evntrace":[],"excpt":[],"exdisp":[],"fibersapi":[],"fileapi":[],"functiondiscoverykeys_devpkey":[],"gl-gl":[],"guiddef":[],"handleapi":[],"heapapi":[],"hidclass":[],"hidpi":[],"hidsdi":[],"hidusage":[],"highlevelmonitorconfigurationapi":[],"hstring":[],"http":[],"ifdef":[],"ifmib":[],"imm":[],"impl-debug":[],"impl-default":[],"in6addr":[],"inaddr":[],"inspectable":[],"interlockedapi":[],"intsafe":[],"ioapiset":[],"ipexport":[],"iphlpapi":[],"ipifcons":[],"ipmib":[],"iprtrmib":[],"iptypes":[],"jobapi":[],"jobapi2":[],"knownfolders":[],"ks":[],"ksmedia":[],"ktmtypes":[],"ktmw32":[],"l2cmn":[],"libloaderapi":[],"limits":[],"lmaccess":[],"lmalert":[],"lmapibuf":[],"lmat":[],"lmcons":[],"lmdfs":[],"lmerrlog":[],"lmjoin":[],"lmmsg":[],"lmremutl":[],"lmrepl":[],"lmserver":[],"lmshare":[],"lmstats":[],"lmsvc":[],"lmuse":[],"lmwksta":[],"lowlevelmonitorconfigurationapi":[],"lsalookup":[],"memoryapi":[],"minschannel":[],"minwinbase":[],"minwindef":[],"mmdeviceapi":[],"mmeapi":[],"mmreg":[],"mmsystem":[],"mprapidef":[],"msaatext":[],"mscat":[],"mschapp":[],"mssip":[],"mstcpip":[],"mswsock":[],"mswsockdef":[],"namedpipeapi":[],"namespaceapi":[],"nb30":[],"ncrypt":[],"netioapi":[],"nldef":[],"ntddndis":[],"ntddscsi":[],"ntddser":[],"ntdef":[],"ntlsa":[],"ntsecapi":[],"ntstatus":[],"oaidl":[],"objbase":[],"objidl":[],"objidlbase":[],"ocidl":[],"ole2":[],"oleauto":[],"olectl":[],"oleidl":[],"opmapi":[],"pdh":[],"perflib":[],"physicalmonitorenumerationapi":[],"playsoundapi":[],"portabledevice":[],"portabledeviceapi":[],"portabledevicetypes":[],"powerbase":[],"powersetting":[],"powrprof":[],"processenv":[],"processsnapshot":[],"processthreadsapi":[],"processtopologyapi":[],"profileapi":[],"propidl":[],"propkey":[],"propkeydef":[],"propsys":[],"prsht":[],"psapi":[],"qos":[],"realtimeapiset":[],"reason":[],"restartmanager":[],"restrictederrorinfo":[],"rmxfguid":[],"roapi":[],"robuffer":[],"roerrorapi":[],"rpc":[],"rpcdce":[],"rpcndr":[],"rtinfo":[],"sapi":[],"sapi51":[],"sapi53":[],"sapiddk":[],"sapiddk51":[],"schannel":[],"sddl":[],"securityappcontainer":[],"securitybaseapi":[],"servprov":[],"setupapi":[],"shellapi":[],"shellscalingapi":[],"shlobj":[],"shobjidl":[],"shobjidl_core":[],"shtypes":[],"softpub":[],"spapidef":[],"spellcheck":[],"sporder":[],"sql":[],"sqlext":[],"sqltypes":[],"sqlucode":[],"sspi":[],"std":[],"stralign":[],"stringapiset":[],"strmif":[],"subauth":[],"synchapi":[],"sysinfoapi":[],"systemtopologyapi":[],"taskschd":[],"tcpestats":[],"tcpmib":[],"textstor":[],"threadpoolapiset":[],"threadpoollegacyapiset":[],"timeapi":[],"timezoneapi":[],"tlhelp32":[],"transportsettingcommon":[],"tvout":[],"udpmib":[],"unknwnbase":[],"urlhist":[],"urlmon":[],"usb":[],"usbioctl":[],"usbiodef":[],"usbscan":[],"usbspec":[],"userenv":[],"usp10":[],"utilapiset":[],"uxtheme":[],"vadefs":[],"vcruntime":[],"vsbackup":[],"vss":[],"vsserror":[],"vswriter":[],"wbemads":[],"wbemcli":[],"wbemdisp":[],"wbemprov":[],"wbemtran":[],"wct":[],"werapi":[],"winbase":[],"wincodec":[],"wincodecsdk":[],"wincon":[],"wincontypes":[],"wincred":[],"wincrypt":[],"windef":[],"windot11":[],"windowsceip":[],"windowsx":[],"winefs":[],"winerror":[],"winevt":[],"wingdi":[],"winhttp":[],"wininet":[],"winineti":[],"winioctl":[],"winnetwk":[],"winnls":[],"winnt":[],"winreg":[],"winsafer":[],"winscard":[],"winsmcrd":[],"winsock2":[],"winspool":[],"winstring":[],"winsvc":[],"wintrust":[],"winusb":[],"winusbio":[],"winuser":[],"winver":[],"wlanapi":[],"wlanihv":[],"wlanihvtypes":[],"wlantypes":[],"wlclient":[],"wmistr":[],"wnnc":[],"wow64apiset":[],"wpdmtpextensions":[],"ws2bth":[],"ws2def":[],"ws2ipdef":[],"ws2spi":[],"ws2tcpip":[],"wtsapi32":[],"wtypes":[],"wtypesbase":[],"xinput":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-0.3.9/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","features":["everything","impl-debug","impl-default"],"targets":["aarch64-pc-windows-msvc","i686-pc-windows-msvc","x86_64-pc-windows-msvc"]}}},"publish":null,"authors":["Peter Atashian "],"categories":["external-ffi-bindings","no-std","os::windows-apis"],"keywords":["windows","ffi","win32","com","directx"],"readme":"README.md","repository":"https://github.com/retep998/winapi-rs","homepage":null,"documentation":"https://docs.rs/winapi/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"winapi-i686-pc-windows-gnu","version":"0.4.0","id":"registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0","license":"MIT/Apache-2.0","license_file":null,"description":"Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"winapi_i686_pc_windows_gnu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-i686-pc-windows-gnu-0.4.0/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml","metadata":null,"publish":null,"authors":["Peter Atashian "],"categories":[],"keywords":["windows"],"readme":null,"repository":"https://github.com/retep998/winapi-rs","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"winapi-util","version":"0.1.11","id":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","license":"Unlicense OR MIT","license_file":null,"description":"A dumping ground for high level safe wrappers over windows-sys.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.48.0, <=0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_Storage_FileSystem","Win32_System_Console","Win32_System_SystemInformation"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"winapi_util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-util-0.1.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-util-0.1.11/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-pc-windows-msvc"]}}},"publish":null,"authors":["Andrew Gallant "],"categories":["os::windows-apis","external-ffi-bindings"],"keywords":["windows","windows-sys","util","win"],"readme":"README.md","repository":"https://github.com/BurntSushi/winapi-util","homepage":"https://github.com/BurntSushi/winapi-util","documentation":"https://docs.rs/winapi-util","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"winapi-x86_64-pc-windows-gnu","version":"0.4.0","id":"registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0","license":"MIT/Apache-2.0","license_file":null,"description":"Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"winapi_x86_64_pc_windows_gnu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml","metadata":null,"publish":null,"authors":["Peter Atashian "],"categories":[],"keywords":["windows"],"readme":null,"repository":"https://github.com/retep998/winapi-rs","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"windows-link","version":"0.2.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Linking for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_link","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-link-0.2.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-link-0.2.1/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"windows-sys","version":"0.60.2","id":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.60.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Rust for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"windows-targets","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-sys-0.60.2/src/lib.rs","edition":"2021","doc":true,"doctest":false,"test":false}],"features":{"Wdk":["Win32_Foundation"],"Wdk_Devices":["Wdk"],"Wdk_Devices_Bluetooth":["Wdk_Devices"],"Wdk_Devices_HumanInterfaceDevice":["Wdk_Devices"],"Wdk_Foundation":["Wdk"],"Wdk_Graphics":["Wdk"],"Wdk_Graphics_Direct3D":["Wdk_Graphics"],"Wdk_NetworkManagement":["Wdk"],"Wdk_NetworkManagement_Ndis":["Wdk_NetworkManagement"],"Wdk_NetworkManagement_WindowsFilteringPlatform":["Wdk_NetworkManagement"],"Wdk_Storage":["Wdk"],"Wdk_Storage_FileSystem":["Wdk_Storage"],"Wdk_Storage_FileSystem_Minifilters":["Wdk_Storage_FileSystem"],"Wdk_System":["Wdk"],"Wdk_System_IO":["Wdk_System"],"Wdk_System_Memory":["Wdk_System"],"Wdk_System_OfflineRegistry":["Wdk_System"],"Wdk_System_Registry":["Wdk_System"],"Wdk_System_SystemInformation":["Wdk_System"],"Wdk_System_SystemServices":["Wdk_System"],"Wdk_System_Threading":["Wdk_System"],"Win32":["Win32_Foundation"],"Win32_Data":["Win32"],"Win32_Data_HtmlHelp":["Win32_Data"],"Win32_Data_RightsManagement":["Win32_Data"],"Win32_Devices":["Win32"],"Win32_Devices_AllJoyn":["Win32_Devices"],"Win32_Devices_Beep":["Win32_Devices"],"Win32_Devices_BiometricFramework":["Win32_Devices"],"Win32_Devices_Bluetooth":["Win32_Devices"],"Win32_Devices_Cdrom":["Win32_Devices"],"Win32_Devices_Communication":["Win32_Devices"],"Win32_Devices_DeviceAndDriverInstallation":["Win32_Devices"],"Win32_Devices_DeviceQuery":["Win32_Devices"],"Win32_Devices_Display":["Win32_Devices"],"Win32_Devices_Dvd":["Win32_Devices"],"Win32_Devices_Enumeration":["Win32_Devices"],"Win32_Devices_Enumeration_Pnp":["Win32_Devices_Enumeration"],"Win32_Devices_Fax":["Win32_Devices"],"Win32_Devices_HumanInterfaceDevice":["Win32_Devices"],"Win32_Devices_Nfc":["Win32_Devices"],"Win32_Devices_Nfp":["Win32_Devices"],"Win32_Devices_PortableDevices":["Win32_Devices"],"Win32_Devices_Properties":["Win32_Devices"],"Win32_Devices_Pwm":["Win32_Devices"],"Win32_Devices_Sensors":["Win32_Devices"],"Win32_Devices_SerialCommunication":["Win32_Devices"],"Win32_Devices_Tapi":["Win32_Devices"],"Win32_Devices_Usb":["Win32_Devices"],"Win32_Devices_WebServicesOnDevices":["Win32_Devices"],"Win32_Foundation":["Win32"],"Win32_Gaming":["Win32"],"Win32_Globalization":["Win32"],"Win32_Graphics":["Win32"],"Win32_Graphics_Dwm":["Win32_Graphics"],"Win32_Graphics_Gdi":["Win32_Graphics"],"Win32_Graphics_GdiPlus":["Win32_Graphics"],"Win32_Graphics_Hlsl":["Win32_Graphics"],"Win32_Graphics_OpenGL":["Win32_Graphics"],"Win32_Graphics_Printing":["Win32_Graphics"],"Win32_Graphics_Printing_PrintTicket":["Win32_Graphics_Printing"],"Win32_Management":["Win32"],"Win32_Management_MobileDeviceManagementRegistration":["Win32_Management"],"Win32_Media":["Win32"],"Win32_Media_Audio":["Win32_Media"],"Win32_Media_DxMediaObjects":["Win32_Media"],"Win32_Media_KernelStreaming":["Win32_Media"],"Win32_Media_Multimedia":["Win32_Media"],"Win32_Media_Streaming":["Win32_Media"],"Win32_Media_WindowsMediaFormat":["Win32_Media"],"Win32_NetworkManagement":["Win32"],"Win32_NetworkManagement_Dhcp":["Win32_NetworkManagement"],"Win32_NetworkManagement_Dns":["Win32_NetworkManagement"],"Win32_NetworkManagement_InternetConnectionWizard":["Win32_NetworkManagement"],"Win32_NetworkManagement_IpHelper":["Win32_NetworkManagement"],"Win32_NetworkManagement_Multicast":["Win32_NetworkManagement"],"Win32_NetworkManagement_Ndis":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetBios":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetManagement":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetShell":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetworkDiagnosticsFramework":["Win32_NetworkManagement"],"Win32_NetworkManagement_P2P":["Win32_NetworkManagement"],"Win32_NetworkManagement_QoS":["Win32_NetworkManagement"],"Win32_NetworkManagement_Rras":["Win32_NetworkManagement"],"Win32_NetworkManagement_Snmp":["Win32_NetworkManagement"],"Win32_NetworkManagement_WNet":["Win32_NetworkManagement"],"Win32_NetworkManagement_WebDav":["Win32_NetworkManagement"],"Win32_NetworkManagement_WiFi":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsConnectionManager":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsFilteringPlatform":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsFirewall":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsNetworkVirtualization":["Win32_NetworkManagement"],"Win32_Networking":["Win32"],"Win32_Networking_ActiveDirectory":["Win32_Networking"],"Win32_Networking_Clustering":["Win32_Networking"],"Win32_Networking_HttpServer":["Win32_Networking"],"Win32_Networking_Ldap":["Win32_Networking"],"Win32_Networking_WebSocket":["Win32_Networking"],"Win32_Networking_WinHttp":["Win32_Networking"],"Win32_Networking_WinInet":["Win32_Networking"],"Win32_Networking_WinSock":["Win32_Networking"],"Win32_Networking_WindowsWebServices":["Win32_Networking"],"Win32_Security":["Win32"],"Win32_Security_AppLocker":["Win32_Security"],"Win32_Security_Authentication":["Win32_Security"],"Win32_Security_Authentication_Identity":["Win32_Security_Authentication"],"Win32_Security_Authorization":["Win32_Security"],"Win32_Security_Credentials":["Win32_Security"],"Win32_Security_Cryptography":["Win32_Security"],"Win32_Security_Cryptography_Catalog":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_Certificates":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_Sip":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_UI":["Win32_Security_Cryptography"],"Win32_Security_DiagnosticDataQuery":["Win32_Security"],"Win32_Security_DirectoryServices":["Win32_Security"],"Win32_Security_EnterpriseData":["Win32_Security"],"Win32_Security_ExtensibleAuthenticationProtocol":["Win32_Security"],"Win32_Security_Isolation":["Win32_Security"],"Win32_Security_LicenseProtection":["Win32_Security"],"Win32_Security_NetworkAccessProtection":["Win32_Security"],"Win32_Security_WinTrust":["Win32_Security"],"Win32_Security_WinWlx":["Win32_Security"],"Win32_Storage":["Win32"],"Win32_Storage_Cabinets":["Win32_Storage"],"Win32_Storage_CloudFilters":["Win32_Storage"],"Win32_Storage_Compression":["Win32_Storage"],"Win32_Storage_DistributedFileSystem":["Win32_Storage"],"Win32_Storage_FileHistory":["Win32_Storage"],"Win32_Storage_FileSystem":["Win32_Storage"],"Win32_Storage_Imapi":["Win32_Storage"],"Win32_Storage_IndexServer":["Win32_Storage"],"Win32_Storage_InstallableFileSystems":["Win32_Storage"],"Win32_Storage_IscsiDisc":["Win32_Storage"],"Win32_Storage_Jet":["Win32_Storage"],"Win32_Storage_Nvme":["Win32_Storage"],"Win32_Storage_OfflineFiles":["Win32_Storage"],"Win32_Storage_OperationRecorder":["Win32_Storage"],"Win32_Storage_Packaging":["Win32_Storage"],"Win32_Storage_Packaging_Appx":["Win32_Storage_Packaging"],"Win32_Storage_ProjectedFileSystem":["Win32_Storage"],"Win32_Storage_StructuredStorage":["Win32_Storage"],"Win32_Storage_Vhd":["Win32_Storage"],"Win32_Storage_Xps":["Win32_Storage"],"Win32_System":["Win32"],"Win32_System_AddressBook":["Win32_System"],"Win32_System_Antimalware":["Win32_System"],"Win32_System_ApplicationInstallationAndServicing":["Win32_System"],"Win32_System_ApplicationVerifier":["Win32_System"],"Win32_System_ClrHosting":["Win32_System"],"Win32_System_Com":["Win32_System"],"Win32_System_Com_Marshal":["Win32_System_Com"],"Win32_System_Com_StructuredStorage":["Win32_System_Com"],"Win32_System_Com_Urlmon":["Win32_System_Com"],"Win32_System_ComponentServices":["Win32_System"],"Win32_System_Console":["Win32_System"],"Win32_System_CorrelationVector":["Win32_System"],"Win32_System_DataExchange":["Win32_System"],"Win32_System_DeploymentServices":["Win32_System"],"Win32_System_DeveloperLicensing":["Win32_System"],"Win32_System_Diagnostics":["Win32_System"],"Win32_System_Diagnostics_Ceip":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_Debug":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_Debug_Extensions":["Win32_System_Diagnostics_Debug"],"Win32_System_Diagnostics_Etw":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_ProcessSnapshotting":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_ToolHelp":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_TraceLogging":["Win32_System_Diagnostics"],"Win32_System_DistributedTransactionCoordinator":["Win32_System"],"Win32_System_Environment":["Win32_System"],"Win32_System_ErrorReporting":["Win32_System"],"Win32_System_EventCollector":["Win32_System"],"Win32_System_EventLog":["Win32_System"],"Win32_System_EventNotificationService":["Win32_System"],"Win32_System_GroupPolicy":["Win32_System"],"Win32_System_HostCompute":["Win32_System"],"Win32_System_HostComputeNetwork":["Win32_System"],"Win32_System_HostComputeSystem":["Win32_System"],"Win32_System_Hypervisor":["Win32_System"],"Win32_System_IO":["Win32_System"],"Win32_System_Iis":["Win32_System"],"Win32_System_Ioctl":["Win32_System"],"Win32_System_JobObjects":["Win32_System"],"Win32_System_Js":["Win32_System"],"Win32_System_Kernel":["Win32_System"],"Win32_System_LibraryLoader":["Win32_System"],"Win32_System_Mailslots":["Win32_System"],"Win32_System_Mapi":["Win32_System"],"Win32_System_Memory":["Win32_System"],"Win32_System_Memory_NonVolatile":["Win32_System_Memory"],"Win32_System_MessageQueuing":["Win32_System"],"Win32_System_MixedReality":["Win32_System"],"Win32_System_Ole":["Win32_System"],"Win32_System_PasswordManagement":["Win32_System"],"Win32_System_Performance":["Win32_System"],"Win32_System_Performance_HardwareCounterProfiling":["Win32_System_Performance"],"Win32_System_Pipes":["Win32_System"],"Win32_System_Power":["Win32_System"],"Win32_System_ProcessStatus":["Win32_System"],"Win32_System_Recovery":["Win32_System"],"Win32_System_Registry":["Win32_System"],"Win32_System_RemoteDesktop":["Win32_System"],"Win32_System_RemoteManagement":["Win32_System"],"Win32_System_RestartManager":["Win32_System"],"Win32_System_Restore":["Win32_System"],"Win32_System_Rpc":["Win32_System"],"Win32_System_Search":["Win32_System"],"Win32_System_Search_Common":["Win32_System_Search"],"Win32_System_SecurityCenter":["Win32_System"],"Win32_System_Services":["Win32_System"],"Win32_System_SetupAndMigration":["Win32_System"],"Win32_System_Shutdown":["Win32_System"],"Win32_System_StationsAndDesktops":["Win32_System"],"Win32_System_SubsystemForLinux":["Win32_System"],"Win32_System_SystemInformation":["Win32_System"],"Win32_System_SystemServices":["Win32_System"],"Win32_System_Threading":["Win32_System"],"Win32_System_Time":["Win32_System"],"Win32_System_TpmBaseServices":["Win32_System"],"Win32_System_UserAccessLogging":["Win32_System"],"Win32_System_Variant":["Win32_System"],"Win32_System_VirtualDosMachines":["Win32_System"],"Win32_System_WindowsProgramming":["Win32_System"],"Win32_System_Wmi":["Win32_System"],"Win32_UI":["Win32"],"Win32_UI_Accessibility":["Win32_UI"],"Win32_UI_ColorSystem":["Win32_UI"],"Win32_UI_Controls":["Win32_UI"],"Win32_UI_Controls_Dialogs":["Win32_UI_Controls"],"Win32_UI_HiDpi":["Win32_UI"],"Win32_UI_Input":["Win32_UI"],"Win32_UI_Input_Ime":["Win32_UI_Input"],"Win32_UI_Input_KeyboardAndMouse":["Win32_UI_Input"],"Win32_UI_Input_Pointer":["Win32_UI_Input"],"Win32_UI_Input_Touch":["Win32_UI_Input"],"Win32_UI_Input_XboxController":["Win32_UI_Input"],"Win32_UI_InteractionContext":["Win32_UI"],"Win32_UI_Magnification":["Win32_UI"],"Win32_UI_Shell":["Win32_UI"],"Win32_UI_Shell_Common":["Win32_UI_Shell"],"Win32_UI_Shell_PropertiesSystem":["Win32_UI_Shell"],"Win32_UI_TabletPC":["Win32_UI"],"Win32_UI_TextServices":["Win32_UI"],"Win32_UI_WindowsAndMessaging":["Win32_UI"],"Win32_Web":["Win32"],"Win32_Web_InternetExplorer":["Win32_Web"],"default":[],"docs":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-sys-0.60.2/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":["Microsoft"],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows-sys","version":"0.61.2","id":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Rust for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"windows-link","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-sys-0.61.2/src/lib.rs","edition":"2021","doc":true,"doctest":false,"test":false}],"features":{"Wdk":["Win32_Foundation"],"Wdk_Devices":["Wdk"],"Wdk_Devices_Bluetooth":["Wdk_Devices"],"Wdk_Devices_HumanInterfaceDevice":["Wdk_Devices"],"Wdk_Foundation":["Wdk"],"Wdk_Graphics":["Wdk"],"Wdk_Graphics_Direct3D":["Wdk_Graphics"],"Wdk_NetworkManagement":["Wdk"],"Wdk_NetworkManagement_Ndis":["Wdk_NetworkManagement"],"Wdk_NetworkManagement_WindowsFilteringPlatform":["Wdk_NetworkManagement"],"Wdk_Storage":["Wdk"],"Wdk_Storage_FileSystem":["Wdk_Storage"],"Wdk_Storage_FileSystem_Minifilters":["Wdk_Storage_FileSystem"],"Wdk_System":["Wdk"],"Wdk_System_IO":["Wdk_System"],"Wdk_System_Memory":["Wdk_System"],"Wdk_System_OfflineRegistry":["Wdk_System"],"Wdk_System_Registry":["Wdk_System"],"Wdk_System_SystemInformation":["Wdk_System"],"Wdk_System_SystemServices":["Wdk_System"],"Wdk_System_Threading":["Wdk_System"],"Win32":["Win32_Foundation"],"Win32_Data":["Win32"],"Win32_Data_HtmlHelp":["Win32_Data"],"Win32_Data_RightsManagement":["Win32_Data"],"Win32_Devices":["Win32"],"Win32_Devices_AllJoyn":["Win32_Devices"],"Win32_Devices_Beep":["Win32_Devices"],"Win32_Devices_BiometricFramework":["Win32_Devices"],"Win32_Devices_Bluetooth":["Win32_Devices"],"Win32_Devices_Cdrom":["Win32_Devices"],"Win32_Devices_Communication":["Win32_Devices"],"Win32_Devices_DeviceAndDriverInstallation":["Win32_Devices"],"Win32_Devices_DeviceQuery":["Win32_Devices"],"Win32_Devices_Display":["Win32_Devices"],"Win32_Devices_Dvd":["Win32_Devices"],"Win32_Devices_Enumeration":["Win32_Devices"],"Win32_Devices_Enumeration_Pnp":["Win32_Devices_Enumeration"],"Win32_Devices_Fax":["Win32_Devices"],"Win32_Devices_HumanInterfaceDevice":["Win32_Devices"],"Win32_Devices_Nfc":["Win32_Devices"],"Win32_Devices_Nfp":["Win32_Devices"],"Win32_Devices_PortableDevices":["Win32_Devices"],"Win32_Devices_Properties":["Win32_Devices"],"Win32_Devices_Pwm":["Win32_Devices"],"Win32_Devices_Sensors":["Win32_Devices"],"Win32_Devices_SerialCommunication":["Win32_Devices"],"Win32_Devices_Tapi":["Win32_Devices"],"Win32_Devices_Usb":["Win32_Devices"],"Win32_Devices_WebServicesOnDevices":["Win32_Devices"],"Win32_Foundation":["Win32"],"Win32_Gaming":["Win32"],"Win32_Globalization":["Win32"],"Win32_Graphics":["Win32"],"Win32_Graphics_Dwm":["Win32_Graphics"],"Win32_Graphics_Gdi":["Win32_Graphics"],"Win32_Graphics_GdiPlus":["Win32_Graphics"],"Win32_Graphics_Hlsl":["Win32_Graphics"],"Win32_Graphics_OpenGL":["Win32_Graphics"],"Win32_Graphics_Printing":["Win32_Graphics"],"Win32_Graphics_Printing_PrintTicket":["Win32_Graphics_Printing"],"Win32_Management":["Win32"],"Win32_Management_MobileDeviceManagementRegistration":["Win32_Management"],"Win32_Media":["Win32"],"Win32_Media_Audio":["Win32_Media"],"Win32_Media_DxMediaObjects":["Win32_Media"],"Win32_Media_KernelStreaming":["Win32_Media"],"Win32_Media_Multimedia":["Win32_Media"],"Win32_Media_Streaming":["Win32_Media"],"Win32_Media_WindowsMediaFormat":["Win32_Media"],"Win32_NetworkManagement":["Win32"],"Win32_NetworkManagement_Dhcp":["Win32_NetworkManagement"],"Win32_NetworkManagement_Dns":["Win32_NetworkManagement"],"Win32_NetworkManagement_InternetConnectionWizard":["Win32_NetworkManagement"],"Win32_NetworkManagement_IpHelper":["Win32_NetworkManagement"],"Win32_NetworkManagement_Multicast":["Win32_NetworkManagement"],"Win32_NetworkManagement_Ndis":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetBios":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetManagement":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetShell":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetworkDiagnosticsFramework":["Win32_NetworkManagement"],"Win32_NetworkManagement_P2P":["Win32_NetworkManagement"],"Win32_NetworkManagement_QoS":["Win32_NetworkManagement"],"Win32_NetworkManagement_Rras":["Win32_NetworkManagement"],"Win32_NetworkManagement_Snmp":["Win32_NetworkManagement"],"Win32_NetworkManagement_WNet":["Win32_NetworkManagement"],"Win32_NetworkManagement_WebDav":["Win32_NetworkManagement"],"Win32_NetworkManagement_WiFi":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsConnectionManager":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsFilteringPlatform":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsFirewall":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsNetworkVirtualization":["Win32_NetworkManagement"],"Win32_Networking":["Win32"],"Win32_Networking_ActiveDirectory":["Win32_Networking"],"Win32_Networking_Clustering":["Win32_Networking"],"Win32_Networking_HttpServer":["Win32_Networking"],"Win32_Networking_Ldap":["Win32_Networking"],"Win32_Networking_WebSocket":["Win32_Networking"],"Win32_Networking_WinHttp":["Win32_Networking"],"Win32_Networking_WinInet":["Win32_Networking"],"Win32_Networking_WinSock":["Win32_Networking"],"Win32_Networking_WindowsWebServices":["Win32_Networking"],"Win32_Security":["Win32"],"Win32_Security_AppLocker":["Win32_Security"],"Win32_Security_Authentication":["Win32_Security"],"Win32_Security_Authentication_Identity":["Win32_Security_Authentication"],"Win32_Security_Authorization":["Win32_Security"],"Win32_Security_Credentials":["Win32_Security"],"Win32_Security_Cryptography":["Win32_Security"],"Win32_Security_Cryptography_Catalog":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_Certificates":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_Sip":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_UI":["Win32_Security_Cryptography"],"Win32_Security_DiagnosticDataQuery":["Win32_Security"],"Win32_Security_DirectoryServices":["Win32_Security"],"Win32_Security_EnterpriseData":["Win32_Security"],"Win32_Security_ExtensibleAuthenticationProtocol":["Win32_Security"],"Win32_Security_Isolation":["Win32_Security"],"Win32_Security_LicenseProtection":["Win32_Security"],"Win32_Security_NetworkAccessProtection":["Win32_Security"],"Win32_Security_WinTrust":["Win32_Security"],"Win32_Security_WinWlx":["Win32_Security"],"Win32_Storage":["Win32"],"Win32_Storage_Cabinets":["Win32_Storage"],"Win32_Storage_CloudFilters":["Win32_Storage"],"Win32_Storage_Compression":["Win32_Storage"],"Win32_Storage_DistributedFileSystem":["Win32_Storage"],"Win32_Storage_FileHistory":["Win32_Storage"],"Win32_Storage_FileSystem":["Win32_Storage"],"Win32_Storage_Imapi":["Win32_Storage"],"Win32_Storage_IndexServer":["Win32_Storage"],"Win32_Storage_InstallableFileSystems":["Win32_Storage"],"Win32_Storage_IscsiDisc":["Win32_Storage"],"Win32_Storage_Jet":["Win32_Storage"],"Win32_Storage_Nvme":["Win32_Storage"],"Win32_Storage_OfflineFiles":["Win32_Storage"],"Win32_Storage_OperationRecorder":["Win32_Storage"],"Win32_Storage_Packaging":["Win32_Storage"],"Win32_Storage_Packaging_Appx":["Win32_Storage_Packaging"],"Win32_Storage_ProjectedFileSystem":["Win32_Storage"],"Win32_Storage_StructuredStorage":["Win32_Storage"],"Win32_Storage_Vhd":["Win32_Storage"],"Win32_Storage_Xps":["Win32_Storage"],"Win32_System":["Win32"],"Win32_System_AddressBook":["Win32_System"],"Win32_System_Antimalware":["Win32_System"],"Win32_System_ApplicationInstallationAndServicing":["Win32_System"],"Win32_System_ApplicationVerifier":["Win32_System"],"Win32_System_ClrHosting":["Win32_System"],"Win32_System_Com":["Win32_System"],"Win32_System_Com_Marshal":["Win32_System_Com"],"Win32_System_Com_StructuredStorage":["Win32_System_Com"],"Win32_System_Com_Urlmon":["Win32_System_Com"],"Win32_System_ComponentServices":["Win32_System"],"Win32_System_Console":["Win32_System"],"Win32_System_CorrelationVector":["Win32_System"],"Win32_System_DataExchange":["Win32_System"],"Win32_System_DeploymentServices":["Win32_System"],"Win32_System_DeveloperLicensing":["Win32_System"],"Win32_System_Diagnostics":["Win32_System"],"Win32_System_Diagnostics_Ceip":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_Debug":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_Debug_Extensions":["Win32_System_Diagnostics_Debug"],"Win32_System_Diagnostics_Etw":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_ProcessSnapshotting":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_ToolHelp":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_TraceLogging":["Win32_System_Diagnostics"],"Win32_System_DistributedTransactionCoordinator":["Win32_System"],"Win32_System_Environment":["Win32_System"],"Win32_System_ErrorReporting":["Win32_System"],"Win32_System_EventCollector":["Win32_System"],"Win32_System_EventLog":["Win32_System"],"Win32_System_EventNotificationService":["Win32_System"],"Win32_System_GroupPolicy":["Win32_System"],"Win32_System_HostCompute":["Win32_System"],"Win32_System_HostComputeNetwork":["Win32_System"],"Win32_System_HostComputeSystem":["Win32_System"],"Win32_System_Hypervisor":["Win32_System"],"Win32_System_IO":["Win32_System"],"Win32_System_Iis":["Win32_System"],"Win32_System_Ioctl":["Win32_System"],"Win32_System_JobObjects":["Win32_System"],"Win32_System_Js":["Win32_System"],"Win32_System_Kernel":["Win32_System"],"Win32_System_LibraryLoader":["Win32_System"],"Win32_System_Mailslots":["Win32_System"],"Win32_System_Mapi":["Win32_System"],"Win32_System_Memory":["Win32_System"],"Win32_System_Memory_NonVolatile":["Win32_System_Memory"],"Win32_System_MessageQueuing":["Win32_System"],"Win32_System_MixedReality":["Win32_System"],"Win32_System_Ole":["Win32_System"],"Win32_System_PasswordManagement":["Win32_System"],"Win32_System_Performance":["Win32_System"],"Win32_System_Performance_HardwareCounterProfiling":["Win32_System_Performance"],"Win32_System_Pipes":["Win32_System"],"Win32_System_Power":["Win32_System"],"Win32_System_ProcessStatus":["Win32_System"],"Win32_System_Recovery":["Win32_System"],"Win32_System_Registry":["Win32_System"],"Win32_System_RemoteDesktop":["Win32_System"],"Win32_System_RemoteManagement":["Win32_System"],"Win32_System_RestartManager":["Win32_System"],"Win32_System_Restore":["Win32_System"],"Win32_System_Rpc":["Win32_System"],"Win32_System_Search":["Win32_System"],"Win32_System_Search_Common":["Win32_System_Search"],"Win32_System_SecurityCenter":["Win32_System"],"Win32_System_Services":["Win32_System"],"Win32_System_SetupAndMigration":["Win32_System"],"Win32_System_Shutdown":["Win32_System"],"Win32_System_StationsAndDesktops":["Win32_System"],"Win32_System_SubsystemForLinux":["Win32_System"],"Win32_System_SystemInformation":["Win32_System"],"Win32_System_SystemServices":["Win32_System"],"Win32_System_Threading":["Win32_System"],"Win32_System_Time":["Win32_System"],"Win32_System_TpmBaseServices":["Win32_System"],"Win32_System_UserAccessLogging":["Win32_System"],"Win32_System_Variant":["Win32_System"],"Win32_System_VirtualDosMachines":["Win32_System"],"Win32_System_WindowsProgramming":["Win32_System"],"Win32_System_Wmi":["Win32_System"],"Win32_UI":["Win32"],"Win32_UI_Accessibility":["Win32_UI"],"Win32_UI_ColorSystem":["Win32_UI"],"Win32_UI_Controls":["Win32_UI"],"Win32_UI_Controls_Dialogs":["Win32_UI_Controls"],"Win32_UI_HiDpi":["Win32_UI"],"Win32_UI_Input":["Win32_UI"],"Win32_UI_Input_Ime":["Win32_UI_Input"],"Win32_UI_Input_KeyboardAndMouse":["Win32_UI_Input"],"Win32_UI_Input_Pointer":["Win32_UI_Input"],"Win32_UI_Input_Touch":["Win32_UI_Input"],"Win32_UI_Input_XboxController":["Win32_UI_Input"],"Win32_UI_InteractionContext":["Win32_UI"],"Win32_UI_Magnification":["Win32_UI"],"Win32_UI_Shell":["Win32_UI"],"Win32_UI_Shell_Common":["Win32_UI_Shell"],"Win32_UI_Shell_PropertiesSystem":["Win32_UI_Shell"],"Win32_UI_TabletPC":["Win32_UI"],"Win32_UI_TextServices":["Win32_UI"],"Win32_UI_WindowsAndMessaging":["Win32_UI"],"Win32_Web":["Win32"],"Win32_Web_InternetExplorer":["Win32_Web"],"default":[],"docs":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-sys-0.61.2/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[],"all-features":true}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"windows-targets","version":"0.53.5","id":"registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.53.5","license":"MIT OR Apache-2.0","license_file":null,"description":"Import libs for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"windows_aarch64_gnullvm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"aarch64-pc-windows-gnullvm","registry":null},{"name":"windows_x86_64_msvc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(any(target_arch = \"x86_64\", target_arch = \"arm64ec\"), target_env = \"msvc\", not(windows_raw_dylib)))","registry":null},{"name":"windows_aarch64_msvc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))","registry":null},{"name":"windows_i686_gnu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"x86\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))","registry":null},{"name":"windows_i686_msvc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"x86\", target_env = \"msvc\", not(windows_raw_dylib)))","registry":null},{"name":"windows_x86_64_gnu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"x86_64\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))","registry":null},{"name":"windows-link","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(windows_raw_dylib)","registry":null},{"name":"windows_i686_gnullvm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"i686-pc-windows-gnullvm","registry":null},{"name":"windows_x86_64_gnullvm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"x86_64-pc-windows-gnullvm","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_targets","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-targets-0.53.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-targets-0.53.5/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_aarch64_gnullvm","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_aarch64_gnullvm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_gnullvm-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_gnullvm-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_gnullvm-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_aarch64_msvc","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_aarch64_msvc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_msvc-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_msvc-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_msvc-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_i686_gnu","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_i686_gnu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnu-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnu-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnu-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_i686_gnullvm","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnullvm@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_i686_gnullvm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnullvm-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnullvm-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnullvm-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_i686_msvc","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_i686_msvc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_msvc-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_msvc-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_msvc-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_x86_64_gnu","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_x86_64_gnu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnu-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnu-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnu-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_x86_64_gnullvm","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_x86_64_gnullvm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnullvm-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnullvm-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnullvm-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_x86_64_msvc","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_x86_64_msvc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_msvc-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_msvc-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_msvc-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"wit-bindgen","version":"0.57.1","id":"registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"Rust bindings generator and runtime support for WIT and the component model.\nUsed when compiling Rust programs to the component model.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"alloc","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.11.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.30","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wit-bindgen-rust-macro","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.57.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wit_bindgen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wit-bindgen-0.57.1/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wit-bindgen-0.57.1/build.rs","edition":"2024","doc":false,"doctest":false,"test":false}],"features":{"async":[],"async-spawn":["async","dep:futures","std"],"bitflags":["dep:bitflags"],"default":["macros","realloc","async","std","bitflags","macro-string"],"futures-stream":["async","dep:futures"],"inter-task-wakeup":["async"],"macro-string":["wit-bindgen-rust-macro?/macro-string"],"macros":["dep:wit-bindgen-rust-macro"],"realloc":[],"rustc-dep-of-std":["dep:core","dep:alloc"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wit-bindgen-0.57.1/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["Alex Crichton "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/bytecodealliance/wit-bindgen","homepage":"https://github.com/bytecodealliance/wit-bindgen","documentation":null,"edition":"2024","links":null,"default_run":null,"rust_version":"1.85.0"},{"name":"writeable","version":"0.6.3","id":"registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","license":"Unicode-3.0","license_file":null,"description":"A more efficient alternative to fmt::Display","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"either","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.9.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"writeable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"writeable_message","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/examples/writeable_message.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"writeable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/tests/writeable.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"writeable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/benches/writeable.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"default":["alloc"],"either":["dep:either"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":[],"keywords":["formatting","write","string","no-std"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"xattr","version":"1.6.1","id":"registry+https://github.com/rust-lang/crates.io-index#xattr@1.6.1","license":"MIT OR Apache-2.0","license_file":null,"description":"unix extended filesystem attributes","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustix","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["fs","std"],"target":"cfg(any(target_os = \"android\", target_os = \"linux\", target_os = \"macos\", target_os = \"hurd\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.150","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(target_os = \"freebsd\", target_os = \"netbsd\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"xattr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/xattr-1.6.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"main","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/xattr-1.6.1/tests/main.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["unsupported"],"unsupported":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/xattr-1.6.1/Cargo.toml","metadata":null,"publish":null,"authors":["Steven Allen "],"categories":[],"keywords":["xattr","filesystem","unix"],"readme":"README.md","repository":"https://github.com/Stebalien/xattr","homepage":null,"documentation":"https://docs.rs/xattr","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"xtask","version":"3.0.4","id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask#3.0.4","license":null,"license_file":null,"description":null,"source":null,"dependencies":[{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sha2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"yaml_serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["bin"],"crate_types":["bin"],"name":"xtask","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask/src/main.rs","edition":"2021","doc":true,"doctest":false,"test":true}],"features":{},"manifest_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask/Cargo.toml","metadata":null,"publish":[],"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"yaml_serde","version":"0.10.7","id":"registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7","license":"MIT OR Apache-2.0","license_file":null,"description":"serde_yaml maintained by The YAML Organization","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"indexmap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libyaml-rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ryu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.195","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.79","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"indoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.195","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"yaml_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_de","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/tests/test_de.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_error","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/tests/test_error.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/tests/test_serde.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_value","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/tests/test_value.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"std":["serde/std","indexmap/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["YAML Organization "],"categories":["encoding","parser-implementations"],"keywords":["yaml","serde","data","serialization"],"readme":"README.md","repository":"https://github.com/yaml/yaml-serde","homepage":null,"documentation":"https://docs.rs/yaml_serde/","edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"yoke","version":"0.8.3","id":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","license":"Unicode-3.0","license_file":null,"description":"Abstraction allowing borrowed data to be carried along with the backing data it borrows from","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"stable_deref_trait","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"yoke-derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"yoke","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"bincode","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.3/tests/bincode.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"miri","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.3/tests/miri.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"alloc":["stable_deref_trait/alloc","zerofrom/alloc"],"default":["alloc","zerofrom"],"derive":["dep:yoke-derive","zerofrom/derive"],"serde":[],"zerofrom":["dep:zerofrom"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.3/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["Manish Goregaokar "],"categories":["data-structures","memory-management","caching","no-std"],"keywords":["zero-copy","serialization","memory-efficiency","self-referential"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"yoke-derive","version":"0.8.2","id":"registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.8.2","license":"Unicode-3.0","license_file":null,"description":"Custom derive for the yoke crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.44","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.21","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["fold"],"target":null,"registry":null},{"name":"synstructure","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"yoke_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-derive-0.8.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"yoke_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-derive-0.8.2/examples/yoke_derive.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-derive-0.8.2/Cargo.toml","metadata":{"workspaces":{"independent":true}},"publish":null,"authors":["Manish Goregaokar "],"categories":["data-structures","memory-management","caching","no-std"],"keywords":["macros","derive","zero-copy","serialization","self-referential"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"zerofrom","version":"0.1.8","id":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","license":"Unicode-3.0","license_file":null,"description":"ZeroFrom trait for constructing","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"zerofrom-derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zerofrom","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-0.1.8/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["alloc"],"derive":["dep:zerofrom-derive"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-0.1.8/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"playground":{"all-features":true}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["data-structures","caching","no-std"],"keywords":["zero-copy","serialization","memory-efficiency"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.1"},{"name":"zerofrom-derive","version":"0.1.7","id":"registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.7","license":"Unicode-3.0","license_file":null,"description":"Custom derive for the zerofrom crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.44","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.21","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["fold"],"target":null,"registry":null},{"name":"synstructure","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"zerofrom_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-derive-0.1.7/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"zf_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-derive-0.1.7/examples/zf_derive.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-derive-0.1.7/Cargo.toml","metadata":{"workspaces":{"independent":true}},"publish":null,"authors":["Manish Goregaokar "],"categories":["data-structures","memory-management","caching","no-std"],"keywords":["macros","derive","zero-copy","serialization"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.1"},{"name":"zerotrie","version":"0.2.4","id":"registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","license":"Unicode-3.0","license_file":null,"description":"A data structure that efficiently maps strings to integers","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"litemap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"icu_locale_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"itertools","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.14.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_pcg","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rmp-serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zerotrie","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"first_weekday_for_region","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/examples/first_weekday_for_region.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"asciitrie_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/asciitrie_test.rs","edition":"2021","required-features":["alloc","litemap"],"doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"builder_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/builder_test.rs","edition":"2021","required-features":["alloc","litemap"],"doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dense_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/dense_test.rs","edition":"2021","required-features":["alloc","dense"],"doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"derive_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/derive_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"ignorecase_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/ignorecase_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"locale_aux_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/locale_aux_test.rs","edition":"2021","required-features":["alloc","litemap"],"doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"overview","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/benches/overview.rs","edition":"2021","required-features":["alloc","litemap"],"doc":false,"doctest":false,"test":false}],"features":{"alloc":["zerovec?/alloc"],"databake":["dep:databake","zerovec?/databake"],"default":[],"dense":["dep:zerovec"],"litemap":["dep:litemap","alloc"],"serde":["dep:serde_core","dep:litemap","alloc","litemap/serde","zerovec?/serde"],"yoke":["dep:yoke"],"zerofrom":["dep:zerofrom"],"zerovec":["dep:zerovec"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["zero-copy","serialization","memory-efficiency","trie"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"zerovec","version":"0.11.6","id":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","license":"Unicode-3.0","license_file":null,"description":"Zero-copy vector backed by a byte array","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"schemars","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"twox-hash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["xxhash64"],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec-derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["wasm_js"],"target":null,"registry":null},{"name":"iai","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.43.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["json"],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["use-std"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_distr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_pcg","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rmp-serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zerovec","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"zv_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/examples/zv_serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"vzv","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/vzv.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"zeromap","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/zeromap.rs","edition":"2021","required-features":["serde","hashmap","derive"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"zerovec","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/zerovec.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"zerovec_iai","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/zerovec_iai.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"zerovec_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/zerovec_serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde?/alloc"],"databake":["dep:databake"],"derive":["dep:zerovec-derive"],"hashmap":["dep:twox-hash","alloc"],"schemars":["dep:schemars","alloc"],"serde":["dep:serde"],"std":[],"yoke":["dep:yoke"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["rust-patterns","memory-management","caching","no-std","data-structures"],"keywords":["zero-copy","serialization","memory-efficiency","serde"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.83"},{"name":"zerovec-derive","version":"0.11.3","id":"registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.11.3","license":"Unicode-3.0","license_file":null,"description":"Custom derive for the zerovec crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.44","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.21","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["extra-traits"],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"zerovec_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"derives","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/examples/derives.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"make","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/examples/make.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"make_var","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/examples/make_var.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/Cargo.toml","metadata":{"workspaces":{"independent":true}},"publish":null,"authors":["Manish Goregaokar "],"categories":["rust-patterns","memory-management","caching","no-std","data-structures"],"keywords":["macros","derive","serialization","zero-copy"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"zlib-rs","version":"0.6.8","id":"registry+https://github.com/rust-lang/crates.io-index#zlib-rs@0.6.8","license":"Zlib","license_file":null,"description":"A memory-safe zlib implementation written in rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"crc32fast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memoffset","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zlib_rs","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zlib-rs-0.6.8/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"ZLIB_DEBUG":[],"__internal-api":[],"__internal-fuzz":["arbitrary"],"__internal-fuzz-disable-checksum":[],"__internal-test":["quickcheck"],"arbitrary":["dep:arbitrary"],"avx512":["vpclmulqdq"],"c-allocator":[],"default":["std","c-allocator"],"lsx":[],"quickcheck":["dep:quickcheck"],"rust-allocator":[],"std":["rust-allocator"],"vpclmulqdq":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zlib-rs-0.6.8/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/trifectatechfoundation/zlib-rs","homepage":"https://github.com/trifectatechfoundation/zlib-rs","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.75"},{"name":"zmij","version":"1.0.21","id":"registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.21","license":"MIT","license_file":null,"description":"A double-to-string conversion algorithm based on Schubfach and yy","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"no-panic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.36","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num-bigint","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num-integer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num_cpus","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"opt-level","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ryu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zmij","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"exhaustive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/tests/exhaustive.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"ryu_comparison","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/tests/ryu_comparison.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"no-panic":["dep:no-panic"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["value-formatting","no-std","no-std::no-alloc"],"keywords":["float"],"readme":"README.md","repository":"https://github.com/dtolnay/zmij","homepage":null,"documentation":"https://docs.rs/zmij","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"}],"workspace_members":["path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki#3.0.4","path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask#3.0.4"],"workspace_default_members":["path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4"],"resolve":{"nodes":[{"id":"registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2"],"deps":[{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]}],"features":["perf-literal","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#ammonia@4.1.8","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cssparser@0.37.0","registry+https://github.com/rust-lang/crates.io-index#html5ever@0.39.0","registry+https://github.com/rust-lang/crates.io-index#maplit@1.0.2","registry+https://github.com/rust-lang/crates.io-index#url@2.5.8"],"deps":[{"name":"cssparser","pkg":"registry+https://github.com/rust-lang/crates.io-index#cssparser@0.37.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"html5ever","pkg":"registry+https://github.com/rust-lang/crates.io-index#html5ever@0.39.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"maplit","pkg":"registry+https://github.com/rust-lang/crates.io-index#maplit@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"url","pkg":"registry+https://github.com/rust-lang/crates.io-index#url@2.5.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#axum@0.8.9","dependencies":["registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.6","registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.20","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#matchit@0.8.4","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.20","registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","registry+https://github.com/rust-lang/crates.io-index#tower@0.5.3","registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3"],"deps":[{"name":"axum_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.6","dep_kinds":[{"kind":null,"target":null}]},{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"hyper","pkg":"registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"hyper_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.20","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"matchit","pkg":"registry+https://github.com/rust-lang/crates.io-index#matchit@0.8.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"mime","pkg":"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"percent_encoding","pkg":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_path_to_error","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.20","dep_kinds":[{"kind":null,"target":null}]},{"name":"sync_wrapper","pkg":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower@0.5.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_layer","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_service","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["http1","json","tokio"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.6","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"mime","pkg":"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"sync_wrapper","pkg":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_layer","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_service","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dependencies":[],"deps":[],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7"],"deps":[{"name":"generic_array","pkg":"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.20.3","dependencies":[],"deps":[],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dependencies":["registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9","registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1"],"deps":[{"name":"find_msvc_tools","pkg":"registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"shlex","pkg":"registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cmake@0.1.58","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#fs2@0.4.3","registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4","registry+https://github.com/rust-lang/crates.io-index#lbug@0.19.0","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","registry+https://github.com/rust-lang/crates.io-index#lzma-rs@0.3.0","registry+https://github.com/rust-lang/crates.io-index#mimalloc@0.1.52","registry+https://github.com/rust-lang/crates.io-index#notify@8.2.0","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","registry+https://github.com/rust-lang/crates.io-index#tree-sitter@0.25.10","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-c@0.24.2","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-cpp@0.23.4","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-css@0.25.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-fortran@0.6.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-go@0.25.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-html@0.23.2","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-javascript@0.25.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-python@0.25.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-rust@0.24.2","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-typescript@0.23.2","registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"fs2","pkg":"registry+https://github.com/rust-lang/crates.io-index#fs2@0.4.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"getrandom","pkg":"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"lbug","pkg":"registry+https://github.com/rust-lang/crates.io-index#lbug@0.19.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]},{"name":"libmimalloc_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","dep_kinds":[{"kind":null,"target":null}]},{"name":"lzma_rs","pkg":"registry+https://github.com/rust-lang/crates.io-index#lzma-rs@0.3.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"mimalloc","pkg":"registry+https://github.com/rust-lang/crates.io-index#mimalloc@0.1.52","dep_kinds":[{"kind":null,"target":null}]},{"name":"notify","pkg":"registry+https://github.com/rust-lang/crates.io-index#notify@8.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"sha2","pkg":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter@0.25.10","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_c","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-c@0.24.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_cpp","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-cpp@0.23.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_css","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-css@0.25.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_fortran","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-fortran@0.6.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_go","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-go@0.25.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_html","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-html@0.23.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_javascript","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-javascript@0.25.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_python","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-python@0.25.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_rust","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-rust@0.24.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_typescript","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-typescript@0.23.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"walkdir","pkg":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#codespan-reporting@0.11.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1","registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.14"],"deps":[{"name":"termcolor","pkg":"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"unicode_width","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.14","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"aarch64-linux-android"},{"kind":null,"target":"cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))"},{"kind":null,"target":"cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))"},{"kind":null,"target":"cfg(all(target_arch = \"loongarch64\", target_os = \"linux\"))"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.5.0"],"deps":[{"name":"crc_catalog","pkg":"registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.5.0","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.5.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]}],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1"],"deps":[{"name":"generic_array","pkg":"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"typenum","pkg":"registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","dep_kinds":[{"kind":null,"target":null}]}],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cssparser@0.37.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#dtoa-short@0.3.5","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2"],"deps":[{"name":"dtoa_short","pkg":"registry+https://github.com/rust-lang/crates.io-index#dtoa-short@0.3.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cty@0.2.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cxx@1.0.138","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#cxxbridge-flags@1.0.138","registry+https://github.com/rust-lang/crates.io-index#cxxbridge-macro@1.0.138","registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5","registry+https://github.com/rust-lang/crates.io-index#link-cplusplus@1.0.12"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"cxxbridge_flags","pkg":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-flags@1.0.138","dep_kinds":[{"kind":"build","target":null}]},{"name":"cxxbridge_macro","pkg":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-macro@1.0.138","dep_kinds":[{"kind":null,"target":null}]},{"name":"foldhash","pkg":"registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"link_cplusplus","pkg":"registry+https://github.com/rust-lang/crates.io-index#link-cplusplus@1.0.12","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cxx-build@1.0.138","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#codespan-reporting@0.11.1","registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#scratch@1.0.9","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":null,"target":null}]},{"name":"codespan_reporting","pkg":"registry+https://github.com/rust-lang/crates.io-index#codespan-reporting@0.11.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"scratch","pkg":"registry+https://github.com/rust-lang/crates.io-index#scratch@1.0.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-flags@1.0.138","dependencies":[],"deps":[],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-macro@1.0.138","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"rustversion","pkg":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11","dependencies":["registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0"],"deps":[{"name":"powerfmt","pkg":"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","powerfmt","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4","registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.7"],"deps":[{"name":"block_buffer","pkg":"registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"crypto_common","pkg":"registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.7","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","block-buffer","core-api","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#dtoa@1.0.11","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#dtoa-short@0.3.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#dtoa@1.0.11"],"deps":[{"name":"dtoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#dtoa@1.0.11","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"},{"kind":null,"target":"cfg(target_os = \"hermit\")"},{"kind":null,"target":"cfg(target_os = \"wasi\")"}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.5.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#filetime@0.2.29","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.10","dependencies":["registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0","registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.9.1","registry+https://github.com/rust-lang/crates.io-index#zlib-rs@0.6.8"],"deps":[{"name":"crc32fast","pkg":"registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"miniz_oxide","pkg":"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.9.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"zlib_rs","pkg":"registry+https://github.com/rust-lang/crates.io-index#zlib-rs@0.6.8","dep_kinds":[{"kind":null,"target":null}]}],"features":["any_impl","default","miniz_oxide","runtime_detection","rust_backend"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5","dependencies":[],"deps":[],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2"],"deps":[{"name":"percent_encoding","pkg":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#fs2@0.4.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]},{"name":"winapi","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#fsevent-sys@4.1.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.32","dependencies":["registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32"],"deps":[{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dependencies":[],"deps":[],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.32","dependencies":[],"deps":[],"features":["alloc","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","dependencies":["registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.32","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#slab@0.4.12"],"deps":[{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_task","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"slab","pkg":"registry+https://github.com/rust-lang/crates.io-index#slab@0.4.12","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","slab","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5"],"deps":[{"name":"typenum","pkg":"registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"version_check","pkg":"registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5","dep_kinds":[{"kind":"build","target":null}]}],"features":["more_lengths"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0","registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(all(any(target_os = \"linux\", target_os = \"android\"), not(any(all(target_os = \"linux\", target_env = \"\"), getrandom_backend = \"custom\", getrandom_backend = \"linux_raw\", getrandom_backend = \"rdrand\", getrandom_backend = \"rndr\"))))"},{"kind":null,"target":"cfg(any(target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"hurd\", target_os = \"illumos\", target_os = \"cygwin\", all(target_os = \"horizon\", target_arch = \"arm\")))"},{"kind":null,"target":"cfg(any(target_os = \"haiku\", target_os = \"redox\", target_os = \"nto\", target_os = \"aix\"))"},{"kind":null,"target":"cfg(any(target_os = \"ios\", target_os = \"visionos\", target_os = \"watchos\", target_os = \"tvos\"))"},{"kind":null,"target":"cfg(any(target_os = \"macos\", target_os = \"openbsd\", target_os = \"vita\", target_os = \"emscripten\"))"},{"kind":null,"target":"cfg(target_os = \"netbsd\")"},{"kind":null,"target":"cfg(target_os = \"solaris\")"},{"kind":null,"target":"cfg(target_os = \"vxworks\")"}]},{"name":"r_efi","pkg":"registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0","dep_kinds":[{"kind":null,"target":"cfg(all(target_os = \"uefi\", getrandom_backend = \"efi_rng\"))"}]},{"name":"wasip2","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"wasm32\", target_os = \"wasi\", target_env = \"p2\"))"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.5","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#html5ever@0.39.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","registry+https://github.com/rust-lang/crates.io-index#markup5ever@0.39.0"],"deps":[{"name":"log","pkg":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"markup5ever","pkg":"registry+https://github.com/rust-lang/crates.io-index#markup5ever@0.39.0","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2","registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.32","registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1","registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1"],"deps":[{"name":"atomic_waker","pkg":"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_channel","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"httparse","pkg":"registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"httpdate","pkg":"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","http1","server"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.20","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"hyper","pkg":"registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_service","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","http1","server","service","tokio"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#potential_utf@0.1.5","registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"potential_utf","pkg":"registry+https://github.com/rust-lang/crates.io-index#potential_utf@0.1.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"utf8_iter","pkg":"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"yoke","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#litemap@0.8.2","registry+https://github.com/rust-lang/crates.io-index#tinystr@0.8.3","registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"litemap","pkg":"registry+https://github.com/rust-lang/crates.io-index#litemap@0.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tinystr","pkg":"registry+https://github.com/rust-lang/crates.io-index#tinystr@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"writeable","pkg":"registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["zerovec"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"icu_collections","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_normalizer_data","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_properties","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_provider","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["compiled_data"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@2.2.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"icu_collections","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_locale_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_properties_data","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_provider","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerotrie","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["compiled_data"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@2.2.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_locale_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"writeable","pkg":"registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"yoke","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerotrie","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["baked"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.2","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4"],"deps":[{"name":"idna_adapter","pkg":"registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"utf8_iter","pkg":"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","compiled_data","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0"],"deps":[{"name":"icu_normalizer","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_properties","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["compiled_data"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.5"],"deps":[{"name":"equivalent","pkg":"registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"hashbrown","pkg":"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.5","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#inotify@0.11.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#inotify-sys@0.1.5","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"inotify_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#inotify-sys@0.1.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#inotify-sys@0.1.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.102","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasm_bindgen","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125","dep_kinds":[{"kind":null,"target":null}]}],"features":["std"]},{"id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki#3.0.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#ammonia@4.1.8","registry+https://github.com/rust-lang/crates.io-index#axum@0.8.9","path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark@0.13.4","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7"],"deps":[{"name":"ammonia","pkg":"registry+https://github.com/rust-lang/crates.io-index#ammonia@4.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"axum","pkg":"registry+https://github.com/rust-lang/crates.io-index#axum@0.8.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"codebase_graph","pkg":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"pulldown_cmark","pkg":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark@0.13.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"sha2","pkg":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"walkdir","pkg":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"yaml_serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#kqueue@1.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#kqueue-sys@1.1.2","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"kqueue_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#kqueue-sys@1.1.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#kqueue-sys@1.1.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#lbug@0.19.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cmake@0.1.58","registry+https://github.com/rust-lang/crates.io-index#cxx@1.0.138","registry+https://github.com/rust-lang/crates.io-index#cxx-build@1.0.138","registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.42.1","registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#time@0.3.36","registry+https://github.com/rust-lang/crates.io-index#uuid@1.18.1"],"deps":[{"name":"cmake","pkg":"registry+https://github.com/rust-lang/crates.io-index#cmake@0.1.58","dep_kinds":[{"kind":"build","target":null}]},{"name":"cxx","pkg":"registry+https://github.com/rust-lang/crates.io-index#cxx@1.0.138","dep_kinds":[{"kind":null,"target":null}]},{"name":"cxx_build","pkg":"registry+https://github.com/rust-lang/crates.io-index#cxx-build@1.0.138","dep_kinds":[{"kind":"build","target":null}]},{"name":"rust_decimal","pkg":"registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.42.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"rustversion","pkg":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","dep_kinds":[{"kind":"build","target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"time","pkg":"registry+https://github.com/rust-lang/crates.io-index#time@0.3.36","dep_kinds":[{"kind":null,"target":null}]},{"name":"uuid","pkg":"registry+https://github.com/rust-lang/crates.io-index#uuid@1.18.1","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#cty@0.2.2"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"cty","pkg":"registry+https://github.com/rust-lang/crates.io-index#cty@0.2.2","dep_kinds":[{"kind":null,"target":null}]}],"features":["cty","extended"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#libyaml-rs@0.3.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#link-cplusplus@1.0.12","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.12.1","dependencies":[],"deps":[],"features":["auxvec","elf","errno","general","ioctl","no_std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#litemap@0.8.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.14","dependencies":["registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0"],"deps":[{"name":"scopeguard","pkg":"registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["atomic_usize","default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#lzma-rs@0.3.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0","registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0"],"deps":[{"name":"byteorder","pkg":"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"crc","pkg":"registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#maplit@1.0.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#markup5ever@0.39.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","registry+https://github.com/rust-lang/crates.io-index#tendril@0.5.1","registry+https://github.com/rust-lang/crates.io-index#web_atoms@0.2.6"],"deps":[{"name":"log","pkg":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"tendril","pkg":"registry+https://github.com/rust-lang/crates.io-index#tendril@0.5.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"web_atoms","pkg":"registry+https://github.com/rust-lang/crates.io-index#web_atoms@0.2.6","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#matchit@0.8.4","dependencies":[],"deps":[],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dependencies":[],"deps":[],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#mimalloc@0.1.52","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49"],"deps":[{"name":"libmimalloc_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","extended"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.9.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1","registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.10"],"deps":[{"name":"adler2","pkg":"registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"simd_adler32","pkg":"registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.10","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","simd","simd-adler32","with-alloc"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.1+wasi-snapshot-preview1","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(any(unix, target_os = \"hermit\", target_os = \"wasi\"))"}]},{"name":"log","pkg":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasi","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.1+wasi-snapshot-preview1","dep_kinds":[{"kind":null,"target":"cfg(target_os = \"wasi\")"}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["default","log","net","os-ext","os-poll"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#notify@8.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#fsevent-sys@4.1.0","registry+https://github.com/rust-lang/crates.io-index#inotify@0.11.2","registry+https://github.com/rust-lang/crates.io-index#kqueue@1.2.0","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","registry+https://github.com/rust-lang/crates.io-index#notify-types@2.0.0","registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.60.2"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":"cfg(target_os = \"macos\")"}]},{"name":"fsevent_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#fsevent-sys@4.1.0","dep_kinds":[{"kind":null,"target":"cfg(target_os = \"macos\")"}]},{"name":"inotify","pkg":"registry+https://github.com/rust-lang/crates.io-index#inotify@0.11.2","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"linux\", target_os = \"android\"))"}]},{"name":"kqueue","pkg":"registry+https://github.com/rust-lang/crates.io-index#kqueue@1.2.0","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonflybsd\", target_os = \"ios\"))"}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]},{"name":"log","pkg":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"mio","pkg":"registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonflybsd\", target_os = \"ios\"))"},{"kind":null,"target":"cfg(any(target_os = \"linux\", target_os = \"android\"))"}]},{"name":"notify_types","pkg":"registry+https://github.com/rust-lang/crates.io-index#notify-types@2.0.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"walkdir","pkg":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.60.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["default","fsevent-sys","macos_fsevent"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#notify-types@2.0.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#num-conv@0.1.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19","dependencies":["registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1"],"deps":[{"name":"autocfg","pkg":"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1","dep_kinds":[{"kind":"build","target":null}]}],"features":["i128"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.14","registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12"],"deps":[{"name":"lock_api","pkg":"registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.14","dep_kinds":[{"kind":null,"target":null}]},{"name":"parking_lot_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.5.18","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]},{"name":"syscall","pkg":"registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.5.18","dep_kinds":[{"kind":null,"target":"cfg(target_os = \"redox\")"}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"windows_link","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","dependencies":[],"deps":[],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#phf@0.13.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228"],"deps":[{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.13.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1"],"deps":[{"name":"phf_generator","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#fastrand@2.5.0","registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1"],"deps":[{"name":"fastrand","pkg":"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#siphasher@1.0.3"],"deps":[{"name":"siphasher","pkg":"registry+https://github.com/rust-lang/crates.io-index#siphasher@1.0.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#potential_utf@0.1.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["zerovec"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#precomputed-hash@0.1.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dependencies":["registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"],"deps":[{"name":"unicode_ident","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","proc-macro","span-locations"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark@0.13.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark-escape@0.11.0","registry+https://github.com/rust-lang/crates.io-index#unicase@2.9.0"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"pulldown_cmark_escape","pkg":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark-escape@0.11.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"unicase","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicase@2.9.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["html","pulldown-cmark-escape"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark-escape@0.11.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","proc-macro"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.5.18","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","userspace"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#regex@1.12.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.14","registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11"],"deps":[{"name":"aho_corasick","pkg":"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"regex_automata","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.14","dep_kinds":[{"kind":null,"target":null}]},{"name":"regex_syntax","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","dep_kinds":[{"kind":null,"target":null}]}],"features":["perf","perf-backtrack","perf-cache","perf-dfa","perf-inline","perf-literal","perf-onepass","std","unicode","unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.14","dependencies":["registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11"],"deps":[{"name":"aho_corasick","pkg":"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"regex_syntax","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","dfa-onepass","hybrid","meta","nfa-backtrack","nfa-pikevm","nfa-thompson","perf-inline","perf-literal","perf-literal-multisubstring","perf-literal-substring","std","syntax","unicode","unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment","unicode-word-boundary"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","dependencies":[],"deps":[],"features":["std","unicode","unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.42.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6","registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19"],"deps":[{"name":"arrayvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6","dep_kinds":[{"kind":null,"target":null}]},{"name":"num_traits","pkg":"registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.12.1","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc_errno","pkg":"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","dep_kinds":[{"kind":null,"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))"},{"kind":null,"target":"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))"},{"kind":null,"target":"cfg(windows)"}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))"},{"kind":null,"target":"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))"}]},{"name":"linux_raw_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.12.1","dep_kinds":[{"kind":null,"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))"},{"kind":null,"target":"cfg(all(any(target_os = \"linux\", target_os = \"android\"), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))"}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["alloc","fs","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.23","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#same-file@1.0.6","dependencies":["registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11"],"deps":[{"name":"winapi_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#scratch@1.0.9","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dependencies":["registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228"],"deps":[{"name":"serde_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","default","derive","serde_derive","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dependencies":["registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228"],"deps":[{"name":"serde_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","dep_kinds":[{"kind":null,"target":"cfg(any())"}]}],"features":["alloc","default","result","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dependencies":["registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.21"],"deps":[{"name":"indexmap","pkg":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":"cfg(any())"}]},{"name":"serde_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"zmij","pkg":"registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.21","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","indexmap","preserve_order","raw_value","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.20","dependencies":["registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228"],"deps":[{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":"cfg(any())"}]},{"name":"serde_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17","registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"cpufeatures","pkg":"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17","dep_kinds":[{"kind":null,"target":"cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))"}]},{"name":"digest","pkg":"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.8","dependencies":["registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"errno","pkg":"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.10","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#siphasher@1.0.3","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#slab@0.4.12","dependencies":[],"deps":[],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dependencies":[],"deps":[],"features":["const_generics","const_new"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(any(unix, target_os = \"wasi\"))"}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["all"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#streaming-iterator@0.1.9","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#string_cache@0.9.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5","registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","registry+https://github.com/rust-lang/crates.io-index#precomputed-hash@0.1.1"],"deps":[{"name":"debug_unreachable","pkg":"registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","dep_kinds":[{"kind":null,"target":null}]},{"name":"parking_lot","pkg":"registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"precomputed_hash","pkg":"registry+https://github.com/rust-lang/crates.io-index#precomputed-hash@0.1.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#string_cache_codegen@0.6.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45"],"deps":[{"name":"phf_generator","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"unicode_ident","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dep_kinds":[{"kind":null,"target":null}]}],"features":["clone-impls","default","derive","extra-traits","fold","full","parsing","printing","proc-macro","visit","visit-mut"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"unicode_ident","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dep_kinds":[{"kind":null,"target":null}]}],"features":["clone-impls","default","derive","full","parsing","printing","proc-macro"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","proc-macro"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tar@0.4.46","dependencies":["registry+https://github.com/rust-lang/crates.io-index#filetime@0.2.29","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#xattr@1.6.1"],"deps":[{"name":"filetime","pkg":"registry+https://github.com/rust-lang/crates.io-index#filetime@0.2.29","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]},{"name":"xattr","pkg":"registry+https://github.com/rust-lang/crates.io-index#xattr@1.6.1","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]}],"features":["default","xattr"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tendril@0.5.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6"],"deps":[{"name":"debug_unreachable","pkg":"registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11"],"deps":[{"name":"winapi_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#time@0.3.36","dependencies":["registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11","registry+https://github.com/rust-lang/crates.io-index#num-conv@0.1.0","registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2"],"deps":[{"name":"deranged","pkg":"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11","dep_kinds":[{"kind":null,"target":null}]},{"name":"num_conv","pkg":"registry+https://github.com/rust-lang/crates.io-index#num-conv@0.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"powerfmt","pkg":"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"time_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tinystr@0.8.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["zerovec"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.8","registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.5","registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.7.2","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(all(tokio_unstable, target_os = \"linux\"))"},{"kind":null,"target":"cfg(unix)"},{"kind":null,"target":"cfg(target_os = \"wasi\")"}]},{"name":"mio","pkg":"registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","dep_kinds":[{"kind":null,"target":null},{"kind":null,"target":"cfg(all(tokio_unstable, target_os = \"linux\"))"}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"signal_hook_registry","pkg":"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.8","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]},{"name":"socket2","pkg":"registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.5","dep_kinds":[{"kind":null,"target":"cfg(any(not(target_family = \"wasm\"), all(target_os = \"wasi\", not(target_env = \"p1\"))))"}]},{"name":"tokio_macros","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.7.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["bytes","default","io-util","libc","macros","mio","net","rt","rt-multi-thread","signal","signal-hook-registry","socket2","sync","time","tokio-macros","windows-sys"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.7.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tower@0.5.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3"],"deps":[{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"sync_wrapper","pkg":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_layer","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_service","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["futures-core","futures-util","make","pin-project-lite","sync_wrapper","tokio","util"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter@0.25.10","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#regex@1.12.4","registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#streaming-iterator@0.1.9","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"regex","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex@1.12.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"regex_syntax","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":"build","target":null}]},{"name":"streaming_iterator","pkg":"registry+https://github.com/rust-lang/crates.io-index#streaming-iterator@0.1.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-c@0.24.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-cpp@0.23.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-css@0.25.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-fortran@0.6.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-go@0.25.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-html@0.23.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-javascript@0.25.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-python@0.25.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-rust@0.24.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-typescript@0.23.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#unicase@2.9.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.14","dependencies":[],"deps":[],"features":["cjk","default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#url@2.5.8","dependencies":["registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2","registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0","registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228"],"deps":[{"name":"form_urlencoded","pkg":"registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"idna","pkg":"registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"percent_encoding","pkg":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#uuid@1.18.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.102","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125"],"deps":[{"name":"js_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.102","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"), target_feature = \"atomics\"))"}]},{"name":"wasm_bindgen","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))"}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#same-file@1.0.6","registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11"],"deps":[{"name":"same_file","pkg":"registry+https://github.com/rust-lang/crates.io-index#same-file@1.0.6","dep_kinds":[{"kind":null,"target":null}]},{"name":"winapi_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.1+wasi-snapshot-preview1","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12","dependencies":["registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1"],"deps":[{"name":"wit_bindgen","pkg":"registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4","registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.125","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"once_cell","pkg":"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"rustversion_compat","pkg":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","dep_kinds":[{"kind":"build","target":null}]},{"name":"wasm_bindgen_macro","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.125","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasm_bindgen_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125","dep_kinds":[{"kind":null,"target":null}]}],"features":["msrv","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.125","dependencies":["registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.125"],"deps":[{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasm_bindgen_macro_support","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.125","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.125","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.20.3","registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125"],"deps":[{"name":"bumpalo","pkg":"registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.20.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasm_bindgen_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125","dependencies":["registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"],"deps":[{"name":"unicode_ident","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#web_atoms@0.2.6","dependencies":["registry+https://github.com/rust-lang/crates.io-index#phf@0.13.1","registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.13.1","registry+https://github.com/rust-lang/crates.io-index#string_cache@0.9.0","registry+https://github.com/rust-lang/crates.io-index#string_cache_codegen@0.6.1"],"deps":[{"name":"phf","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_codegen","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.13.1","dep_kinds":[{"kind":"build","target":null}]},{"name":"string_cache","pkg":"registry+https://github.com/rust-lang/crates.io-index#string_cache@0.9.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"string_cache_codegen","pkg":"registry+https://github.com/rust-lang/crates.io-index#string_cache_codegen@0.6.1","dep_kinds":[{"kind":"build","target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9","dependencies":["registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0","registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0"],"deps":[{"name":"winapi_i686_pc_windows_gnu","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0","dep_kinds":[{"kind":null,"target":"i686-pc-windows-gnu"}]},{"name":"winapi_x86_64_pc_windows_gnu","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0","dep_kinds":[{"kind":null,"target":"x86_64-pc-windows-gnu"}]}],"features":["fileapi","handleapi","processthreadsapi","std","winbase","winerror"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","dependencies":["registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.60.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.53.5"],"deps":[{"name":"windows_targets","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.53.5","dep_kinds":[{"kind":null,"target":null}]}],"features":["Win32","Win32_Foundation","Win32_Security","Win32_Storage","Win32_Storage_FileSystem","Win32_System","Win32_System_IO","Win32_System_Threading","Win32_System_WindowsProgramming","default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1"],"deps":[{"name":"windows_link","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","dep_kinds":[{"kind":null,"target":null}]}],"features":["Wdk","Wdk_Foundation","Wdk_Storage","Wdk_Storage_FileSystem","Wdk_System","Wdk_System_IO","Win32","Win32_Foundation","Win32_Networking","Win32_Networking_WinSock","Win32_Security","Win32_Security_Authorization","Win32_Storage","Win32_Storage_FileSystem","Win32_System","Win32_System_Console","Win32_System_Diagnostics","Win32_System_Diagnostics_Debug","Win32_System_Diagnostics_ToolHelp","Win32_System_IO","Win32_System_JobObjects","Win32_System_Memory","Win32_System_Pipes","Win32_System_ProcessStatus","Win32_System_SystemInformation","Win32_System_SystemServices","Win32_System_Threading","Win32_System_WindowsProgramming","default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.53.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnullvm@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.53.1"],"deps":[{"name":"windows_link","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","dep_kinds":[{"kind":null,"target":"cfg(windows_raw_dylib)"}]},{"name":"windows_aarch64_gnullvm","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.53.1","dep_kinds":[{"kind":null,"target":"aarch64-pc-windows-gnullvm"}]},{"name":"windows_aarch64_msvc","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))"}]},{"name":"windows_i686_gnu","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"x86\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))"}]},{"name":"windows_i686_gnullvm","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnullvm@0.53.1","dep_kinds":[{"kind":null,"target":"i686-pc-windows-gnullvm"}]},{"name":"windows_i686_msvc","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"x86\", target_env = \"msvc\", not(windows_raw_dylib)))"}]},{"name":"windows_x86_64_gnu","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"x86_64\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))"}]},{"name":"windows_x86_64_gnullvm","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.53.1","dep_kinds":[{"kind":null,"target":"x86_64-pc-windows-gnullvm"}]},{"name":"windows_x86_64_msvc","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(any(target_arch = \"x86_64\", target_arch = \"arm64ec\"), target_env = \"msvc\", not(windows_raw_dylib)))"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnullvm@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#xattr@1.6.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.4"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"freebsd\", target_os = \"netbsd\"))"}]},{"name":"rustix","pkg":"registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.4","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"android\", target_os = \"linux\", target_os = \"macos\", target_os = \"hurd\"))"}]}],"features":["default","unsupported"]},{"id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask#3.0.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.10","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","registry+https://github.com/rust-lang/crates.io-index#tar@0.4.46","registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7"],"deps":[{"name":"flate2","pkg":"registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.10","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"sha2","pkg":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"tar","pkg":"registry+https://github.com/rust-lang/crates.io-index#tar@0.4.46","dep_kinds":[{"kind":null,"target":null}]},{"name":"yaml_serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#libyaml-rs@0.3.0","registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.23","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228"],"deps":[{"name":"indexmap","pkg":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"libyaml_rs","pkg":"registry+https://github.com/rust-lang/crates.io-index#libyaml-rs@0.3.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"ryu","pkg":"registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.23","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1","registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.8.2","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8"],"deps":[{"name":"stable_deref_trait","pkg":"registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"yoke_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":["derive","zerofrom"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.8.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]},{"name":"synstructure","pkg":"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dependencies":["registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.7"],"deps":[{"name":"zerofrom_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.7","dep_kinds":[{"kind":null,"target":null}]}],"features":["derive"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]},{"name":"synstructure","pkg":"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"yoke","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":["yoke","zerofrom"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dependencies":["registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.11.3"],"deps":[{"name":"yoke","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.11.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["derive","yoke"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.11.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zlib-rs@0.6.8","dependencies":[],"deps":[],"features":["rust-allocator","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.21","dependencies":[],"deps":[],"features":[]}],"root":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4"},"target_directory":"/Users/rabii/Projects/Repositories/codebaseGraph/target","build_directory":"/Users/rabii/Projects/Repositories/codebaseGraph/target","version":1,"workspace_root":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo","metadata":null} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stderr b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stderr new file mode 100644 index 00000000..e69de29b diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stdout b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stdout new file mode 100644 index 00000000..cbb4a463 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stdout @@ -0,0 +1 @@ +{"packages":[{"name":"adler2","version":"2.0.1","id":"registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1","license":"0BSD OR MIT OR Apache-2.0","license_file":null,"description":"A simple clean-room implementation of the Adler-32 checksum","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"adler2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/adler2-2.0.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/adler2-2.0.1/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"core":["dep:core"],"default":["std"],"rustc-dep-of-std":["core"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/adler2-2.0.1/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--cfg=docsrs"]}},"release":{"no-dev-version":true,"pre-release-commit-message":"Release {{version}}","tag-message":"{{version}}","pre-release-replacements":[{"file":"CHANGELOG.md","replace":"## Unreleased\n\nNo changes.\n\n## [{{version}} - {{date}}](https://github.com/jonas-schievink/adler/releases/tag/v{{version}})\n","search":"## Unreleased\n"},{"file":"README.md","replace":"adler = \"{{version}}\"","search":"adler = \"[a-z0-9\\\\.-]+\""},{"file":"src/lib.rs","replace":"https://docs.rs/adler/{{version}}","search":"https://docs.rs/adler/[a-z0-9\\.-]+"}]}},"publish":null,"authors":["Jonas Schievink ","oyvindln "],"categories":["algorithms"],"keywords":["checksum","integrity","hash","adler32","zlib"],"readme":"README.md","repository":"https://github.com/oyvindln/adler2","homepage":null,"documentation":"https://docs.rs/adler2/","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"aho-corasick","version":"1.1.4","id":"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","license":"Unlicense OR MIT","license_file":null,"description":"Fast multiple substring searching.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.17","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"aho_corasick","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/aho-corasick-1.1.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"default":["std","perf-literal"],"logging":["dep:log"],"perf-literal":["dep:memchr"],"std":["memchr?/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/aho-corasick-1.1.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--generate-link-to-definition"]}}},"publish":null,"authors":["Andrew Gallant "],"categories":["text-processing"],"keywords":["string","search","text","pattern","multi"],"readme":"README.md","repository":"https://github.com/BurntSushi/aho-corasick","homepage":"https://github.com/BurntSushi/aho-corasick","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60.0"},{"name":"ammonia","version":"4.1.8","id":"registry+https://github.com/rust-lang/crates.io-index#ammonia@4.1.8","license":"MIT OR Apache-2.0","license_file":null,"description":"HTML Sanitization","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cssparser","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.37.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"html5ever","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.39","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"maplit","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"url","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"version-sync","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"ammonia","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ammonia-4.1.8/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"ammonia-cat","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ammonia-4.1.8/examples/ammonia-cat.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"version-numbers","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ammonia-4.1.8/tests/version-numbers.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ammonia-4.1.8/Cargo.toml","metadata":null,"publish":null,"authors":["Michael Howell "],"categories":["web-programming","text-processing"],"keywords":["sanitization","html","security","xss"],"readme":"README.md","repository":"https://github.com/rust-ammonia/ammonia","homepage":null,"documentation":"https://docs.rs/ammonia/","edition":"2021","links":null,"default_run":null,"rust_version":"1.80"},{"name":"arrayvec","version":"0.7.6","id":"registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6","license":"MIT OR Apache-2.0","license_file":null,"description":"A vector with fixed capacity, backed by an array (it can be stored on the stack too). Implements fixed capacity ArrayVec and ArrayString.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"borsh","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zeroize","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bencher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"matches","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"arrayvec","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"borsh","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/tests/borsh.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/tests/serde.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/tests/tests.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"arraystring","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/benches/arraystring.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"extend","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/benches/extend.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"borsh":["dep:borsh"],"default":["std"],"serde":["dep:serde"],"std":[],"zeroize":["dep:zeroize"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayvec-0.7.6/Cargo.toml","metadata":{"docs":{"rs":{"features":["borsh","serde","zeroize"]}},"release":{"no-dev-version":true,"tag-name":"{{version}}"}},"publish":null,"authors":["bluss"],"categories":["data-structures","no-std"],"keywords":["stack","vector","array","data-structure","no_std"],"readme":"README.md","repository":"https://github.com/bluss/arrayvec","homepage":null,"documentation":"https://docs.rs/arrayvec/","edition":"2018","links":null,"default_run":null,"rust_version":"1.51"},{"name":"atomic-waker","version":"1.1.2","id":"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2","license":"Apache-2.0 OR MIT","license_file":null,"description":"A synchronization primitive for task wakeup","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"portable-atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["cargo_bench_support"],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.7.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"atomic_waker","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic-waker-1.1.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"basic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic-waker-1.1.2/tests/basic.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"waker","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic-waker-1.1.2/benches/waker.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"portable-atomic":["dep:portable-atomic"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic-waker-1.1.2/Cargo.toml","metadata":null,"publish":null,"authors":["Stjepan Glavina ","Contributors to futures-rs"],"categories":["asynchronous","concurrency"],"keywords":["waker","notify","wake","futures","async"],"readme":"README.md","repository":"https://github.com/smol-rs/atomic-waker","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.36"},{"name":"autocfg","version":"1.5.1","id":"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1","license":"Apache-2.0 OR MIT","license_file":null,"description":"Automatic cfg for Rust compiler features","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"autocfg","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"integers","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/integers.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"nightly","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/nightly.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"paths","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/paths.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"traits","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/traits.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"versions","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/examples/versions.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/tests/no_std.rs","edition":"2015","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rustflags","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/tests/rustflags.rs","edition":"2015","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/tests/tests.rs","edition":"2015","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"wrappers","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/tests/wrappers.rs","edition":"2015","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/autocfg-1.5.1/Cargo.toml","metadata":null,"publish":null,"authors":["Josh Stone "],"categories":["development-tools::build-utils"],"keywords":["rustc","build","autoconf"],"readme":"README.md","repository":"https://github.com/cuviper/autocfg","homepage":null,"documentation":"https://docs.rs/autocfg/","edition":"2015","links":null,"default_run":null,"rust_version":"1.0"},{"name":"axum","version":"0.8.9","id":"registry+https://github.com/rust-lang/crates.io-index#axum@0.8.9","license":"MIT","license_file":null,"description":"HTTP routing and request handling library that focuses on ergonomics and modularity","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"axum-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"axum-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"base64","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.22.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"form_urlencoded","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["tokio","server","service"],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"matchit","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.8.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mime","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.16","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"multer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"percent-encoding","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"reqwest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["json","stream","multipart"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.211","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.221","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["raw_value"],"target":null,"registry":null},{"name":"serde_path_to_error","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.8","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_urlencoded","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sha1","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sync_wrapper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.44","kind":null,"rename":"tokio","optional":true,"uses_default_features":true,"features":["time"],"target":null,"registry":null},{"name":"tokio-tungstenite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.29.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["util"],"target":null,"registry":null},{"name":"tower-http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.8","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["add-extension","auth","catch-panic","compression-br","compression-deflate","compression-gzip","cors","decompression-br","decompression-deflate","decompression-gzip","follow-redirect","fs","limit","map-request-body","map-response-body","metrics","normalize-path","propagate-header","redirect","request-id","sensitive-headers","set-header","set-status","timeout","trace","util","validate-request"],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-service","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["client"],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"reqwest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["json","stream","multipart"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.221","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["raw_value"],"target":null,"registry":null},{"name":"time","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["serde-human-readable"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.44.2","kind":"dev","rename":"tokio","optional":false,"uses_default_features":true,"features":["macros","rt","rt-multi-thread","net","test-util"],"target":null,"registry":null},{"name":"tokio-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-tungstenite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.29.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.2","kind":"dev","rename":"tower","optional":false,"uses_default_features":true,"features":["util","timeout","limit","load-shed","steer","filter"],"target":null,"registry":null},{"name":"tower-http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["add-extension","auth","catch-panic","compression-br","compression-deflate","compression-gzip","cors","decompression-br","decompression-deflate","decompression-gzip","follow-redirect","fs","limit","map-request-body","map-response-body","metrics","normalize-path","propagate-header","redirect","request-id","sensitive-headers","set-header","set-status","timeout","trace","util","validate-request"],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing-subscriber","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["json"],"target":null,"registry":null},{"name":"uuid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["serde","v4"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"axum","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"panic_location","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/tests/panic_location.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benches","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/benches/benches.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"__private":["tokio","http1","dep:reqwest"],"__private_docs":["axum-core/__private_docs","tower/full","dep:serde","dep:tower-http"],"default":["form","http1","json","matched-path","original-uri","query","tokio","tower-log","tracing"],"form":["dep:form_urlencoded","dep:serde_urlencoded","dep:serde_path_to_error"],"http1":["dep:hyper","hyper?/http1","hyper-util?/http1"],"http2":["dep:hyper","hyper?/http2","hyper-util?/http2"],"json":["dep:serde_json","dep:serde_path_to_error"],"macros":["dep:axum-macros"],"matched-path":[],"multipart":["dep:multer"],"original-uri":[],"query":["dep:form_urlencoded","dep:serde_urlencoded","dep:serde_path_to_error"],"tokio":["dep:hyper-util","dep:tokio","tokio/net","tokio/rt","tower/make","tokio/macros"],"tower-log":["tower/log"],"tracing":["dep:tracing","axum-core/tracing"],"ws":["dep:hyper","tokio","dep:tokio-tungstenite","dep:sha1","dep:base64"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"playground":{"features":["http1","http2","json","multipart","ws"]},"cargo-public-api-crates":{"allowed":["axum_core","axum_macros","futures_core","futures_sink","futures_util","pin_project_lite","tower_layer","tower_service","bytes","http","http_body","serde_core","tokio","reqwest"]}},"publish":null,"authors":[],"categories":["asynchronous","network-programming","web-programming::http-server"],"keywords":["http","web","routing"],"readme":"README.md","repository":"https://github.com/tokio-rs/axum","homepage":"https://github.com/tokio-rs/axum","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.80"},{"name":"axum-core","version":"0.5.6","id":"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.6","license":"MIT","license_file":null,"description":"Core types and traits for axum","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mime","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.16","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sync_wrapper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["limit"],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-service","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.37","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.25.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros"],"target":null,"registry":null},{"name":"tower-http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["limit"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"axum_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-core-0.5.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"__private_docs":["dep:tower-http"],"tracing":["dep:tracing"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-core-0.5.6/Cargo.toml","metadata":{"cargo_check_external_types":{"allowed_external_types":["futures_core::*","tower_layer::*","bytes::*","http::*","http_body::*"]},"cargo-machete":{"ignored":["tower-http"]},"docs":{"rs":{"all-features":true}}},"publish":null,"authors":[],"categories":["asynchronous","network-programming","web-programming"],"keywords":["http","web","framework"],"readme":"README.md","repository":"https://github.com/tokio-rs/axum","homepage":"https://github.com/tokio-rs/axum","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.78"},{"name":"bitflags","version":"2.13.0","id":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A macro to generate structures which behave like bitflags.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bytemuck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.228","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"bytemuck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.103","kind":"dev","rename":"serde_lib","optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.19","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.18","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"zerocopy","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"bitflags","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"custom_bits_type","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/custom_bits_type.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"custom_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/custom_derive.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"fmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/fmt.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"macro_free","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/macro_free.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/examples/serde.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"parse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/benches/parse.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"arbitrary":["dep:arbitrary"],"bytemuck":["dep:bytemuck"],"example_generated":[],"serde":["serde_core"],"serde_core":["dep:serde_core"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitflags-2.13.0/Cargo.toml","metadata":{"docs":{"rs":{"features":["example_generated"]}}},"publish":null,"authors":["The Rust Project Developers"],"categories":["no-std"],"keywords":["bit","bitmask","bitflags","flags"],"readme":"README.md","repository":"https://github.com/bitflags/bitflags","homepage":"https://github.com/bitflags/bitflags","documentation":"https://docs.rs/bitflags","edition":"2021","links":null,"default_run":null,"rust_version":"1.56.0"},{"name":"block-buffer","version":"0.10.4","id":"registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4","license":"MIT OR Apache-2.0","license_file":null,"description":"Buffer type for block processing of data","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"generic-array","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.14","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"block_buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/block-buffer-0.10.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/block-buffer-0.10.4/tests/mod.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/block-buffer-0.10.4/Cargo.toml","metadata":null,"publish":null,"authors":["RustCrypto Developers"],"categories":["cryptography","no-std"],"keywords":["block","buffer"],"readme":"README.md","repository":"https://github.com/RustCrypto/utils","homepage":null,"documentation":"https://docs.rs/block-buffer","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"bumpalo","version":"3.20.3","id":"registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.20.3","license":"MIT OR Apache-2.0","license_file":null,"description":"A fast bump allocation arena for Rust.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"allocator-api2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.171","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"blink-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.10.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rayon-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.12.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.197","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.115","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"bumpalo","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bumpalo-3.20.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"allocator-api2":["dep:allocator-api2"],"allocator_api":[],"bench_allocator_api":["allocator_api","blink-alloc/nightly"],"boxed":[],"collections":[],"default":[],"serde":["dep:serde"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bumpalo-3.20.3/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["Nick Fitzgerald "],"categories":["memory-management","rust-patterns","no-std"],"keywords":["arena","bump","allocator"],"readme":"README.md","repository":"https://github.com/fitzgen/bumpalo","homepage":null,"documentation":"https://docs.rs/bumpalo","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.1"},{"name":"byteorder","version":"1.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0","license":"Unlicense OR MIT","license_file":null,"description":"Library for reading/writing numbers in big-endian and little-endian.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"byteorder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/byteorder-1.5.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/byteorder-1.5.0/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"i128":[],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/byteorder-1.5.0/Cargo.toml","metadata":null,"publish":null,"authors":["Andrew Gallant "],"categories":["encoding","parsing","no-std"],"keywords":["byte","endian","big-endian","little-endian","binary"],"readme":"README.md","repository":"https://github.com/BurntSushi/byteorder","homepage":"https://github.com/BurntSushi/byteorder","documentation":"https://docs.rs/byteorder","edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"bytes","version":"1.12.1","id":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","license":"MIT","license_file":null,"description":"Types and traits for working with bytes","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"portable-atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3","kind":null,"rename":"extra-platforms","optional":true,"uses_default_features":false,"features":["require-cas"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.60","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"loom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(loom)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"bytes","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_buf_mut","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_buf_mut.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_bytes","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_bytes.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_bytes_odd_alloc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_bytes_odd_alloc.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_bytes_vec_alloc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_bytes_vec_alloc.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_chain","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_chain.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_debug","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_debug.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_iter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_iter.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_limit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_limit.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_reader","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_reader.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_serde.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_take","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/tests/test_take.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"extra-platforms":["dep:extra-platforms"],"serde":["dep:serde"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Carl Lerche ","Sean McArthur "],"categories":["network-programming","data-structures"],"keywords":["buffers","zero-copy","io"],"readme":"README.md","repository":"https://github.com/tokio-rs/bytes","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.57"},{"name":"cc","version":"1.2.64","id":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","license":"MIT OR Apache-2.0","license_file":null,"description":"A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"find-msvc-tools","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"jobserver","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.30","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"shlex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.62","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(unix)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cc-1.2.64/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"jobserver":[],"parallel":["dep:libc","dep:jobserver"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cc-1.2.64/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":["development-tools::build-utils"],"keywords":["build-dependencies"],"readme":"README.md","repository":"https://github.com/rust-lang/cc-rs","homepage":"https://github.com/rust-lang/cc-rs","documentation":"https://docs.rs/cc","edition":"2018","links":null,"default_run":null,"rust_version":"1.63"},{"name":"cfg-if","version":"1.0.4","id":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","license":"MIT OR Apache-2.0","license_file":null,"description":"A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cfg_if","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cfg-if-1.0.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"xcrate","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cfg-if-1.0.4/tests/xcrate.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"core":["dep:core"],"rustc-dep-of-std":["core"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cfg-if-1.0.4/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/cfg-if","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.32"},{"name":"cmake","version":"0.1.58","id":"registry+https://github.com/rust-lang/crates.io-index#cmake@0.1.58","license":"MIT OR Apache-2.0","license_file":null,"description":"A build dependency for running `cmake` to build a native library\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.46","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cmake","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cmake-0.1.58/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cmake-0.1.58/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":["development-tools::build-utils"],"keywords":["build-dependencies"],"readme":"README.md","repository":"https://github.com/rust-lang/cmake-rs","homepage":"https://github.com/rust-lang/cmake-rs","documentation":"https://docs.rs/cmake","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"codebase-graph","version":"3.0.4","id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","license":"MIT","license_file":null,"description":"Native codebaseGraph CLI and MCP server for local code knowledge graphs.","source":null,"dependencies":[{"name":"fs2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lbug","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.19.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libmimalloc-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.49","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["extended"],"target":null,"registry":null},{"name":"lzma-rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mimalloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.52","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["extended"],"target":null,"registry":null},{"name":"notify","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^8.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sha2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-c","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-cpp","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.23.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-css","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-fortran","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-go","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-html","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.23.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-javascript","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-python","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-rust","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-typescript","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.23.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_System_ProcessStatus","Win32_System_Threading","Win32_System_JobObjects","Win32_System_SystemServices","Win32_Security","Win32_Security_Authorization","Win32_System_Memory","Win32_Storage_FileSystem","Win32_System_Pipes","Win32_System_Console","Win32_System_Diagnostics_ToolHelp"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"codebase_graph","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"codebase-graph","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/src/bin/codebase-graph.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"agent_hooks_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/agent_hooks_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"auto_repo_root","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/auto_repo_root.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"coordinator_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/coordinator_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"generation_storage","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/generation_storage.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"http_admission_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/http_admission_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"http_daemon_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/http_daemon_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"native_request_identity","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/native_request_identity.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"refresh_recovery_process","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/refresh_recovery_process.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"storage_foundation","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/src/storage/test_harness.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"storage_maintenance","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/tests/storage_maintenance.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"bundled-windows-extensions":[]},"manifest_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["command-line-utilities","development-tools"],"keywords":["codebase","graph","mcp","cli","analysis"],"readme":"README.md","repository":"https://github.com/rabii-chaarani/codebaseGraph","homepage":"https://github.com/rabii-chaarani/codebaseGraph","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"codespan-reporting","version":"0.11.1","id":"registry+https://github.com/rust-lang/crates.io-index#codespan-reporting@0.11.1","license":"Apache-2.0","license_file":null,"description":"Beautiful diagnostic reporting for text-based programming languages","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"termcolor","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"unicode-width","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lazy_static","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"peg","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustyline","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"structopt","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"unindent","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"codespan_reporting","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"custom_files","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/custom_files.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"peg_calculator","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/peg_calculator.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"readme_preview","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/readme_preview.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"reusable_diagnostic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/reusable_diagnostic.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"term","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/examples/term.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"term","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/tests/term.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"ascii-only":[],"serde":["dep:serde"],"serialization":["serde","serde/rc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/codespan-reporting-0.11.1/Cargo.toml","metadata":null,"publish":null,"authors":["Brendan Zabarauskas "],"categories":[],"keywords":[],"readme":"../README.md","repository":"https://github.com/brendanzab/codespan","homepage":"https://github.com/brendanzab/codespan","documentation":"https://docs.rs/codespan-reporting","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"cpufeatures","version":"0.2.17","id":"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17","license":"MIT OR Apache-2.0","license_file":null,"description":"Lightweight runtime CPU feature detection for aarch64, loongarch64, and x86/x86_64 targets, \nwith no_std support and support for mobile targets including Android and iOS\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.155","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"aarch64-linux-android","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.155","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.155","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.155","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"loongarch64\", target_os = \"linux\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cpufeatures","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"aarch64","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/tests/aarch64.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"loongarch64","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/tests/loongarch64.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"x86","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/tests/x86.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cpufeatures-0.2.17/Cargo.toml","metadata":null,"publish":null,"authors":["RustCrypto Developers"],"categories":["hardware-support","no-std"],"keywords":["cpuid","target-feature"],"readme":"README.md","repository":"https://github.com/RustCrypto/utils","homepage":null,"documentation":"https://docs.rs/cpufeatures","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"crc","version":"3.4.0","id":"registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Rust implementation of CRC with support of various standards","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"crc-catalog","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"crc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"crc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/tests/crc.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-3.4.0/Cargo.toml","metadata":null,"publish":null,"authors":["Rui Hu ","Akhil Velagapudi <4@4khil.com>"],"categories":["algorithms","no-std"],"keywords":["crc","crc16","crc32","crc64","hash"],"readme":"README.md","repository":"https://github.com/mrhooray/crc-rs.git","homepage":null,"documentation":"https://docs.rs/crc","edition":"2021","links":null,"default_run":null,"rust_version":"1.83"},{"name":"crc-catalog","version":"2.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.5.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Catalog of CRC algorithms (generated from http://reveng.sourceforge.net/crc-catalogue) expressed as simple Rust structs.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"crc_catalog","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-catalog-2.5.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc-catalog-2.5.0/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["Akhil Velagapudi "],"categories":["no-std","network-programming"],"keywords":["crc"],"readme":"README.md","repository":"https://github.com/akhilles/crc-catalog.git","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"crc32fast","version":"1.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Fast, SIMD-accelerated CRC32 (IEEE) checksum computation","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bencher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"crc32fast","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.0/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"nightly":[],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.0/Cargo.toml","metadata":null,"publish":null,"authors":["Sam Rijs ","Alex Crichton "],"categories":["algorithms","no-std"],"keywords":["hash","crc","crc32","simd","fast"],"readme":"README.md","repository":"https://github.com/srijs/rust-crc32fast","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"crypto-common","version":"0.1.7","id":"registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.7","license":"MIT OR Apache-2.0","license_file":null,"description":"Common cryptographic traits","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"generic-array","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.14.7","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["more_lengths"],"target":null,"registry":null},{"name":"rand_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"typenum","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.14","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"crypto_common","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crypto-common-0.1.7/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"getrandom":["rand_core/getrandom"],"rand_core":["dep:rand_core"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/crypto-common-0.1.7/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["RustCrypto Developers"],"categories":["cryptography","no-std"],"keywords":["crypto","traits"],"readme":"README.md","repository":"https://github.com/RustCrypto/traits","homepage":null,"documentation":"https://docs.rs/crypto-common","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"cssparser","version":"0.37.0","id":"registry+https://github.com/rust-lang/crates.io-index#cssparser@0.37.0","license":"MPL-2.0","license_file":null,"description":"Rust implementation of CSS Syntax Level 3","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cssparser-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"dtoa-short","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"malloc_size_of","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"phf","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["macros"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"difference","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"encoding_rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.25","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cssparser","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cssparser-0.37.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"bench":[],"default":["fast_match_byte","fast_match_color"],"fast_match_byte":["dep:cssparser-macros"],"fast_match_color":["dep:phf"],"malloc_size_of":["dep:malloc_size_of"],"serde":["dep:serde"],"skip_long_tests":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cssparser-0.37.0/Cargo.toml","metadata":null,"publish":null,"authors":["Simon Sapin "],"categories":[],"keywords":["css","syntax","parser"],"readme":"README.md","repository":"https://github.com/servo/rust-cssparser","homepage":null,"documentation":"https://docs.rs/cssparser/","edition":"2018","links":null,"default_run":null,"rust_version":"1.71"},{"name":"cty","version":"0.2.2","id":"registry+https://github.com/rust-lang/crates.io-index#cty@0.2.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Type aliases to C types like c_int for use with bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cty","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cty-0.2.2/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cty-0.2.2/Cargo.toml","metadata":null,"publish":null,"authors":["Jorge Aparicio "],"categories":["embedded","external-ffi-bindings","no-std"],"keywords":["c","types","bindgen","ffi"],"readme":"README.md","repository":"https://github.com/japaric/cty","homepage":null,"documentation":"https://docs.rs/cty","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"cxx","version":"1.0.138","id":"registry+https://github.com/rust-lang/crates.io-index#cxx@1.0.138","license":"MIT OR Apache-2.0","license_file":null,"description":"Safe interop between Rust and C++","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cxxbridge-macro","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.138","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"foldhash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"link-cplusplus","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-build","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.138","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-gen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-test-suite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.81","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["diff"],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.83","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxxbridge-flags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.138","kind":"build","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"cxxbridge-cmd","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.138","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any())","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cxx","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/compiletest.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cxx_gen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/cxx_gen.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cxx_string","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/cxx_string.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cxx_vector","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/cxx_vector.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unique_ptr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/tests/unique_ptr.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"c++14":["cxxbridge-flags/c++14"],"c++17":["cxxbridge-flags/c++17"],"c++20":["cxxbridge-flags/c++20"],"default":["std","cxxbridge-flags/default"],"std":["alloc","foldhash/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-1.0.138/Cargo.toml","metadata":{"bazel":{"additive_build_file_content":"cc_library(\n name = \"cxx_cc\",\n srcs = [\"src/cxx.cc\"],\n hdrs = [\"include/cxx.h\"],\n include_prefix = \"rust\",\n includes = [\"include\"],\n linkstatic = True,\n strip_include_prefix = \"include\",\n visibility = [\"//visibility:public\"],\n)\n","deps":[":cxx_cc"],"gen_build_script":false,"extra_aliased_targets":{"cxx_cc":"cxx_cc"}},"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::ffi","api-bindings","no-std"],"keywords":["ffi","c++"],"readme":"README.md","repository":"https://github.com/dtolnay/cxx","homepage":"https://cxx.rs","documentation":"https://docs.rs/cxx","edition":"2021","links":"cxxbridge1","default_run":null,"rust_version":"1.73"},{"name":"cxx-build","version":"1.0.138","id":"registry+https://github.com/rust-lang/crates.io-index#cxx-build@1.0.138","license":"MIT OR Apache-2.0","license_file":null,"description":"C++ code generator for integrating `cxx` crate into a Cargo build.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.83","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"codespan-reporting","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.74","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["span-locations"],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"scratch","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.46","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["clone-impls","full","parsing","printing"],"target":null,"registry":null},{"name":"cxx","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-gen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pkg-config","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.27","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cxx_build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-build-1.0.138/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"experimental-async-fn":[],"parallel":["cc/parallel"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxx-build-1.0.138/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::build-utils","development-tools::ffi"],"keywords":["ffi","build-dependencies"],"readme":null,"repository":"https://github.com/dtolnay/cxx","homepage":"https://cxx.rs","documentation":"https://docs.rs/cxx-build","edition":"2021","links":null,"default_run":null,"rust_version":"1.73"},{"name":"cxxbridge-flags","version":"1.0.138","id":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-flags@1.0.138","license":"MIT OR Apache-2.0","license_file":null,"description":"Compiler configuration of the `cxx` crate (implementation detail)","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"cxxbridge_flags","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxxbridge-flags-1.0.138/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"c++14":[],"c++17":[],"c++20":[],"default":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxxbridge-flags-1.0.138/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::ffi","compilers"],"keywords":[],"readme":null,"repository":"https://github.com/dtolnay/cxx","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.73"},{"name":"cxxbridge-macro","version":"1.0.138","id":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-macro@1.0.138","license":"MIT OR Apache-2.0","license_file":null,"description":"Implementation detail of the `cxx` crate.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"clang-ast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.18","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.26","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memmap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.74","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.166","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.166","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.100","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.46","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"cxx","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"cxxbridge_macro","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxxbridge-macro-1.0.138/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"clang-ast":["dep:clang-ast"],"experimental-async-fn":[],"experimental-enum-variants-from-header":["clang-ast","flate2","memmap","serde","serde_derive","serde_json"],"flate2":["dep:flate2"],"memmap":["dep:memmap"],"serde":["dep:serde"],"serde_derive":["dep:serde_derive"],"serde_json":["dep:serde_json"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cxxbridge-macro-1.0.138/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::ffi"],"keywords":["ffi"],"readme":"README.md","repository":"https://github.com/dtolnay/cxx","homepage":"https://cxx.rs","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.73"},{"name":"deranged","version":"0.3.11","id":"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11","license":"MIT OR Apache-2.0","license_file":null,"description":"Ranged integers","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"num-traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.15","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"powerfmt","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.126","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.86","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"deranged","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/deranged-0.3.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["std"],"num":["dep:num-traits"],"powerfmt":["dep:powerfmt"],"quickcheck":["dep:quickcheck","alloc"],"rand":["dep:rand"],"serde":["dep:serde"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/deranged-0.3.11/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docs_rs"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["Jacob Pratt "],"categories":[],"keywords":["integer","int","range"],"readme":"README.md","repository":"https://github.com/jhpratt/deranged","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"digest","version":"0.10.7","id":"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7","license":"MIT OR Apache-2.0","license_file":null,"description":"Traits for cryptographic hash functions and message authentication codes","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"blobby","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"block-buffer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"const-oid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"crypto-common","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"subtle","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"digest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/digest-0.10.7/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"blobby":["dep:blobby"],"block-buffer":["dep:block-buffer"],"const-oid":["dep:const-oid"],"core-api":["block-buffer"],"default":["core-api"],"dev":["blobby"],"mac":["subtle"],"oid":["const-oid"],"rand_core":["crypto-common/rand_core"],"std":["alloc","crypto-common/std"],"subtle":["dep:subtle"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/digest-0.10.7/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["RustCrypto Developers"],"categories":["cryptography","no-std"],"keywords":["digest","crypto","hash"],"readme":"README.md","repository":"https://github.com/RustCrypto/traits","homepage":null,"documentation":"https://docs.rs/digest","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"displaydoc","version":"0.2.7","id":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","license":"MIT OR Apache-2.0","license_file":null,"description":"A derive macro for implementing the display Trait via a doc comment and string interpolation\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"pretty_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"static_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"thiserror","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.24","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"displaydoc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compile_tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/compile_tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"happy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/happy.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/no_std.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"num_in_field","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/num_in_field.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"variantless","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/tests/variantless.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/displaydoc-0.2.7/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}},"release":{"no-dev-version":true,"pre-release-hook":["./update-readme.sh"],"pre-release-replacements":[{"file":"CHANGELOG.md","search":"Unreleased","replace":"{{version}}"},{"file":"src/lib.rs","search":"#!\\[doc\\(html_root_url.*","replace":"#![doc(html_root_url = \"https://docs.rs/{{crate_name}}/{{version}}\")]","exactly":1},{"file":"CHANGELOG.md","search":"ReleaseDate","replace":"{{date}}"},{"file":"CHANGELOG.md","search":"","replace":"\n\n# [Unreleased] - ReleaseDate","exactly":1},{"file":"CHANGELOG.md","search":"\\.\\.\\.HEAD","replace":"...{{tag_name}}","exactly":1},{"file":"CHANGELOG.md","search":"","replace":"\n[Unreleased]: https://github.com/yaahc/{{crate_name}}/compare/{{tag_name}}...HEAD","exactly":1}]}},"publish":null,"authors":["Jane Lusby "],"categories":[],"keywords":["display","derive"],"readme":"README.md","repository":"https://github.com/yaahc/displaydoc","homepage":"https://github.com/yaahc/displaydoc","documentation":"https://docs.rs/displaydoc","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"dtoa","version":"1.0.11","id":"registry+https://github.com/rust-lang/crates.io-index#dtoa@1.0.11","license":"MIT OR Apache-2.0","license_file":null,"description":"Fast floating point primitive to string conversion","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"no-panic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"dtoa","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"no-panic":["dep:no-panic"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["value-formatting","no-std","no-std::no-alloc"],"keywords":["float"],"readme":"README.md","repository":"https://github.com/dtolnay/dtoa","homepage":null,"documentation":"https://docs.rs/dtoa","edition":"2021","links":null,"default_run":null,"rust_version":"1.68"},{"name":"dtoa-short","version":"0.3.5","id":"registry+https://github.com/rust-lang/crates.io-index#dtoa-short@0.3.5","license":"MPL-2.0","license_file":null,"description":"Serialize float number and truncate to certain precision","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"dtoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"float-cmp","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"dtoa_short","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-short-0.3.5/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-short-0.3.5/Cargo.toml","metadata":null,"publish":null,"authors":["Xidorn Quan "],"categories":["value-formatting"],"keywords":["float"],"readme":"README.md","repository":"https://github.com/upsuper/dtoa-short","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"equivalent","version":"1.0.2","id":"registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","license":"Apache-2.0 OR MIT","license_file":null,"description":"Traits for key comparison in maps.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"equivalent","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/equivalent-1.0.2/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/equivalent-1.0.2/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["data-structures","no-std"],"keywords":["hashmap","no_std"],"readme":"README.md","repository":"https://github.com/indexmap-rs/equivalent","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":"1.6"},{"name":"errno","version":"0.3.14","id":"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","license":"MIT OR Apache-2.0","license_file":null,"description":"Cross-platform interface to the `errno` variable.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"hermit\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"wasi\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(unix)","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.52, <0.62","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_System_Diagnostics_Debug"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"errno","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/errno-0.3.14/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"default":["std"],"std":["libc/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/errno-0.3.14/Cargo.toml","metadata":null,"publish":null,"authors":["Chris Wong ","Dan Gohman "],"categories":["no-std","os"],"keywords":[],"readme":"README.md","repository":"https://github.com/lambda-fairy/rust-errno","homepage":null,"documentation":"https://docs.rs/errno","edition":"2018","links":null,"default_run":null,"rust_version":"1.56"},{"name":"fastrand","version":"2.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.5.0","license":"Apache-2.0 OR MIT","license_file":null,"description":"A simple and fast random number generator","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wyhash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["wasm_js"],"target":"cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))","registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["wasm_js"],"target":"cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))","registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"fastrand","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"char","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/tests/char.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"smoke","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/tests/smoke.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/benches/bench.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"default":["std"],"getrandom":["dep:getrandom"],"js":["std","getrandom"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fastrand-2.5.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Stjepan Glavina "],"categories":["algorithms"],"keywords":["simple","fast","rand","random","wyrand"],"readme":"README.md","repository":"https://github.com/smol-rs/fastrand","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.63"},{"name":"filetime","version":"0.2.29","id":"registry+https://github.com/rust-lang/crates.io-index#filetime@0.2.29","license":"MIT/Apache-2.0","license_file":null,"description":"Platform-agnostic accessors of timestamps in File metadata\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.27","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"filetime","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/filetime-0.2.29/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/filetime-0.2.29/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":[],"keywords":["timestamp","mtime"],"readme":"README.md","repository":"https://github.com/alexcrichton/filetime","homepage":"https://github.com/alexcrichton/filetime","documentation":"https://docs.rs/filetime","edition":"2018","links":null,"default_run":null,"rust_version":"1.75.0"},{"name":"find-msvc-tools","version":"0.1.9","id":"registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9","license":"MIT OR Apache-2.0","license_file":null,"description":"Find windows-specific tools, read MSVC versions from the registry and from COM interfaces","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"find_msvc_tools","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/find-msvc-tools-0.1.9/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/find-msvc-tools-0.1.9/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["development-tools::build-utils"],"keywords":["build-dependencies"],"readme":"README.md","repository":"https://github.com/rust-lang/cc-rs","homepage":null,"documentation":"https://docs.rs/find-msvc-tools","edition":"2018","links":null,"default_run":null,"rust_version":"1.63"},{"name":"flate2","version":"1.1.10","id":"registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.10","license":"MIT OR Apache-2.0","license_file":null,"description":"DEFLATE compression and decompression exposed as Read/BufRead/Write streams.\nSupports miniz_oxide and multiple zlib implementations. Supports zlib, gzip,\nand raw deflate streams.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"crc32fast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"document-features","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libz-ng-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.16","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libz-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.20","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"miniz_oxide","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["simd"],"target":null,"registry":null},{"name":"zlib-rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["rust-allocator"],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"flate2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"compress_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/compress_file.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"decompress_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/decompress_file.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflatedecoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflatedecoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflatedecoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflatedecoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflatedecoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflatedecoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflateencoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflateencoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflateencoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflateencoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"deflateencoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/deflateencoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzbuilder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzbuilder.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzdecoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzdecoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzdecoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzdecoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzdecoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzdecoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzencoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzencoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzencoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzencoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzencoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzencoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzmultidecoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzmultidecoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gzmultidecoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/gzmultidecoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibdecoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibdecoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibdecoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibdecoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibdecoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibdecoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibencoder-bufread","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibencoder-bufread.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibencoder-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibencoder-read.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"zlibencoder-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/examples/zlibencoder-write.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"capabilities","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/capabilities.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"early-flush","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/early-flush.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"empty-read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/empty-read.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"gunzip","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/gunzip.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"zero-write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/tests/zero-write.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"any_c_zlib":["any_zlib"],"any_impl":[],"any_zlib":["any_impl"],"cloudflare_zlib":["zlib"],"default":["rust_backend","runtime_detection"],"document-features":["dep:document-features"],"libz-ng-sys":["dep:libz-ng-sys"],"libz-sys":["dep:libz-sys"],"miniz-sys":["rust_backend"],"miniz_oxide":["any_impl","dep:miniz_oxide","dep:crc32fast"],"runtime_detection":["zlib-rs?/std","crc32fast?/std"],"rust_backend":["miniz_oxide","any_impl"],"zlib":["any_c_zlib","libz-sys","dep:crc32fast"],"zlib-default":["any_c_zlib","libz-sys/default","dep:crc32fast"],"zlib-ng":["any_c_zlib","libz-ng-sys","dep:crc32fast"],"zlib-ng-compat":["zlib","libz-sys/zlib-ng","dep:crc32fast"],"zlib-rs":["any_zlib","dep:zlib-rs"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.10/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Alex Crichton ","Josh Triplett "],"categories":["compression","api-bindings"],"keywords":["gzip","deflate","zlib","zlib-ng","encoding"],"readme":"README.md","repository":"https://github.com/rust-lang/flate2-rs","homepage":"https://github.com/rust-lang/flate2-rs","documentation":"https://docs.rs/flate2","edition":"2018","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"foldhash","version":"0.1.5","id":"registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5","license":"Zlib","license_file":null,"description":"A fast, non-cryptographic, minimally DoS-resistant hashing algorithm.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"ahash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"chrono","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"fxhash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hashbrown","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.14","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uuid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"foldhash","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/foldhash-0.1.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/foldhash-0.1.5/Cargo.toml","metadata":null,"publish":null,"authors":["Orson Peters "],"categories":["algorithms","no-std"],"keywords":["hash","hasher","no-std"],"readme":"README.md","repository":"https://github.com/orlp/foldhash","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"form_urlencoded","version":"1.2.2","id":"registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Parser and serializer for the application/x-www-form-urlencoded syntax, as used by HTML forms.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"percent-encoding","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"form_urlencoded","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/form_urlencoded-1.2.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":false}],"features":{"alloc":["percent-encoding/alloc"],"default":["std"],"std":["alloc","percent-encoding/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/form_urlencoded-1.2.2/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["The rust-url developers"],"categories":["no-std"],"keywords":[],"readme":null,"repository":"https://github.com/servo/rust-url","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.51"},{"name":"fs2","version":"0.4.3","id":"registry+https://github.com/rust-lang/crates.io-index#fs2@0.4.3","license":"MIT/Apache-2.0","license_file":null,"description":"Cross-platform file locks and file duplication.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tempdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.30","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"winapi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["handleapi","processthreadsapi","winerror","fileapi","winbase","std"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"fs2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fs2-0.4.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fs2-0.4.3/Cargo.toml","metadata":null,"publish":null,"authors":["Dan Burkert "],"categories":[],"keywords":["file","file-system","lock","duplicate","flock"],"readme":"README.md","repository":"https://github.com/danburkert/fs2-rs","homepage":null,"documentation":"https://docs.rs/fs2","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"fsevent-sys","version":"4.1.0","id":"registry+https://github.com/rust-lang/crates.io-index#fsevent-sys@4.1.0","license":"MIT","license_file":null,"description":"Rust bindings to the fsevent macOS API for file changes notifications","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.68","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"fsevent_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fsevent-sys-4.1.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/fsevent-sys-4.1.0/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-apple-darwin"]}}},"publish":null,"authors":["Pierre Baillet "],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/octplane/fsevent-rust/tree/master/fsevent-sys","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"futures-channel","version":"0.3.32","id":"registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.32","license":"MIT OR Apache-2.0","license_file":null,"description":"Channels for asynchronous communication using futures-rs.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-sink","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"futures_channel","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"channel","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/channel.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mpsc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/mpsc.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mpsc-close","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/mpsc-close.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mpsc-size_hint","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/mpsc-size_hint.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"oneshot","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/tests/oneshot.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"sync_mpsc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/benches/sync_mpsc.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"alloc":["futures-core/alloc"],"cfg-target-has-atomic":[],"default":["std"],"futures-sink":["dep:futures-sink"],"sink":["futures-sink"],"std":["alloc","futures-core/std"],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-channel-0.3.32/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/futures-rs","homepage":"https://rust-lang.github.io/futures-rs","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.71"},{"name":"futures-core","version":"0.3.32","id":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","license":"MIT OR Apache-2.0","license_file":null,"description":"The core traits and types in for the `futures` library.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"portable-atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["require-cas"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"futures_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-core-0.3.32/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"cfg-target-has-atomic":[],"default":["std"],"portable-atomic":["dep:portable-atomic"],"std":["alloc"],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-core-0.3.32/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/futures-rs","homepage":"https://rust-lang.github.io/futures-rs","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.36"},{"name":"futures-task","version":"0.3.32","id":"registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.32","license":"MIT OR Apache-2.0","license_file":null,"description":"Tools for working with tasks.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"futures_task","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-task-0.3.32/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"cfg-target-has-atomic":[],"default":["std"],"std":["alloc"],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-task-0.3.32/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/futures-rs","homepage":"https://rust-lang.github.io/futures-rs","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.71"},{"name":"futures-util","version":"0.3.32","id":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","license":"MIT OR Apache-2.0","license_file":null,"description":"Common utilities and extension traits for the futures-rs library.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-io","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"futures-macro","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-sink","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-task","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.32","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.25","kind":null,"rename":"futures_01","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.26","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.6","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"slab","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.7","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"spin","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-io","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.11","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"futures_util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bilock","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/benches/bilock.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"flatten_unordered","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/benches/flatten_unordered.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"futures_unordered","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/benches/futures_unordered.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"select","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/benches/select.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"alloc":["futures-core/alloc","futures-task/alloc","slab"],"async-await":[],"async-await-macro":["async-await","futures-macro"],"bilock":[],"cfg-target-has-atomic":[],"channel":["std","futures-channel"],"compat":["std","futures_01","libc"],"default":["std","async-await","async-await-macro"],"futures-channel":["dep:futures-channel"],"futures-io":["dep:futures-io"],"futures-macro":["dep:futures-macro"],"futures-sink":["dep:futures-sink"],"futures_01":["dep:futures_01"],"io":["std","futures-io","memchr"],"io-compat":["io","compat","tokio-io","libc"],"libc":["dep:libc"],"memchr":["dep:memchr"],"portable-atomic":["futures-core/portable-atomic"],"sink":["futures-sink"],"slab":["dep:slab"],"spin":["dep:spin"],"std":["alloc","futures-core/std","futures-task/std","slab/std"],"tokio-io":["dep:tokio-io"],"unstable":["futures-core/unstable","futures-task/unstable"],"write-all-vectored":["io"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.32/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/futures-rs","homepage":"https://rust-lang.github.io/futures-rs","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.71"},{"name":"generic-array","version":"0.14.7","id":"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","license":"MIT","license_file":null,"description":"Generic types implementing functionality of arrays","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"typenum","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"zeroize","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"version_check","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"generic_array","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/generic-array-0.14.7/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/generic-array-0.14.7/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{"more_lengths":[],"serde":["dep:serde"],"zeroize":["dep:zeroize"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/generic-array-0.14.7/Cargo.toml","metadata":{"docs":{"rs":{"features":["serde","zeroize"]}}},"publish":null,"authors":["Bartłomiej Kamiński ","Aaron Trent "],"categories":["data-structures","no-std"],"keywords":["generic","array"],"readme":"README.md","repository":"https://github.com/fizyk20/generic-array.git","homepage":null,"documentation":"http://fizyk20.github.io/generic-array/generic_array/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"getrandom","version":"0.3.4","id":"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4","license":"MIT OR Apache-2.0","license_file":null,"description":"A small cross-platform library for retrieving random data from system source","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(any(target_os = \"linux\", target_os = \"android\"), not(any(all(target_os = \"linux\", target_env = \"\"), getrandom_backend = \"custom\", getrandom_backend = \"linux_raw\", getrandom_backend = \"rdrand\", getrandom_backend = \"rndr\"))))","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.98","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"js-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.77","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"), target_feature = \"atomics\"))","registry":null},{"name":"wasip2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"wasm32\", target_os = \"wasi\", target_env = \"p2\"))","registry":null},{"name":"r-efi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^5.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(target_os = \"uefi\", getrandom_backend = \"efi_rng\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"hurd\", target_os = \"illumos\", target_os = \"cygwin\", all(target_os = \"horizon\", target_arch = \"arm\")))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"haiku\", target_os = \"redox\", target_os = \"nto\", target_os = \"aix\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"ios\", target_os = \"visionos\", target_os = \"watchos\", target_os = \"tvos\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"macos\", target_os = \"openbsd\", target_os = \"vita\", target_os = \"emscripten\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"netbsd\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"solaris\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.154","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_os = \"vxworks\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"getrandom","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/tests/mod.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/benches/buffer.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"std":[],"wasm_js":["dep:wasm-bindgen","dep:js-sys"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.3.4/Cargo.toml","metadata":{"docs":{"rs":{"features":["std"]}},"cross":{"target":{"x86_64-unknown-netbsd":{"pre-build":["mkdir -p /tmp/netbsd","curl -fO https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.3/amd64/binary/sets/base.tar.xz","tar -C /tmp/netbsd -xJf base.tar.xz","cp /tmp/netbsd/usr/lib/libexecinfo.so /usr/local/x86_64-unknown-netbsd/lib","rm base.tar.xz","rm -rf /tmp/netbsd"]}}}},"publish":null,"authors":["The Rand Project Developers"],"categories":["os","no-std"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-random/getrandom","homepage":null,"documentation":"https://docs.rs/getrandom","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"hashbrown","version":"0.15.5","id":"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.5","license":"MIT OR Apache-2.0","license_file":null,"description":"A Rust port of Google's SwissTable hash map","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"alloc","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"allocator-api2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.9","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"equivalent","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"foldhash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.25","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bumpalo","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.13.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["allocator-api2"],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"fnv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lazy_static","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"hashbrown","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"equivalent_trait","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/equivalent_trait.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"hasher","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/hasher.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rayon","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/rayon.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/serde.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"set","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/tests/set.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"insert_unique_unchecked","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/benches/insert_unique_unchecked.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"set_ops","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/benches/set_ops.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["dep:alloc"],"allocator-api2":["dep:allocator-api2"],"core":["dep:core"],"default":["default-hasher","inline-more","allocator-api2","equivalent","raw-entry"],"default-hasher":["dep:foldhash"],"equivalent":["dep:equivalent"],"inline-more":[],"nightly":["bumpalo/allocator_api"],"raw-entry":[],"rayon":["dep:rayon"],"rustc-dep-of-std":["nightly","core","alloc","rustc-internal-api"],"rustc-internal-api":[],"serde":["dep:serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/Cargo.toml","metadata":{"docs":{"rs":{"features":["nightly","rayon","serde","raw-entry"],"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Amanieu d'Antras "],"categories":["data-structures","no-std"],"keywords":["hash","no_std","hashmap","swisstable"],"readme":"README.md","repository":"https://github.com/rust-lang/hashbrown","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.65.0"},{"name":"html5ever","version":"0.39.0","id":"registry+https://github.com/rust-lang/crates.io-index#html5ever@0.39.0","license":"MIT OR Apache-2.0","license_file":null,"description":"High-performance browser-grade HTML5 parser","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"markup5ever","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.39","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"typed-arena","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"html5ever","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"arena","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/arena.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"noop-tokenize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/noop-tokenize.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"noop-tree-builder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/noop-tree-builder.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"print-tree-actions","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/print-tree-actions.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"tokenize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/examples/tokenize.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"driver","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/tests/driver.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"html5ever","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/benches/html5ever.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"serde":["markup5ever/serde"],"trace_tokenizer":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/html5ever-0.39.0/Cargo.toml","metadata":null,"publish":null,"authors":["The html5ever Project Developers"],"categories":["parser-implementations","web-programming"],"keywords":["html","html5","parser","parsing"],"readme":"README.md","repository":"https://github.com/servo/html5ever","homepage":null,"documentation":"https://docs.rs/html5ever","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"http","version":"1.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A set of types for representing HTTP requests and responses.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"http","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"header_map","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/tests/header_map.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"header_map_fuzz","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/tests/header_map_fuzz.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"status_code","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/tests/status_code.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-1.5.0/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton ","Carl Lerche ","Sean McArthur "],"categories":["web-programming"],"keywords":["http"],"readme":"README.md","repository":"https://github.com/hyperium/http","homepage":null,"documentation":"https://docs.rs/http","edition":"2021","links":null,"default_run":null,"rust_version":"1.57.0"},{"name":"http-body","version":"1.1.0","id":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","license":"MIT","license_file":null,"description":"Trait representing an asynchronous, streaming, HTTP request or response body.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"http_body","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-1.1.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"is_end_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-1.1.0/tests/is_end_stream.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-1.1.0/Cargo.toml","metadata":null,"publish":null,"authors":["Carl Lerche ","Lucio Franco ","Sean McArthur "],"categories":["web-programming"],"keywords":["http"],"readme":"README.md","repository":"https://github.com/hyperium/http-body","homepage":null,"documentation":"https://docs.rs/http-body","edition":"2018","links":null,"default_run":null,"rust_version":"1.61"},{"name":"http-body-util","version":"0.1.4","id":"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","license":"MIT","license_file":null,"description":"Combinators and adapters for HTTP request or response bodies.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["sync"],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","rt","sync","rt-multi-thread"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"http_body_util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-util-0.1.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"channel":["dep:tokio"],"default":[],"full":["channel"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/http-body-util-0.1.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Carl Lerche ","Lucio Franco ","Sean McArthur "],"categories":["web-programming"],"keywords":["http"],"readme":"README.md","repository":"https://github.com/hyperium/http-body","homepage":null,"documentation":"https://docs.rs/http-body-util","edition":"2018","links":null,"default_run":null,"rust_version":"1.61"},{"name":"httparse","version":"1.10.1","id":"registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1","license":"MIT OR Apache-2.0","license_file":null,"description":"A tiny, safe, speedy, zero-copy HTTP/1.x parser.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"httparse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uri","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/tests/uri.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"parse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/benches/parse.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httparse-1.10.1/Cargo.toml","metadata":null,"publish":null,"authors":["Sean McArthur "],"categories":["network-programming","no-std","parser-implementations","web-programming"],"keywords":["http","parser","no_std"],"readme":"README.md","repository":"https://github.com/seanmonstar/httparse","homepage":null,"documentation":"https://docs.rs/httparse","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"httpdate","version":"1.0.3","id":"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3","license":"MIT OR Apache-2.0","license_file":null,"description":"HTTP date parsing and formatting","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"httpdate","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httpdate-1.0.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benchmarks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httpdate-1.0.3/benches/benchmarks.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/httpdate-1.0.3/Cargo.toml","metadata":null,"publish":null,"authors":["Pyfisch "],"categories":[],"keywords":["http","date","time","simple","timestamp"],"readme":"README.md","repository":"https://github.com/pyfisch/httpdate","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.56"},{"name":"hyper","version":"1.11.0","id":"registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","license":"MIT","license_file":null,"description":"A protective and efficient HTTP library for all.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"atomic-waker","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.31","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"h2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.14","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"httparse","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"httpdate","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["const_generics","const_new"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["sync"],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"want","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"form_urlencoded","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["sink"],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc","sink"],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pretty_env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"spmc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["fs","macros","net","io-std","io-util","rt","rt-multi-thread","sync","time","test-util"],"target":null,"registry":null},{"name":"tokio-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing-subscriber","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"hyper","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-1.11.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"capi":[],"client":["dep:want","dep:pin-project-lite","dep:smallvec"],"default":[],"ffi":["dep:http-body-util","dep:futures-util"],"full":["client","http1","http2","server"],"http1":["dep:atomic-waker","dep:futures-channel","dep:futures-core","dep:httparse","dep:itoa"],"http2":["dep:atomic-waker","dep:futures-channel","dep:futures-core","dep:h2"],"nightly":[],"server":["dep:httpdate","dep:pin-project-lite","dep:smallvec"],"tracing":["dep:tracing"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-1.11.0/Cargo.toml","metadata":{"docs":{"rs":{"features":["ffi","full","tracing"],"rustdoc-args":["--cfg","hyper_unstable_ffi","--cfg","hyper_unstable_tracing"]}},"playground":{"features":["full"]},"capi":{"header":{"generation":false,"subdirectory":false},"install":{"include":{"asset":[{"from":"capi/include/hyper.h"}]}}}},"publish":null,"authors":["Sean McArthur "],"categories":["network-programming","web-programming::http-client","web-programming::http-server"],"keywords":["http","hyper","hyperium"],"readme":"README.md","repository":"https://github.com/hyperium/hyper","homepage":"https://hyper.rs","documentation":"https://docs.rs/hyper","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"hyper-util","version":"0.1.20","id":"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.20","license":"MIT","license_file":null,"description":"hyper utilities","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"base64","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.22","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.7.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.16","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http-body","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ipnet","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"percent-encoding","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"socket2","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.5.9, <0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["all"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-service","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.16","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"http-body-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"pretty_env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","test-util","signal"],"target":null,"registry":null},{"name":"tokio-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pnet_datalink","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.35.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(target_os = \"linux\", target_os = \"macos\"))","registry":null},{"name":"system-configuration","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"windows-registry","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.3, <0.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"hyper_util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"client","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/examples/client.rs","edition":"2021","required-features":["client-legacy","http1","tokio"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"server","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/examples/server.rs","edition":"2021","required-features":["server","http1","tokio"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"server_graceful","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/examples/server_graceful.rs","edition":"2021","required-features":["tokio","server-graceful","server-auto"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"legacy_client","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/tests/legacy_client.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"proxy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/tests/proxy.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"__internal_happy_eyeballs_tests":[],"client":["hyper/client","tokio/net","dep:tracing","dep:futures-channel","dep:tower-service"],"client-legacy":["client","dep:socket2","tokio/sync","dep:libc","dep:futures-util"],"client-pool":["client","dep:futures-util","dep:tower-layer"],"client-proxy":["client","dep:base64","dep:ipnet","dep:percent-encoding"],"client-proxy-system":["dep:system-configuration","dep:windows-registry"],"default":[],"full":["client","client-legacy","client-pool","client-proxy","client-proxy-system","server","server-auto","server-graceful","service","http1","http2","tokio","tracing"],"http1":["hyper/http1"],"http2":["hyper/http2"],"server":["hyper/server"],"server-auto":["server","http1","http2"],"server-graceful":["server","tokio/sync"],"service":["dep:tower-service"],"tokio":["dep:tokio","tokio/rt","tokio/time"],"tracing":["dep:tracing"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/Cargo.toml","metadata":{"docs":{"rs":{"features":["full"],"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Sean McArthur "],"categories":["network-programming","web-programming::http-client","web-programming::http-server"],"keywords":["http","hyper","hyperium"],"readme":"README.md","repository":"https://github.com/hyperium/hyper-util","homepage":"https://hyper.rs","documentation":"https://docs.rs/hyper-util","edition":"2021","links":null,"default_run":null,"rust_version":"1.64"},{"name":"icu_collections","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Collection of API for use in ICU libraries.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"potential_utf","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["zerovec"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"utf8_iter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive","yoke"],"target":null,"registry":null},{"name":"iai","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"toml","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["parse"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_collections","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"char16trie","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/tests/char16trie.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cpt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/tests/cpt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"codepointtrie","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/benches/codepointtrie.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"iai_cpt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/benches/iai_cpt.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"inv_list","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/benches/inv_list.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde?/alloc","zerovec/alloc"],"databake":["dep:databake","zerovec/databake"],"serde":["dep:serde","zerovec/serde","potential_utf/serde","alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_collections-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["unicode","data-structures"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_locale_core","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","license":"Unicode-3.0","license_file":null,"description":"API for managing Unicode Language and Locale Identifiers","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"litemap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tinystr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"writeable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"iai","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"litemap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["testing"],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["use-std"],"target":null,"registry":null},{"name":"potential_utf","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_locale_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"langid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/tests/langid.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"locale","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/tests/locale.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"iai_langid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/benches/iai_langid.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"langid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/benches/langid.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"locale","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/benches/locale.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"subtags","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/benches/subtags.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["litemap/alloc","tinystr/alloc","writeable/alloc","serde?/alloc"],"databake":["dep:databake","alloc"],"serde":["dep:serde","tinystr/serde"],"zerovec":["dep:zerovec","tinystr/zerovec"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_locale_core-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-semver-checks":{"lints":{"workspace":true}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["unicode","language-tags","bcp47"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_normalizer","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@2.2.0","license":"Unicode-3.0","license_file":null,"description":"API for normalizing text into Unicode Normalization Forms","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"harfbuzz-traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_collections","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_normalizer_data","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_properties","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_provider","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive","alloc"],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.10.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"utf16_iter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"utf8_iter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"write16","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"arraystring","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"arrayvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"atoi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"detone","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"write16","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["arrayvec","smallvec"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_normalizer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/tests/tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/bench.rs","edition":"2021","required-features":["utf16_iter","utf8_iter"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"canonical_composition","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/canonical_composition.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"canonical_decomposition","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/canonical_decomposition.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"composing_normalizer_nfc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/composing_normalizer_nfc.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"composing_normalizer_nfkc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/composing_normalizer_nfkc.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"decomposing_normalizer_nfd","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/decomposing_normalizer_nfd.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"decomposing_normalizer_nfkd","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/decomposing_normalizer_nfkd.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"utf16_throughput","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/benches/utf16_throughput.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"compiled_data":["dep:icu_normalizer_data","icu_properties?/compiled_data","icu_provider/baked"],"datagen":["serde","dep:databake","icu_properties","icu_collections/databake","zerovec/databake","icu_properties?/datagen","icu_provider/export"],"default":["compiled_data","utf8_iter","utf16_iter"],"harfbuzz_traits":["dep:harfbuzz-traits"],"icu_properties":["dep:icu_properties"],"serde":["dep:serde","icu_collections/serde","zerovec/serde","icu_properties?/serde","icu_provider/serde"],"utf16_iter":["dep:utf16_iter","dep:write16"],"utf8_iter":["dep:utf8_iter"],"write16":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["unicode","normalization","text-processing"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_normalizer_data","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Data for the icu_normalizer crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_normalizer_data","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer_data-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer_data-2.2.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_normalizer_data-2.2.0/Cargo.toml","metadata":{"sources":{"cldr":{"tagged":"48.2.0"},"icuexport":{"tagged":"release-78.1rc"},"segmenter_lstm":{"tagged":"v0.1.0"}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["data","unicode"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_properties","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Definitions for Unicode properties","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"harfbuzz-traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_collections","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_locale_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["zerovec"],"target":null,"registry":null},{"name":"icu_properties_data","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_provider","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"unicode-bidi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.11","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerotrie","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["yoke","zerofrom"],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive","yoke"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_properties","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":["zerovec/alloc","icu_collections/alloc","serde?/alloc"],"compiled_data":["dep:icu_properties_data","icu_provider/baked"],"datagen":["serde","dep:databake","zerovec/databake","icu_collections/databake","icu_locale_core/databake","zerotrie/databake","icu_provider/export"],"default":["compiled_data"],"harfbuzz_traits":["dep:harfbuzz-traits"],"serde":["dep:serde","icu_locale_core/serde","zerovec/serde","icu_collections/serde","icu_provider/serde","zerotrie/serde"],"unicode_bidi":["dep:unicode-bidi"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3},"cargo-semver-checks":{"lints":{"workspace":true}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["character-properties","unicode"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_properties_data","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Data for the icu_properties crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_properties_data","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties_data-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties_data-2.2.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_properties_data-2.2.0/Cargo.toml","metadata":{"sources":{"cldr":{"tagged":"48.2.0"},"icuexport":{"tagged":"release-78.1rc"},"segmenter_lstm":{"tagged":"v0.1.0"}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["data","unicode"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"icu_provider","version":"2.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","license":"Unicode-3.0","license_file":null,"description":"Trait and struct definitions for the ICU data provider","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"erased-serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"icu_locale_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.17","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"stable_deref_trait","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"writeable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerotrie","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"icu_provider","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"data_locale_bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.2.0/benches/data_locale_bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["icu_locale_core/alloc","serde?/alloc","yoke/alloc","zerofrom/alloc","zerovec/alloc","zerotrie?/alloc","dep:stable_deref_trait","dep:writeable"],"baked":["dep:zerotrie","dep:writeable"],"deserialize_bincode_1":["serde","dep:bincode","std"],"deserialize_json":["serde","dep:serde_json"],"deserialize_postcard_1":["serde","dep:postcard"],"export":["serde","dep:erased-serde","dep:databake","std","sync","dep:postcard","zerovec/databake"],"logging":["dep:log"],"serde":["dep:serde","yoke/serde"],"std":["alloc"],"sync":[],"zerotrie":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"denylist":["macros"],"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["data-loading","data"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.86"},{"name":"idna","version":"1.1.0","id":"registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0","license":"MIT OR Apache-2.0","license_file":null,"description":"IDNA (Internationalizing Domain Names in Applications) and Punycode.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"idna_adapter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["const_generics"],"target":null,"registry":null},{"name":"utf8_iter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"assert_matches","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bencher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tester","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"idna","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/src/lib.rs","edition":"2018","doc":true,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/tests/tests.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/tests/unit.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unitbis","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/tests/unitbis.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"all","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/benches/all.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"compiled_data":["idna_adapter/compiled_data"],"default":["std","compiled_data"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna-1.1.0/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["The rust-url developers"],"categories":[],"keywords":["no-std","web","http"],"readme":"README.md","repository":"https://github.com/servo/rust-url/","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.57"},{"name":"idna_adapter","version":"1.2.2","id":"registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.2","license":"Apache-2.0 OR MIT","license_file":null,"description":"Back end adapter for idna","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"icu_normalizer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"icu_properties","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"idna_adapter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna_adapter-1.2.2/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true}],"features":{"compiled_data":["icu_normalizer/compiled_data","icu_properties/compiled_data"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/idna_adapter-1.2.2/Cargo.toml","metadata":null,"publish":null,"authors":["The rust-url developers"],"categories":["no-std","internationalization"],"keywords":["unicode","dns","idna"],"readme":"README.md","repository":"https://github.com/hsivonen/idna_adapter","homepage":"https://docs.rs/crate/idna_adapter/latest","documentation":"https://docs.rs/idna_adapter/latest/idna_adapter/","edition":"2024","links":null,"default_run":null,"rust_version":"1.86"},{"name":"indexmap","version":"2.7.1","id":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","license":"Apache-2.0 OR MIT","license_file":null,"description":"A hash table with consistent order and fast iteration.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"borsh","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"equivalent","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"hashbrown","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.15.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":null,"rename":"rustc-rayon","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"fnv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itertools","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lazy_static","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"indexmap","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"equivalent_trait","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/tests/equivalent_trait.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_full_path","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/tests/macros_full_path.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"quick","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/tests/quick.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/tests/tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"faststring","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/benches/faststring.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"arbitrary":["dep:arbitrary"],"borsh":["dep:borsh"],"default":["std"],"quickcheck":["dep:quickcheck"],"rayon":["dep:rayon"],"rustc-rayon":["dep:rustc-rayon"],"serde":["dep:serde"],"std":[],"test_debug":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.7.1/Cargo.toml","metadata":{"docs":{"rs":{"features":["arbitrary","quickcheck","serde","borsh","rayon"],"rustdoc-args":["--cfg","docsrs"]}},"release":{"allow-branch":["master"],"sign-tag":true,"tag-name":"{{version}}"}},"publish":null,"authors":[],"categories":["data-structures","no-std"],"keywords":["hashmap","no_std"],"readme":"README.md","repository":"https://github.com/indexmap-rs/indexmap","homepage":null,"documentation":"https://docs.rs/indexmap/","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"inotify","version":"0.11.2","id":"registry+https://github.com/rust-lang/crates.io-index#inotify@0.11.2","license":"ISC","license_file":null,"description":"Idiomatic wrapper for inotify","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.30","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"inotify-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.40.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["net"],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.30","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"maplit","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.12.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.40.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","rt-multi-thread","time"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"inotify","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/examples/stream.rs","edition":"2018","required-features":["stream"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"watch","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/examples/watch.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"main","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/tests/main.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"default":["stream"],"futures-util":["dep:futures-util"],"stream":["futures-util","tokio"],"tokio":["dep:tokio"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-0.11.2/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["api-bindings","filesystem"],"keywords":["inotify","linux"],"readme":"README.md","repository":"https://github.com/hannobraun/inotify-rs","homepage":null,"documentation":"https://docs.rs/inotify","edition":"2018","links":null,"default_run":null,"rust_version":"1.70"},{"name":"inotify-sys","version":"0.1.5","id":"registry+https://github.com/rust-lang/crates.io-index#inotify-sys@0.1.5","license":"ISC","license_file":null,"description":"inotify bindings for the Rust programming language","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"inotify_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-sys-0.1.5/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/inotify-sys-0.1.5/Cargo.toml","metadata":null,"publish":null,"authors":["Hanno Braun "],"categories":["external-ffi-bindings","filesystem"],"keywords":["inotify","linux"],"readme":"README.md","repository":"https://github.com/hannobraun/inotify-sys","homepage":null,"documentation":"https://docs.rs/inotify-sys","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"itoa","version":"1.0.18","id":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","license":"MIT OR Apache-2.0","license_file":null,"description":"Fast integer primitive to string conversion","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"no-panic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"itoa","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"no-panic":["dep:no-panic"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["value-formatting","no-std","no-std::no-alloc"],"keywords":["integer"],"readme":"README.md","repository":"https://github.com/dtolnay/itoa","homepage":null,"documentation":"https://docs.rs/itoa","edition":"2021","links":null,"default_run":null,"rust_version":"1.68"},{"name":"js-sys","version":"0.3.102","id":"registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.102","license":"MIT OR Apache-2.0","license_file":null,"description":"Bindings for all JS global objects and functions in all JS environments like\nNode.js and browsers, built on `#[wasm_bindgen]` using the `wasm-bindgen` crate.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.31","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"futures-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"half","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"js_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/js-sys-0.3.102/src/lib.rs","edition":"2021","doc":true,"doctest":false,"test":false}],"features":{"default":["std","unsafe-eval"],"futures-core-03-stream":["dep:futures-util","dep:futures-core"],"std":["wasm-bindgen/std","dep:futures-util"],"unsafe-eval":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/js-sys-0.3.102/Cargo.toml","metadata":null,"publish":null,"authors":["The wasm-bindgen Developers"],"categories":["wasm"],"keywords":[],"readme":"README.md","repository":"https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/js-sys","homepage":"https://wasm-bindgen.github.io/wasm-bindgen/","documentation":"https://docs.rs/js-sys","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"k-wiki","version":"3.0.4","id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki#3.0.4","license":"MIT","license_file":null,"description":"OKF knowledge wiki, projection builder, and MCP service.","source":null,"dependencies":[{"name":"ammonia","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~4.1.8","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"axum","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["http1","json","tokio"],"target":null,"registry":null},{"name":"codebase-graph","source":null,"req":"^3.0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null,"path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo"},{"name":"pulldown-cmark","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["html"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sha2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["io-util","macros","net","rt-multi-thread","signal","sync","time"],"target":null,"registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"yaml_serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"k_wiki","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"k-wiki","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/src/bin/k-wiki.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"api_integration","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/api_integration.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"authoring","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/authoring.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"bundle_reader","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/bundle_reader.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compilation","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/compilation.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"conformance","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/conformance.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"diagnostic_transports","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/diagnostic_transports.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"diagnostics","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/diagnostics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"memory","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/memory.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"projection","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/projection.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"refresh","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/refresh.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rendering","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/rendering.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"search","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/search.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"transports","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/tests/transports.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"bundled-windows-extensions":["codebase-graph/bundled-windows-extensions"],"default":[],"graph-context":[]},"manifest_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki/Cargo.toml","metadata":null,"publish":[],"authors":[],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/rabii-chaarani/codebaseGraph","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"kqueue","version":"1.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#kqueue@1.2.0","license":"MIT","license_file":null,"description":"kqueue interface for BSDs","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"kqueue-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.17","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["html_reports"],"target":null,"registry":null},{"name":"dhat","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"kqueue","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/examples/file.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"pid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/examples/pid.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"dhat_add_hundred","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_add_hundred.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_add_single","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_add_single.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_add_ten","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_add_ten.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_rm_hundred","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_rm_hundred.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_rm_single","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_rm_single.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dhat_rm_ten","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/tests/dhat_rm_ten.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"criterion_scaling","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/benches/criterion_scaling.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-1.2.0/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-netbsd","x86_64-unknown-freebsd","i686-unknown-freebsd","x86_64-unknown-dragonfly","x86_64-unknown-openbsd","aarch64-apple-darwin","x86_64-apple-darwin","aarch64-apple-ios","x86_64-apple-ios"]}}},"publish":null,"authors":["William Orr "],"categories":["os::unix-apis","filesystem"],"keywords":["kqueue","kevent","bsd"],"readme":"README.md","repository":"https://gitlab.com/rust-kqueue/rust-kqueue","homepage":null,"documentation":"https://docs.worrbase.com/rust/kqueue/","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"kqueue-sys","version":"1.1.2","id":"registry+https://github.com/rust-lang/crates.io-index#kqueue-sys@1.1.2","license":"MIT","license_file":null,"description":"Low-level kqueue interface for BSDs","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.11.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.74","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"kqueue_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-sys-1.1.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/kqueue-sys-1.1.2/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-netbsd","x86_64-unknown-freebsd","i686-unknown-freebsd","x86_64-unknown-dragonfly","x86_64-unknown-openbsd","aarch64-apple-darwin","x86_64-apple-darwin","aarch64-apple-ios","x86_64-apple-ios"]}}},"publish":null,"authors":["William Orr ","Daniel (dmilith) Dettlaff "],"categories":["external-ffi-bindings","no-std","os::unix-apis","filesystem"],"keywords":["kqueue","kevent","bsd","darwin","macos"],"readme":"README.md","repository":"https://gitlab.com/rust-kqueue/rust-kqueue-sys","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"lbug","version":"0.19.0","id":"registry+https://github.com/rust-lang/crates.io-index#lbug@0.19.0","license":"MIT","license_file":null,"description":"An in-process property graph database management system built for query speed and scalability","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arrow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^55","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["ffi"],"target":null,"registry":null},{"name":"cxx","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.138","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rust_decimal","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.37","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"time","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uuid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rust_decimal_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.37","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"time","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros"],"target":null,"registry":null},{"name":"cmake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cxx-build","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.138","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"lbug","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"arrow":["dep:arrow"],"default":[],"extension_tests":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":[],"categories":["database"],"keywords":["database","graph","ffi"],"readme":"lbug-src/README.md","repository":"https://github.com/LadybugDB/ladybug-rust","homepage":"https://ladybugdb.com/","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.81"},{"name":"libc","version":"0.2.189","id":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","license":"MIT OR Apache-2.0","license_file":null,"description":"Raw FFI bindings to platform libraries like libc.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"libc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.189/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"const_fn","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.189/tests/const_fn.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.189/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"align":[],"const-extern-fn":[],"default":["std"],"extra_traits":[],"rustc-dep-of-std":["align","rustc-std-workspace-core"],"rustc-std-workspace-core":["dep:rustc-std-workspace-core"],"std":[],"use_std":["std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.189/Cargo.toml","metadata":{"docs":{"rs":{"features":["extra_traits"],"default-target":"x86_64-unknown-linux-gnu","targets":["aarch64-apple-darwin","aarch64-pc-windows-msvc","aarch64-unknown-linux-gnu","i686-pc-windows-msvc","i686-unknown-linux-gnu","x86_64-pc-windows-gnu","x86_64-pc-windows-msvc","x86_64-unknown-linux-gnu","aarch64-pc-windows-gnullvm","aarch64-unknown-linux-musl","aarch64-unknown-linux-ohos","arm-unknown-linux-gnueabi","arm-unknown-linux-gnueabihf","armv7-unknown-linux-gnueabihf","armv7-unknown-linux-ohos","i686-pc-windows-gnu","loongarch64-unknown-linux-gnu","loongarch64-unknown-linux-musl","powerpc-unknown-linux-gnu","powerpc64-unknown-linux-gnu","powerpc64-unknown-linux-musl","powerpc64le-unknown-linux-gnu","powerpc64le-unknown-linux-musl","riscv64gc-unknown-linux-gnu","s390x-unknown-linux-gnu","sparcv9-sun-solaris","x86_64-apple-darwin","x86_64-pc-solaris","x86_64-pc-windows-gnullvm","x86_64-unknown-freebsd","x86_64-unknown-illumos","x86_64-unknown-linux-musl","x86_64-unknown-linux-ohos","x86_64-unknown-netbsd","aarch64-apple-ios","aarch64-apple-tvos","aarch64-apple-visionos","aarch64-apple-watchos","aarch64-linux-android","aarch64-unknown-fuchsia","arm-linux-androideabi","arm-unknown-linux-musleabi","arm-unknown-linux-musleabihf","arm64ec-pc-windows-msvc","armv5te-unknown-linux-gnueabi","armv5te-unknown-linux-musleabi","armv7-linux-androideabi","armv7-unknown-linux-musleabihf","i586-unknown-linux-gnu","i586-unknown-linux-musl","i686-linux-android","i686-unknown-freebsd","i686-unknown-linux-musl","nvptx64-nvidia-cuda","riscv64gc-unknown-linux-musl","sparc64-unknown-linux-gnu","thumbv7neon-linux-androideabi","thumbv7neon-unknown-linux-gnueabihf","wasm32-unknown-emscripten","wasm32-unknown-unknown","wasm32-wasip1","wasm32-wasip2","x86_64-fortanix-unknown-sgx","x86_64-linux-android","x86_64-unknown-fuchsia","x86_64-unknown-linux-gnux32","x86_64-unknown-redox","aarch64-unknown-freebsd","aarch64-unknown-hermit","aarch64-unknown-illumos","aarch64-unknown-netbsd","aarch64-unknown-qnx","aarch64-unknown-openbsd","aarch64-unknown-redox","aarch64-wrs-vxworks","aarch64_be-unknown-linux-gnu","aarch64_be-unknown-linux-musl","armebv7r-none-eabi","armebv7r-none-eabihf","armv7-linux-androideabi","armv7-wrs-vxworks-eabihf","armv7r-none-eabi","armv7r-none-eabihf","hexagon-unknown-linux-musl","i686-unknown-haiku","i686-unknown-netbsd","i686-unknown-openbsd","i686-wrs-vxworks","mips-unknown-linux-gnu","mips-unknown-linux-musl","mips64-unknown-linux-gnuabi64","mips64-unknown-linux-muslabi64","mips64el-unknown-linux-gnuabi64","mips64el-unknown-linux-muslabi64","mipsel-sony-psp","mipsel-unknown-linux-gnu","mipsel-unknown-linux-musl","powerpc-unknown-linux-gnuspe","powerpc-unknown-netbsd","powerpc-wrs-vxworks","powerpc-wrs-vxworks-spe","powerpc64-ibm-aix","powerpc64-unknown-freebsd","powerpc64-unknown-linux-gnu","powerpc64-wrs-vxworks","riscv32-wrs-vxworks","riscv32gc-unknown-linux-musl","riscv32i-unknown-none-elf","riscv32imac-unknown-none-elf","riscv32imc-unknown-none-elf","riscv64-wrs-vxworks","riscv64gc-unknown-freebsd","riscv64gc-unknown-hermit","riscv64gc-unknown-none-elf","riscv64imac-unknown-none-elf","s390x-unknown-linux-musl","sparc-unknown-linux-gnu","sparc64-unknown-netbsd","thumbv6m-none-eabi","thumbv7em-none-eabi","thumbv7em-none-eabihf","thumbv7m-none-eabi","wasm32-wasip3","x86_64-apple-ios","x86_64-pc-cygwin","x86_64-unknown-dragonfly","x86_64-unknown-haiku","x86_64-unknown-hermit","x86_64-unknown-hurd-gnu","x86_64-unknown-l4re-uclibc","x86_64-unknown-openbsd","x86_64-wrs-vxworks"],"cargo-args":["-Zbuild-std=core"]}},"cargo-semver-checks":{"lints":{"repr_align_removed":"warn","global_value_marked_deprecated":"warn"}}},"publish":null,"authors":[],"categories":["external-ffi-bindings","no-std","os"],"keywords":["libc","ffi","bindings","operating","system"],"readme":"README.md","repository":"https://github.com/rust-lang/libc","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"libmimalloc-sys","version":"0.1.49","id":"registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","license":"MIT","license_file":null,"description":"Sys crate wrapping the mimalloc allocator","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cty","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"libmimalloc_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libmimalloc-sys-0.1.49/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libmimalloc-sys-0.1.49/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"arena":[],"cty":["dep:cty"],"debug":[],"debug_in_debug":[],"extended":["cty"],"local_dynamic_tls":[],"no_thp":[],"override":[],"secure":[],"v2":[],"win_direct_tls":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libmimalloc-sys-0.1.49/Cargo.toml","metadata":{"docs":{"rs":{"features":["extended"]}}},"publish":null,"authors":["Octavian Oncescu "],"categories":["memory-management","api-bindings"],"keywords":["allocator","encrypted-heap","performance"],"readme":null,"repository":"https://github.com/purpleprotocol/mimalloc_rust/tree/master/libmimalloc-sys","homepage":null,"documentation":null,"edition":"2018","links":"mimalloc","default_run":null,"rust_version":null},{"name":"libyaml-rs","version":"0.3.0","id":"registry+https://github.com/rust-lang/crates.io-index#libyaml-rs@0.3.0","license":"MIT","license_file":null,"description":"libyaml transpiled to rust by c2rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"pretty_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"libyaml_rs","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"run-emitter-test-suite","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/src/bin/run-emitter-test-suite.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"run-parser-test-suite","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/src/bin/run-parser-test-suite.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_emitter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/tests/test_emitter.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parser","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/tests/test_parser.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parser_error","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/tests/test_parser_error.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libyaml-rs-0.3.0/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["David Tolnay ","YAML Organization "],"categories":["encoding","parser-implementations","no-std"],"keywords":["yaml"],"readme":"README.md","repository":"https://github.com/yaml/libyaml-rs","homepage":null,"documentation":"https://docs.rs/libyaml-rs","edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"link-cplusplus","version":"1.0.12","id":"registry+https://github.com/rust-lang/crates.io-index#link-cplusplus@1.0.12","license":"MIT OR Apache-2.0","license_file":null,"description":"Link libstdc++ or libc++ automatically or manually","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"link_cplusplus","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/link-cplusplus-1.0.12/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/link-cplusplus-1.0.12/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"default":[],"libc++":[],"libcxx":["libc++"],"libstdc++":[],"libstdcxx":["libstdc++"],"nothing":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/link-cplusplus-1.0.12/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["external-ffi-bindings","development-tools::ffi","compilers","no-std","no-std::no-alloc"],"keywords":["linkage","c++"],"readme":"README.md","repository":"https://github.com/dtolnay/link-cplusplus","homepage":null,"documentation":"https://docs.rs/link-cplusplus","edition":"2018","links":"cplusplus","default_run":null,"rust_version":"1.68"},{"name":"linux-raw-sys","version":"0.12.1","id":"registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.12.1","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"Generated bindings for Linux's userspace API","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.100","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"static_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"linux_raw_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/linux-raw-sys-0.12.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"auxvec":[],"bootparam":[],"btrfs":[],"core":["dep:core"],"default":["std","general","errno"],"elf":[],"elf_uapi":[],"errno":[],"general":[],"if_arp":[],"if_ether":[],"if_packet":[],"if_tun":[],"image":[],"io_uring":[],"ioctl":[],"landlock":[],"loop_device":[],"mempolicy":[],"net":[],"netlink":[],"no_std":[],"prctl":[],"ptrace":[],"rustc-dep-of-std":["core","no_std"],"std":[],"system":[],"vm_sockets":[],"xdp":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/linux-raw-sys-0.12.1/Cargo.toml","metadata":{"docs":{"rs":{"features":["default","bootparam","btrfs","elf_uapi","image","ioctl","landlock","netlink","io_uring","if_arp","if_ether","if_packet","net","ptrace","prctl","elf","xdp","mempolicy","system","loop_device"],"targets":["x86_64-unknown-linux-gnu","i686-unknown-linux-gnu"]}}},"publish":null,"authors":["Dan Gohman "],"categories":["external-ffi-bindings"],"keywords":["linux","uapi","ffi"],"readme":"README.md","repository":"https://github.com/sunfishcode/linux-raw-sys","homepage":null,"documentation":"https://docs.rs/linux-raw-sys","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"litemap","version":"0.8.2","id":"registry+https://github.com/rust-lang/crates.io-index#litemap@0.8.2","license":"Unicode-3.0","license_file":null,"description":"A key-value Map implementation based on a flat, sorted Vec.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["use-std"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rkyv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["validation"],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"litemap","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"language_names_hash_map","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/examples/language_names_hash_map.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"language_names_lite_map","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/examples/language_names_lite_map.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"litemap_bincode","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/examples/litemap_bincode.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"litemap_postcard","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/examples/litemap_postcard.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"rkyv","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/tests/rkyv.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/tests/serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"store","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/tests/store.rs","edition":"2021","required-features":["testing"],"doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"litemap","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/benches/litemap.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"databake":["dep:databake"],"default":["alloc"],"serde":["dep:serde_core","alloc"],"testing":["alloc"],"yoke":["dep:yoke"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/litemap-0.8.2/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":[],"keywords":["data-structures","memory-efficiency","sorted","vec","map"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":"https://docs.rs/litemap","edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"lock_api","version":"0.4.14","id":"registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.14","license":"MIT OR Apache-2.0","license_file":null,"description":"Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"owning_ref","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"scopeguard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.126","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"lock_api","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lock_api-0.4.14/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"arc_lock":[],"atomic_usize":[],"default":["atomic_usize"],"nightly":[],"owning_ref":["dep:owning_ref"],"serde":["dep:serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lock_api-0.4.14/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--generate-link-to-definition"]}}},"publish":null,"authors":["Amanieu d'Antras "],"categories":["concurrency","no-std"],"keywords":["mutex","rwlock","lock","no_std"],"readme":null,"repository":"https://github.com/Amanieu/parking_lot","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"log","version":"0.4.32","id":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","license":"MIT OR Apache-2.0","license_file":null,"description":"A lightweight logging facade for Rust\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"sval","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.16","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"sval_ref","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.16","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"value-bag","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["inline-i128"],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.63","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sval","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.16","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sval_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.16","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"value-bag","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["test"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"log","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"integration","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/tests/integration.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/tests/macros.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"value","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/benches/value.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"kv":[],"kv_serde":["kv_std","value-bag/serde","serde"],"kv_std":["std","kv","value-bag/error"],"kv_sval":["kv","value-bag/sval","sval","sval_ref"],"kv_unstable":["kv","value-bag"],"kv_unstable_serde":["kv_serde","kv_unstable_std"],"kv_unstable_std":["kv_std","kv_unstable"],"kv_unstable_sval":["kv_sval","kv_unstable"],"max_level_debug":[],"max_level_error":[],"max_level_info":[],"max_level_off":[],"max_level_trace":[],"max_level_warn":[],"release_max_level_debug":[],"release_max_level_error":[],"release_max_level_info":[],"release_max_level_off":[],"release_max_level_trace":[],"release_max_level_warn":[],"serde":["serde_core"],"serde_core":["dep:serde_core"],"std":[],"sval":["dep:sval"],"sval_ref":["dep:sval_ref"],"value-bag":["dep:value-bag"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/log-0.4.32/Cargo.toml","metadata":{"docs":{"rs":{"features":["std","serde","kv_std","kv_sval","kv_serde"]}}},"publish":null,"authors":["The Rust Project Developers"],"categories":["development-tools::debugging"],"keywords":["logging"],"readme":"README.md","repository":"https://github.com/rust-lang/log","homepage":null,"documentation":"https://docs.rs/log","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"lzma-rs","version":"0.3.0","id":"registry+https://github.com/rust-lang/crates.io-index#lzma-rs@0.3.0","license":"MIT","license_file":null,"description":"A codec for LZMA, LZMA2 and XZ written in pure Rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"byteorder","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"crc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.17","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rust-lzma","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"lzma_rs","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lzma-rs-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"enable_logging":["env_logger","log"],"env_logger":["dep:env_logger"],"log":["dep:log"],"raw_decoder":[],"stream":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lzma-rs-0.3.0/Cargo.toml","metadata":{"docs":{"rs":{"features":["stream","raw_decoder"],"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["Guillaume Endignoux "],"categories":["compression"],"keywords":["lzma","compression","decompression"],"readme":"README.md","repository":"https://github.com/gendx/lzma-rs","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"maplit","version":"1.0.2","id":"registry+https://github.com/rust-lang/crates.io-index#maplit@1.0.2","license":"MIT/Apache-2.0","license_file":null,"description":"Collection “literal” macros for HashMap, HashSet, BTreeMap, and BTreeSet.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"maplit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/maplit-1.0.2/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/maplit-1.0.2/tests/tests.rs","edition":"2015","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/maplit-1.0.2/Cargo.toml","metadata":{"release":{"no-dev-version":true}},"publish":null,"authors":["bluss"],"categories":["rust-patterns"],"keywords":["literal","data-structure","hashmap","macro"],"readme":null,"repository":"https://github.com/bluss/maplit","homepage":null,"documentation":"https://docs.rs/maplit/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"markup5ever","version":"0.39.0","id":"registry+https://github.com/rust-lang/crates.io-index#markup5ever@0.39.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Common code for xml5ever and html5ever","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tendril","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"web_atoms","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"markup5ever","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/markup5ever-0.39.0/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"serde":["web_atoms/serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/markup5ever-0.39.0/Cargo.toml","metadata":null,"publish":null,"authors":["The html5ever Project Developers"],"categories":["parser-implementations","web-programming"],"keywords":[],"readme":null,"repository":"https://github.com/servo/html5ever","homepage":null,"documentation":"https://docs.rs/markup5ever","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"matchit","version":"0.8.4","id":"registry+https://github.com/rust-lang/crates.io-index#matchit@0.8.4","license":"MIT AND BSD-3-Clause","license_file":null,"description":"A high performance, zero-copy URL router.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"actix-router","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"gonzales","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.0.3-beta","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"hyper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.14","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"path-tree","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"regex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.5.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"route-recognizer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"routefinder","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"tower","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["make","util"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"matchit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"hyper","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/examples/hyper.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"insert","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/tests/insert.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"match","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/tests/match.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"remove","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/tests/remove.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"__test_helpers":[],"default":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/matchit-0.8.4/Cargo.toml","metadata":null,"publish":null,"authors":["Ibraheem Ahmed "],"categories":["network-programming","algorithms"],"keywords":["router","path","tree","match","url"],"readme":"README.md","repository":"https://github.com/ibraheemdev/matchit","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"memchr","version":"2.8.2","id":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","license":"Unlicense OR MIT","license_file":null,"description":"Provides extremely fast (uses SIMD on x86_64, aarch64 and wasm32) routines for\n1, 2 or 3 byte search and single substring search.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.20","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"memchr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/memchr-2.8.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"core":["dep:core"],"default":["std"],"libc":[],"logging":["dep:log"],"rustc-dep-of-std":["core"],"std":["alloc"],"use_std":["std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/memchr-2.8.2/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Andrew Gallant ","bluss"],"categories":[],"keywords":["memchr","memmem","substring","find","search"],"readme":"README.md","repository":"https://github.com/BurntSushi/memchr","homepage":"https://github.com/BurntSushi/memchr","documentation":"https://docs.rs/memchr/","edition":"2021","links":null,"default_run":null,"rust_version":"1.61"},{"name":"mimalloc","version":"0.1.52","id":"registry+https://github.com/rust-lang/crates.io-index#mimalloc@0.1.52","license":"MIT","license_file":null,"description":"Performance and security oriented drop-in allocator","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libmimalloc-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.49","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"mimalloc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mimalloc-0.1.52/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"debug":["libmimalloc-sys/debug"],"debug_in_debug":["libmimalloc-sys/debug_in_debug"],"default":[],"extended":["libmimalloc-sys/extended"],"local_dynamic_tls":["libmimalloc-sys/local_dynamic_tls"],"no_thp":["libmimalloc-sys/no_thp"],"override":["libmimalloc-sys/override"],"secure":["libmimalloc-sys/secure"],"v2":["libmimalloc-sys/v2"],"win_direct_tls":["libmimalloc-sys/win_direct_tls"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mimalloc-0.1.52/Cargo.toml","metadata":null,"publish":null,"authors":["Octavian Oncescu ","Vincent Rouillé ","Thom Chiovoloni "],"categories":["memory-management","api-bindings"],"keywords":["mimalloc","allocator","encrypted-heap","performance"],"readme":"README.md","repository":"https://github.com/purpleprotocol/mimalloc_rust","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"mime","version":"0.3.17","id":"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","license":"MIT OR Apache-2.0","license_file":null,"description":"Strongly Typed Mimes","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"mime","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"cmp","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/benches/cmp.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"fmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/benches/fmt.rs","edition":"2015","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"parse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/benches/parse.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mime-0.3.17/Cargo.toml","metadata":null,"publish":null,"authors":["Sean McArthur "],"categories":[],"keywords":["mime","media-extensions","media-types"],"readme":"README.md","repository":"https://github.com/hyperium/mime","homepage":null,"documentation":"https://docs.rs/mime","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"miniz_oxide","version":"0.9.1","id":"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.9.1","license":"MIT OR Zlib OR Apache-2.0","license_file":null,"description":"DEFLATE compression and decompression library rewritten in Rust based on miniz","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"adler2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"alloc","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive","alloc"],"target":null,"registry":null},{"name":"simd-adler32","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"miniz_oxide","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/miniz_oxide-0.9.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":["dep:alloc"],"block-boundary":[],"core":["dep:core"],"default":["with-alloc"],"rustc-dep-of-std":["core","alloc","adler2/rustc-dep-of-std"],"serde":["dep:serde"],"simd":["simd-adler32"],"simd-adler32":["dep:simd-adler32"],"std":["serde?/std"],"with-alloc":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/miniz_oxide-0.9.1/Cargo.toml","metadata":null,"publish":null,"authors":["Frommi ","oyvindln ","Rich Geldreich richgel99@gmail.com"],"categories":["compression"],"keywords":["zlib","miniz","deflate","encoding"],"readme":"Readme.md","repository":"https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide","homepage":"https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide","documentation":"https://docs.rs/miniz_oxide","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"mio","version":"1.2.1","id":"registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","license":"MIT","license_file":null,"description":"Lightweight non-blocking I/O.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.8","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.183","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(unix, target_os = \"hermit\", target_os = \"wasi\"))","registry":null},{"name":"wasi","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"wasi\")","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Wdk_Foundation","Wdk_Storage_FileSystem","Wdk_System_IO","Win32_Foundation","Win32_Networking_WinSock","Win32_Storage_FileSystem","Win32_Security","Win32_System_IO","Win32_System_WindowsProgramming"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"mio","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"tcp_listenfd_server","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/examples/tcp_listenfd_server.rs","edition":"2021","required-features":["os-poll","net"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"tcp_server","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/examples/tcp_server.rs","edition":"2021","required-features":["os-poll","net"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"udp_server","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/examples/udp_server.rs","edition":"2021","required-features":["os-poll","net"],"doc":false,"doctest":false,"test":false}],"features":{"default":["log"],"log":["dep:log"],"net":[],"os-ext":["os-poll","windows-sys/Win32_System_Pipes","windows-sys/Win32_Security"],"os-poll":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.2.1/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--generate-link-to-definition"],"targets":["aarch64-apple-ios","aarch64-linux-android","wasm32-wasip1","x86_64-apple-darwin","x86_64-pc-windows-gnu","x86_64-pc-windows-msvc","x86_64-unknown-dragonfly","x86_64-unknown-freebsd","x86_64-unknown-illumos","x86_64-unknown-linux-gnu","x86_64-unknown-netbsd","x86_64-unknown-openbsd","x86_64-unknown-hermit"]}},"playground":{"features":["os-poll","os-ext","net"]}},"publish":null,"authors":["Carl Lerche ","Thomas de Zeeuw ","Tokio Contributors "],"categories":["asynchronous"],"keywords":["io","async","non-blocking"],"readme":"README.md","repository":"https://github.com/tokio-rs/mio","homepage":"https://github.com/tokio-rs/mio","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"new_debug_unreachable","version":"1.0.6","id":"registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","license":"MIT","license_file":null,"description":"panic in debug, intrinsics::unreachable() in release (fork of debug_unreachable)","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"debug_unreachable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/new_debug_unreachable-1.0.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"simple","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/new_debug_unreachable-1.0.6/examples/simple.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"check","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/new_debug_unreachable-1.0.6/tests/check.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/new_debug_unreachable-1.0.6/Cargo.toml","metadata":null,"publish":null,"authors":["Matt Brubeck ","Jonathan Reem "],"categories":["rust-patterns","no-std"],"keywords":["optimization","macro"],"readme":"README.md","repository":"https://github.com/mbrubeck/rust-debug-unreachable","homepage":null,"documentation":"https://docs.rs/new_debug_unreachable","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"notify","version":"8.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#notify@8.2.0","license":"CC0-1.0","license_file":null,"description":"Cross-platform filesystem notification library","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"crossbeam-channel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flume","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.17","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"notify-types","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.34.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"nix","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.29.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.39","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.10.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"kqueue","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonflybsd\", target_os = \"ios\"))","registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["os-ext"],"target":"cfg(any(target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonflybsd\", target_os = \"ios\"))","registry":null},{"name":"inotify","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any(target_os = \"linux\", target_os = \"android\"))","registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["os-ext"],"target":"cfg(any(target_os = \"linux\", target_os = \"android\"))","registry":null},{"name":"trash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^5.2.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"windows\")","registry":null},{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.7.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"fsevent-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^4.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"kqueue","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["os-ext"],"target":"cfg(target_os = \"macos\")","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.60.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_System_Threading","Win32_Foundation","Win32_Storage_FileSystem","Win32_Security","Win32_System_WindowsProgramming","Win32_System_IO"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"notify","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/notify-8.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"crossbeam-channel":["dep:crossbeam-channel"],"default":["macos_fsevent"],"flume":["dep:flume"],"fsevent-sys":["dep:fsevent-sys"],"kqueue":["dep:kqueue"],"macos_fsevent":["fsevent-sys"],"macos_kqueue":["kqueue","mio"],"mio":["dep:mio"],"serde":["notify-types/serde"],"serialization-compat-6":["notify-types/serialization-compat-6"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/notify-8.2.0/Cargo.toml","metadata":null,"publish":null,"authors":["Félix Saparelli ","Daniel Faust ","Aron Heinecke "],"categories":["filesystem"],"keywords":["events","filesystem","notify","watch"],"readme":"README.md","repository":"https://github.com/notify-rs/notify.git","homepage":"https://github.com/notify-rs/notify","documentation":"https://docs.rs/notify","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"notify-types","version":"2.0.0","id":"registry+https://github.com/rust-lang/crates.io-index#notify-types@2.0.0","license":"MIT OR Apache-2.0","license_file":null,"description":"Types used by the notify crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.89","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"web-time","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.34.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rstest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.39","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"notify_types","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/notify-types-2.0.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"serde":["dep:serde"],"serialization-compat-6":[],"web-time":["dep:web-time"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/notify-types-2.0.0/Cargo.toml","metadata":null,"publish":null,"authors":["Daniel Faust "],"categories":["filesystem"],"keywords":["events","filesystem","notify","watch"],"readme":"README.md","repository":"https://github.com/notify-rs/notify.git","homepage":"https://github.com/notify-rs/notify","documentation":"https://docs.rs/notify-types","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"num-conv","version":"0.1.0","id":"registry+https://github.com/rust-lang/crates.io-index#num-conv@0.1.0","license":"MIT OR Apache-2.0","license_file":null,"description":"`num_conv` is a crate to convert between integer types without using `as` casts. This provides\nbetter certainty when refactoring, makes the exact behavior of code more explicit, and allows using\nturbofish syntax.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"num_conv","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-conv-0.1.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-conv-0.1.0/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Jacob Pratt "],"categories":["no-std","no-std::no-alloc","rust-patterns"],"keywords":["cast","extend","truncate","convert","integer"],"readme":"README.md","repository":"https://github.com/jhpratt/num-conv","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.57.0"},{"name":"num-traits","version":"0.2.19","id":"registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19","license":"MIT OR Apache-2.0","license_file":null,"description":"Numeric traits for generic mathematics","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"autocfg","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"num_traits","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-traits-0.2.19/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"cast","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-traits-0.2.19/tests/cast.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-traits-0.2.19/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"i128":[],"libm":["dep:libm"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-traits-0.2.19/Cargo.toml","metadata":{"docs":{"rs":{"features":["std"],"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["The Rust Project Developers"],"categories":["algorithms","science","no-std"],"keywords":["mathematics","numerics"],"readme":"README.md","repository":"https://github.com/rust-num/num-traits","homepage":"https://github.com/rust-num/num-traits","documentation":"https://docs.rs/num-traits","edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"once_cell","version":"1.21.4","id":"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4","license":"MIT OR Apache-2.0","license_file":null,"description":"Single assignment cells and lazy values.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"critical-section","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"parking_lot_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.10","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"portable-atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"critical-section","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["std"],"target":null,"registry":null},{"name":"regex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.10.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"once_cell","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/bench.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"bench_acquire","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/bench_acquire.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"lazy_static","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/lazy_static.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"reentrant_init_deadlocks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/reentrant_init_deadlocks.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"regex","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/regex.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"test_synchronization","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/examples/test_synchronization.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"it","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/tests/it/main.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"alloc":["race"],"atomic-polyfill":["critical-section"],"critical-section":["dep:critical-section","portable-atomic"],"default":["std"],"parking_lot":["dep:parking_lot_core"],"portable-atomic":["dep:portable-atomic"],"race":[],"std":["alloc"],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/once_cell-1.21.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Aleksey Kladov "],"categories":["rust-patterns","memory-management"],"keywords":["lazy","static"],"readme":"README.md","repository":"https://github.com/matklad/once_cell","homepage":null,"documentation":"https://docs.rs/once_cell","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"parking_lot","version":"0.12.5","id":"registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5","license":"MIT OR Apache-2.0","license_file":null,"description":"More compact and efficient implementations of the standard synchronization primitives.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"lock_api","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.14","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"parking_lot_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.12","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"parking_lot","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"issue_203","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.5/tests/issue_203.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"issue_392","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.5/tests/issue_392.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"arc_lock":["lock_api/arc_lock"],"deadlock_detection":["parking_lot_core/deadlock_detection"],"default":[],"hardware-lock-elision":[],"nightly":["parking_lot_core/nightly","lock_api/nightly"],"owning_ref":["lock_api/owning_ref"],"send_guard":[],"serde":["lock_api/serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.5/Cargo.toml","metadata":{"docs":{"rs":{"features":["arc_lock","serde","deadlock_detection"],"rustdoc-args":["--generate-link-to-definition"]}},"playground":{"features":["arc_lock","serde","deadlock_detection"]}},"publish":null,"authors":["Amanieu d'Antras "],"categories":["concurrency"],"keywords":["mutex","condvar","rwlock","once","thread"],"readme":"README.md","repository":"https://github.com/Amanieu/parking_lot","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"parking_lot_core","version":"0.9.12","id":"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12","license":"MIT OR Apache-2.0","license_file":null,"description":"An advanced API for creating custom synchronization primitives.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"backtrace","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.60","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"petgraph","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"smallvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"redox_syscall","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"redox\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.95","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"windows-link","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"parking_lot_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot_core-0.9.12/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot_core-0.9.12/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"backtrace":["dep:backtrace"],"deadlock_detection":["petgraph","backtrace"],"nightly":[],"petgraph":["dep:petgraph"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot_core-0.9.12/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Amanieu d'Antras "],"categories":["concurrency"],"keywords":["mutex","condvar","rwlock","once","thread"],"readme":null,"repository":"https://github.com/Amanieu/parking_lot","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"percent-encoding","version":"2.3.2","id":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Percent encoding and decoding","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"percent_encoding","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/percent-encoding-2.3.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["std"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/percent-encoding-2.3.2/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["The rust-url developers"],"categories":["no-std"],"keywords":[],"readme":null,"repository":"https://github.com/servo/rust-url/","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.51"},{"name":"phf","version":"0.13.1","id":"registry+https://github.com/rust-lang/crates.io-index#phf@0.13.1","license":"MIT","license_file":null,"description":"Runtime support for perfect hash function data structures","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"phf_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"phf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf-0.13.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":false}],"features":{"default":["std"],"macros":["phf_macros"],"phf_macros":["dep:phf_macros"],"serde":["dep:serde"],"std":["phf_shared/std","serde?/std"],"uncased":["phf_macros?/uncased","phf_shared/uncased"],"unicase":["phf_macros?/unicase","phf_shared/unicase"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf-0.13.1/Cargo.toml","metadata":{"docs":{"rs":{"features":["macros"]}},"playground":{"all-features":false,"default-features":true,"features":["macros"]}},"publish":null,"authors":["Steven Fackler "],"categories":["data-structures","no-std"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-phf/rust-phf","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.66"},{"name":"phf_codegen","version":"0.13.1","id":"registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.13.1","license":"MIT","license_file":null,"description":"Codegen library for PHF types","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"phf_generator","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"phf_codegen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_codegen-0.13.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_codegen-0.13.1/Cargo.toml","metadata":null,"publish":null,"authors":["Steven Fackler "],"categories":["data-structures"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-phf/rust-phf","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.66"},{"name":"phf_generator","version":"0.13.1","id":"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","license":"MIT","license_file":null,"description":"PHF generation logic","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"fastrand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"phf_generator","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_generator-0.13.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"gen_hash_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_generator-0.13.1/src/bin/gen_hash_test.rs","edition":"2021","doc":true,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benches","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_generator-0.13.1/benches/benches.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_generator-0.13.1/Cargo.toml","metadata":null,"publish":null,"authors":["Steven Fackler "],"categories":["data-structures"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-phf/rust-phf","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.66"},{"name":"phf_shared","version":"0.13.1","id":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","license":"MIT","license_file":null,"description":"Support code shared by PHF libraries","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"siphasher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uncased","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.9","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unicase","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"phf_shared","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_shared-0.13.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":false}],"features":{"default":["std"],"std":[],"uncased":["dep:uncased"],"unicase":["dep:unicase"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/phf_shared-0.13.1/Cargo.toml","metadata":null,"publish":null,"authors":["Steven Fackler "],"categories":["data-structures"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-phf/rust-phf","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.66"},{"name":"pin-project-lite","version":"0.2.17","id":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","license":"Apache-2.0 OR MIT","license_file":null,"description":"A lightweight version of pin-project written with declarative macros.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"static_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"pin_project_lite","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/compiletest.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"drop_order","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/drop_order.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"expandtest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/expandtest.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"proper_unpin","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/proper_unpin.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/tests/test.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pin-project-lite-0.2.17/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"]}},"cargo_check_external_types":{"allowed_external_types":[]}},"publish":null,"authors":[],"categories":["no-std","no-std::no-alloc","rust-patterns"],"keywords":["pin","macros"],"readme":"README.md","repository":"https://github.com/taiki-e/pin-project-lite","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.37"},{"name":"potential_utf","version":"0.1.5","id":"registry+https://github.com/rust-lang/crates.io-index#potential_utf@0.1.5","license":"Unicode-3.0","license_file":null,"description":"Unvalidated string and character types","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"writeable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"potential_utf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/potential_utf-0.1.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":["serde_core?/alloc","writeable/alloc","zerovec?/alloc"],"databake":["dep:databake"],"default":["alloc"],"serde":["dep:serde_core"],"writeable":["dep:writeable"],"zerovec":["dep:zerovec"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/potential_utf-0.1.5/Cargo.toml","metadata":null,"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["text-processing","encoding"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"powerfmt","version":"0.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","license":"MIT OR Apache-2.0","license_file":null,"description":" `powerfmt` is a library that provides utilities for formatting values. This crate makes it\n significantly easier to support filling to a minimum width with alignment, avoid heap\n allocation, and avoid repetitive calculations.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"powerfmt-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"powerfmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/powerfmt-0.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["std","macros"],"macros":["dep:powerfmt-macros"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/powerfmt-0.2.0/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","__powerfmt_docs","--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["Jacob Pratt "],"categories":["no-std","no-std::no-alloc","rust-patterns"],"keywords":["display","format","fmt","formatter","extension"],"readme":"README.md","repository":"https://github.com/jhpratt/powerfmt","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"precomputed-hash","version":"0.1.1","id":"registry+https://github.com/rust-lang/crates.io-index#precomputed-hash@0.1.1","license":"MIT","license_file":null,"description":"A library intending to be a base dependency to expose a precomputed hash","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"precomputed_hash","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/precomputed-hash-0.1.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/precomputed-hash-0.1.1/Cargo.toml","metadata":null,"publish":null,"authors":["Emilio Cobos Álvarez "],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/emilio/precomputed-hash","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"proc-macro2","version":"1.0.106","id":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","license":"MIT OR Apache-2.0","license_file":null,"description":"A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"unicode-ident","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"proc_macro2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"comments","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/comments.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"features","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/features.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"marker","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/marker.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_fmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/test_fmt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_size","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/tests/test_size.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["proc-macro"],"nightly":[],"proc-macro":[],"span-locations":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.106/Cargo.toml","metadata":{"docs":{"rs":{"rustc-args":["--cfg=procmacro2_semver_exempt"],"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--cfg=procmacro2_semver_exempt","--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}},"playground":{"features":["span-locations"]}},"publish":null,"authors":["David Tolnay ","Alex Crichton "],"categories":["development-tools::procedural-macro-helpers"],"keywords":["macros","syn"],"readme":"README.md","repository":"https://github.com/dtolnay/proc-macro2","homepage":null,"documentation":"https://docs.rs/proc-macro2","edition":"2021","links":null,"default_run":null,"rust_version":"1.68"},{"name":"pulldown-cmark","version":"0.13.4","id":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark@0.13.4","license":"MIT","license_file":null,"description":"A pull parser for CommonMark","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"getopts","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pulldown-cmark-escape","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"unicase","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.6","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"lazy_static","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"regex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.61","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"pulldown_cmark","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bin"],"crate_types":["bin"],"name":"pulldown-cmark","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/src/main.rs","edition":"2021","required-features":["getopts"],"doc":false,"doctest":false,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"broken-link-callbacks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/broken-link-callbacks.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"event-filter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/event-filter.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"events","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/events.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"footnote-rewrite","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/footnote-rewrite.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"normalize-wikilink","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/normalize-wikilink.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"parser-map-event-print","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/parser-map-event-print.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"parser-map-tag-print","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/parser-map-tag-print.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"string-to-string","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/examples/string-to-string.rs","edition":"2021","required-features":["html"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"errors","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/tests/errors.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"html","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/tests/html.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"lib","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/tests/lib.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/tests/serde.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["getopts","html"],"gen-tests":[],"getopts":["dep:getopts"],"html":["pulldown-cmark-escape"],"pulldown-cmark-escape":["dep:pulldown-cmark-escape"],"serde":["dep:serde"],"simd":["pulldown-cmark-escape?/simd"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.4/Cargo.toml","metadata":null,"publish":null,"authors":["Raph Levien ","Marcus Klaas de Vries "],"categories":["text-processing"],"keywords":["markdown","commonmark"],"readme":"README.md","repository":"https://github.com/raphlinus/pulldown-cmark","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.1"},{"name":"pulldown-cmark-escape","version":"0.11.0","id":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark-escape@0.11.0","license":"MIT","license_file":null,"description":"An escape library for HTML created in the pulldown-cmark project","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"pulldown_cmark_escape","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-escape-0.11.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"simd":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-escape-0.11.0/Cargo.toml","metadata":null,"publish":null,"authors":["Raph Levien ","Marcus Klaas de Vries "],"categories":["text-processing"],"keywords":["escape","html","markdown","commonmark"],"readme":"./README.md","repository":"https://github.com/raphlinus/pulldown-cmark","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.70"},{"name":"quote","version":"1.0.45","id":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","license":"MIT OR Apache-2.0","license_file":null,"description":"Quasi-quoting macro quote!(...)","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.80","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.108","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["diff"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"quote","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/tests/compiletest.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["proc-macro"],"proc-macro":["proc-macro2/proc-macro"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.45/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::procedural-macro-helpers"],"keywords":["macros","syn"],"readme":"README.md","repository":"https://github.com/dtolnay/quote","homepage":null,"documentation":"https://docs.rs/quote/","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"r-efi","version":"5.3.0","id":"registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0","license":"MIT OR Apache-2.0 OR LGPL-2.1-or-later","license_file":null,"description":"UEFI Reference Specification Protocol Constants and Definitions","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"r_efi","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"freestanding","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/examples/freestanding.rs","edition":"2018","required-features":["native"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"gop-query","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/examples/gop-query.rs","edition":"2018","required-features":["native"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"hello-world","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/examples/hello-world.rs","edition":"2018","required-features":["native"],"doc":false,"doctest":false,"test":false}],"features":{"core":["dep:core"],"efiapi":[],"examples":["native"],"native":[],"rustc-dep-of-std":["core"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/r-efi-5.3.0/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["embedded","hardware-support","no-std","os"],"keywords":["boot","efi","firmware","specification","uefi"],"readme":"README.md","repository":"https://github.com/r-efi/r-efi","homepage":"https://github.com/r-efi/r-efi/wiki","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.68"},{"name":"redox_syscall","version":"0.5.18","id":"registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.5.18","license":"MIT","license_file":null,"description":"A Rust library to access raw Redox system calls","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"loom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(loom)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"syscall","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/redox_syscall-0.5.18/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"core":["dep:core"],"default":["userspace"],"rustc-dep-of-std":["core","bitflags/rustc-dep-of-std"],"std":[],"userspace":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/redox_syscall-0.5.18/Cargo.toml","metadata":null,"publish":null,"authors":["Jeremy Soller "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://gitlab.redox-os.org/redox-os/syscall","homepage":null,"documentation":"https://docs.rs/redox_syscall","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"regex","version":"1.12.4","id":"registry+https://github.com/rust-lang/crates.io-index#regex@1.12.4","license":"MIT OR Apache-2.0","license_file":null,"description":"An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"aho-corasick","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"regex-automata","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.12","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc","syntax","meta","nfa-pikevm"],"target":null,"registry":null},{"name":"regex-syntax","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.11","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.69","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["atty","humantime","termcolor"],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"regex-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"regex","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.12.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"integration","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.12.4/tests/lib.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std","perf","unicode","regex-syntax/default"],"logging":["aho-corasick?/logging","memchr?/logging","regex-automata/logging"],"pattern":[],"perf":["perf-cache","perf-dfa","perf-onepass","perf-backtrack","perf-inline","perf-literal"],"perf-backtrack":["regex-automata/nfa-backtrack"],"perf-cache":[],"perf-dfa":["regex-automata/hybrid"],"perf-dfa-full":["regex-automata/dfa-build","regex-automata/dfa-search"],"perf-inline":["regex-automata/perf-inline"],"perf-literal":["dep:aho-corasick","dep:memchr","regex-automata/perf-literal"],"perf-onepass":["regex-automata/dfa-onepass"],"std":["aho-corasick?/std","memchr?/std","regex-automata/std","regex-syntax/std"],"unicode":["unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment","regex-automata/unicode","regex-syntax/unicode"],"unicode-age":["regex-automata/unicode-age","regex-syntax/unicode-age"],"unicode-bool":["regex-automata/unicode-bool","regex-syntax/unicode-bool"],"unicode-case":["regex-automata/unicode-case","regex-syntax/unicode-case"],"unicode-gencat":["regex-automata/unicode-gencat","regex-syntax/unicode-gencat"],"unicode-perl":["regex-automata/unicode-perl","regex-automata/unicode-word-boundary","regex-syntax/unicode-perl"],"unicode-script":["regex-automata/unicode-script","regex-syntax/unicode-script"],"unicode-segment":["regex-automata/unicode-segment","regex-syntax/unicode-segment"],"unstable":["pattern"],"use_std":["std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.12.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs_regex"]}}},"publish":null,"authors":["The Rust Project Developers","Andrew Gallant "],"categories":["text-processing"],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/regex","homepage":"https://github.com/rust-lang/regex","documentation":"https://docs.rs/regex","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"regex-automata","version":"0.4.14","id":"registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.14","license":"MIT OR Apache-2.0","license_file":null,"description":"Automata construction and matching using regular expressions.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"aho-corasick","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"log","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.14","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.6.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"regex-syntax","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.5","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.69","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bstr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"env_logger","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["atty","humantime","termcolor"],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"regex-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"regex_automata","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-automata-0.4.14/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"integration","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-automata-0.4.14/tests/lib.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"alloc":[],"default":["std","syntax","perf","unicode","meta","nfa","dfa","hybrid"],"dfa":["dfa-build","dfa-search","dfa-onepass"],"dfa-build":["nfa-thompson","dfa-search"],"dfa-onepass":["nfa-thompson"],"dfa-search":[],"hybrid":["alloc","nfa-thompson"],"internal-instrument":["internal-instrument-pikevm"],"internal-instrument-pikevm":["logging","std"],"logging":["dep:log","aho-corasick?/logging","memchr?/logging"],"meta":["syntax","nfa-pikevm"],"nfa":["nfa-thompson","nfa-pikevm","nfa-backtrack"],"nfa-backtrack":["nfa-thompson"],"nfa-pikevm":["nfa-thompson"],"nfa-thompson":["alloc"],"perf":["perf-inline","perf-literal"],"perf-inline":[],"perf-literal":["perf-literal-substring","perf-literal-multisubstring"],"perf-literal-multisubstring":["dep:aho-corasick"],"perf-literal-substring":["aho-corasick?/perf-literal","dep:memchr"],"std":["regex-syntax?/std","memchr?/std","aho-corasick?/std","alloc"],"syntax":["dep:regex-syntax","alloc"],"unicode":["unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment","unicode-word-boundary","regex-syntax?/unicode"],"unicode-age":["regex-syntax?/unicode-age"],"unicode-bool":["regex-syntax?/unicode-bool"],"unicode-case":["regex-syntax?/unicode-case"],"unicode-gencat":["regex-syntax?/unicode-gencat"],"unicode-perl":["regex-syntax?/unicode-perl"],"unicode-script":["regex-syntax?/unicode-script"],"unicode-segment":["regex-syntax?/unicode-segment"],"unicode-word-boundary":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-automata-0.4.14/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs_regex"]}}},"publish":null,"authors":["The Rust Project Developers","Andrew Gallant "],"categories":["text-processing"],"keywords":["regex","dfa","automata","automaton","nfa"],"readme":"README.md","repository":"https://github.com/rust-lang/regex","homepage":"https://github.com/rust-lang/regex/tree/master/regex-automata","documentation":"https://docs.rs/regex-automata","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"regex-syntax","version":"0.8.11","id":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","license":"MIT OR Apache-2.0","license_file":null,"description":"A regular expression parser.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"regex_syntax","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-syntax-0.8.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-syntax-0.8.11/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"arbitrary":["dep:arbitrary"],"default":["std","unicode"],"std":[],"unicode":["unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment"],"unicode-age":[],"unicode-bool":[],"unicode-case":[],"unicode-gencat":[],"unicode-perl":[],"unicode-script":[],"unicode-segment":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/regex-syntax-0.8.11/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs_regex"]}}},"publish":null,"authors":["The Rust Project Developers","Andrew Gallant "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/rust-lang/regex","homepage":"https://github.com/rust-lang/regex/tree/master/regex-syntax","documentation":"https://docs.rs/regex-syntax","edition":"2021","links":null,"default_run":null,"rust_version":"1.65"},{"name":"rust_decimal","version":"1.42.1","id":"registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.42.1","license":"MIT","license_file":null,"description":"Decimal number implementation written in pure Rust suitable for financial and fixed-precision calculations.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"arrayvec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"borsh","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive","unstable__schema"],"target":null,"registry":null},{"name":"bytemuck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.17.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bytemuck_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.7.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"diesel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"ndarray","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.15.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"num-traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["i128"],"target":null,"registry":null},{"name":"postgres-types","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"proptest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":null,"rename":"rand-0_9","optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rkyv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.46","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["size_32","std"],"target":null,"registry":null},{"name":"rocket","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0-rc.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rust_decimal_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tokio-postgres","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zmij","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"csv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"version-sync","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["html_root_url_updated","markdown_deps_updated"],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"diesel","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["mysql","postgres"],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"postgres","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.19","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["getrandom"],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":"rand-0_9","optional":false,"uses_default_features":false,"features":["thread_rng"],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"rkyv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.13","kind":"dev","rename":"rkyv-0_8","optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["macros","rt-multi-thread","test-util"],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"tokio-postgres","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"rust_decimal","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"rkyv-remote","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/examples/rkyv-remote.rs","edition":"2021","required-features":["macros"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"decimal_tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/tests/decimal_tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/tests/macros.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"version-numbers","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/tests/version-numbers.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"wasm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/tests/wasm.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"comparison","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/benches/comparison.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"lib_benches","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/benches/lib_benches.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"align16":[],"borsh":["dep:borsh","std"],"bytemuck":["c-repr","dep:bytemuck","dep:bytemuck_derive"],"c-repr":[],"db-diesel-mysql":["diesel/mysql_backend","std"],"db-diesel-postgres":["diesel/postgres_backend","std"],"db-diesel2-mysql":["db-diesel-mysql"],"db-diesel2-postgres":["db-diesel-postgres"],"db-postgres":["dep:bytes","dep:postgres-types","std"],"db-tokio-postgres":["dep:bytes","dep:postgres-types","std"],"default":["serde","std"],"diesel":["dep:diesel"],"legacy-ops":[],"macros":["dep:rust_decimal_macros"],"maths":[],"maths-nopanic":["maths"],"ndarray":["dep:ndarray"],"proptest":["dep:proptest"],"rand":["dep:rand"],"rand-0_9":["dep:rand-0_9"],"rkyv":["dep:rkyv"],"rkyv-safe":["rkyv/validation"],"rocket-traits":["dep:rocket","std"],"rust-fuzz":["dep:arbitrary"],"serde":["dep:serde","wasm-bindgen?/serde"],"serde-arbitrary-precision":["serde-with-arbitrary-precision"],"serde-bincode":["serde-str"],"serde-float":["serde-with-float"],"serde-str":["serde-with-str"],"serde-with-arbitrary-precision":["serde","serde_json/arbitrary_precision","serde_json/std","zmij"],"serde-with-float":["serde"],"serde-with-str":["serde"],"serde_json":["dep:serde_json"],"std":["arrayvec/std","wasm-bindgen?/std","borsh?/std","bytes?/std","rand?/std","rkyv?/std","serde?/std","serde_json?/std"],"tokio-pg":["db-tokio-postgres"],"tokio-postgres":["dep:tokio-postgres"],"wasm":["dep:wasm-bindgen"],"zmij":["dep:zmij"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.42.1/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"],"targets":["x86_64-unknown-linux-gnu","wasm32-unknown-unknown"]}}},"publish":null,"authors":["Paul Mason "],"categories":["science","mathematics","data-structures"],"keywords":["decimal","financial","fixed","precision","number"],"readme":"README.md","repository":"https://github.com/paupino/rust-decimal","homepage":null,"documentation":"https://docs.rs/rust_decimal/","edition":"2021","links":null,"default_run":null,"rust_version":"1.67.1"},{"name":"rustix","version":"1.1.4","id":"registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.4","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"Safe Rust bindings to POSIX/Unix/Linux/Winsock-like syscalls","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.4.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.171","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.10","kind":"dev","rename":"libc_errno","optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"memoffset","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serial_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"static_assertions","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"linux-raw-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["general","ioctl","no_std"],"target":"cfg(all(any(target_os = \"linux\", target_os = \"android\"), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))","registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(criterion, not(any(target_os = \"emscripten\", target_os = \"wasi\"))))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.182","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))","registry":null},{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.10","kind":null,"rename":"libc_errno","optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))","registry":null},{"name":"linux-raw-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["auxvec","general","errno","ioctl","no_std","elf"],"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.182","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))","registry":null},{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.10","kind":null,"rename":"libc_errno","optional":false,"uses_default_features":false,"features":[],"target":"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))","registry":null},{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.10","kind":null,"rename":"libc_errno","optional":false,"uses_default_features":false,"features":[],"target":"cfg(windows)","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.52, <0.62","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_Networking_WinSock"],"target":"cfg(windows)","registry":null},{"name":"once_cell","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.20.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"rustix","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.4/benches/mod.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.4/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"all-apis":["event","fs","io_uring","mm","mount","net","param","pipe","process","pty","rand","runtime","shm","stdio","system","termios","thread","time"],"alloc":[],"core":["dep:core"],"default":["std"],"event":[],"fs":[],"io_uring":["event","fs","net","thread","linux-raw-sys/io_uring"],"libc":["dep:libc"],"libc_errno":["dep:libc_errno"],"linux_4_11":[],"linux_5_1":["linux_4_11"],"linux_5_11":["linux_5_1"],"linux_latest":["linux_5_11"],"mm":[],"mount":[],"net":["linux-raw-sys/net","linux-raw-sys/netlink","linux-raw-sys/if_ether","linux-raw-sys/xdp"],"param":[],"pipe":[],"process":["linux-raw-sys/prctl"],"pty":["fs"],"rand":[],"runtime":["linux-raw-sys/prctl"],"rustc-dep-of-std":["core","rustc-std-workspace-alloc","linux-raw-sys/rustc-dep-of-std","bitflags/rustc-dep-of-std"],"rustc-std-workspace-alloc":["dep:rustc-std-workspace-alloc"],"shm":["fs"],"std":["bitflags/std","alloc","libc?/std","libc_errno?/std"],"stdio":[],"system":["linux-raw-sys/system"],"termios":[],"thread":["linux-raw-sys/prctl"],"time":[],"try_close":[],"use-explicitly-provided-auxv":[],"use-libc":["libc_errno","libc"],"use-libc-auxv":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-1.1.4/Cargo.toml","metadata":{"docs":{"rs":{"features":["all-apis"],"targets":["x86_64-unknown-linux-gnu","i686-unknown-linux-gnu","x86_64-apple-darwin","x86_64-pc-windows-msvc","x86_64-unknown-freebsd","x86_64-unknown-openbsd","x86_64-unknown-netbsd","x86_64-unknown-dragonfly","x86_64-unknown-illumos","x86_64-unknown-redox","x86_64-unknown-haiku","wasm32-unknown-emscripten","wasm32-wasip1"]}}},"publish":null,"authors":["Dan Gohman ","Jakub Konka "],"categories":["os::unix-apis","date-and-time","filesystem","network-programming"],"keywords":["api","file","network","safe","syscall"],"readme":"README.md","repository":"https://github.com/bytecodealliance/rustix","homepage":null,"documentation":"https://docs.rs/rustix","edition":"2021","links":null,"default_run":null,"rust_version":"1.63","hints":{"mostly-unused":true}},{"name":"rustversion","version":"1.0.22","id":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","license":"MIT OR Apache-2.0","license_file":null,"description":"Conditional compilation according to rustc compiler version","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.49","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["diff"],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"rustversion","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/tests/compiletest.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_const","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/tests/test_const.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_eval","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/tests/test_eval.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/tests/test_parse.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/build/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustversion-1.0.22/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::build-utils","no-std","no-std::no-alloc"],"keywords":[],"readme":"README.md","repository":"https://github.com/dtolnay/rustversion","homepage":null,"documentation":"https://docs.rs/rustversion","edition":"2018","links":null,"default_run":null,"rust_version":"1.31"},{"name":"ryu","version":"1.0.23","id":"registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.23","license":"Apache-2.0 OR BSL-1.0","license_file":null,"description":"Fast floating point to string conversion","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"no-panic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num_cpus","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_xorshift","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"ryu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"upstream_benchmark","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/examples/upstream_benchmark.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"common_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/common_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"d2s_intrinsics_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/d2s_intrinsics_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"d2s_table_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/d2s_table_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"d2s_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/d2s_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"exhaustive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/exhaustive.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"f2s_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/f2s_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"s2d_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/s2d_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"s2f_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/tests/s2f_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"no-panic":["dep:no-panic"],"small":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.23/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["value-formatting","no-std","no-std::no-alloc"],"keywords":["float"],"readme":"README.md","repository":"https://github.com/dtolnay/ryu","homepage":null,"documentation":"https://docs.rs/ryu","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"same-file","version":"1.0.6","id":"registry+https://github.com/rust-lang/crates.io-index#same-file@1.0.6","license":"Unlicense/MIT","license_file":null,"description":"A simple crate for determining whether two file paths point to the same file.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"winapi-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"same_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/same-file-1.0.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"is_same_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/same-file-1.0.6/examples/is_same_file.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"is_stderr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/same-file-1.0.6/examples/is_stderr.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/same-file-1.0.6/Cargo.toml","metadata":null,"publish":null,"authors":["Andrew Gallant "],"categories":[],"keywords":["same","file","equal","inode"],"readme":"README.md","repository":"https://github.com/BurntSushi/same-file","homepage":"https://github.com/BurntSushi/same-file","documentation":"https://docs.rs/same-file","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"scopeguard","version":"1.2.0","id":"registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A RAII scope guard that will run a given closure when it goes out of scope,\neven if the code between panics (assuming unwinding panic).\n\nDefines the macros `defer!`, `defer_on_unwind!`, `defer_on_success!` as\nshorthands for guards with one of the implemented strategies.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"scopeguard","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scopeguard-1.2.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"readme","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scopeguard-1.2.0/examples/readme.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{"default":["use_std"],"use_std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scopeguard-1.2.0/Cargo.toml","metadata":{"release":{"no-dev-version":true}},"publish":null,"authors":["bluss"],"categories":["rust-patterns","no-std"],"keywords":["scope-guard","defer","panic","unwind"],"readme":"README.md","repository":"https://github.com/bluss/scopeguard","homepage":null,"documentation":"https://docs.rs/scopeguard/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"scratch","version":"1.0.9","id":"registry+https://github.com/rust-lang/crates.io-index#scratch@1.0.9","license":"MIT OR Apache-2.0","license_file":null,"description":"Compile-time temporary directory shared by multiple crates and erased by `cargo clean`","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"scratch","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scratch-1.0.9/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scratch-1.0.9/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scratch-1.0.9/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::build-utils","filesystem"],"keywords":[],"readme":"README.md","repository":"https://github.com/dtolnay/scratch","homepage":null,"documentation":"https://docs.rs/scratch","edition":"2015","links":null,"default_run":null,"rust_version":"1.0"},{"name":"serde","version":"1.0.228","id":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","license":"MIT OR Apache-2.0","license_file":null,"description":"A generic serialization/deserialization framework","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.228","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["result"],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde_core/alloc"],"default":["std"],"derive":["serde_derive"],"rc":["serde_core/rc"],"serde_derive":["dep:serde_derive"],"std":["serde_core/std"],"unstable":["serde_core/unstable"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/Cargo.toml","metadata":{"playground":{"features":["derive","rc"]},"docs":{"rs":{"features":["derive","rc","unstable"],"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["Erick Tryzelaar ","David Tolnay "],"categories":["encoding","no-std","no-std::no-alloc"],"keywords":["serde","serialization","no_std"],"readme":"crates-io.md","repository":"https://github.com/serde-rs/serde","homepage":"https://serde.rs","documentation":"https://docs.rs/serde","edition":"2021","links":null,"default_run":null,"rust_version":"1.56"},{"name":"serde_core","version":"1.0.228","id":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","license":"MIT OR Apache-2.0","license_file":null,"description":"Serde traits only, with no support for derive -- use the `serde` crate instead","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=1.0.228","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any())","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"serde_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"default":["std","result"],"rc":[],"result":[],"std":[],"unstable":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/Cargo.toml","metadata":{"playground":{"features":["rc","result"]},"docs":{"rs":{"features":["rc","result","unstable"],"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["Erick Tryzelaar ","David Tolnay "],"categories":["encoding","no-std","no-std::no-alloc"],"keywords":["serde","serialization","no_std"],"readme":"README.md","repository":"https://github.com/serde-rs/serde","homepage":"https://serde.rs","documentation":"https://docs.rs/serde_core","edition":"2021","links":null,"default_run":null,"rust_version":"1.56"},{"name":"serde_derive","version":"1.0.228","id":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","license":"MIT OR Apache-2.0","license_file":null,"description":"Macros 1.1 implementation of #[derive(Serialize, Deserialize)]","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.74","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["proc-macro"],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["proc-macro"],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.81","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["clone-impls","derive","parsing","printing","proc-macro"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"serde_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_derive-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"default":[],"deserialize_in_place":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_derive-1.0.228/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}}},"publish":null,"authors":["Erick Tryzelaar ","David Tolnay "],"categories":["no-std","no-std::no-alloc"],"keywords":["serde","serialization","no_std","derive"],"readme":"crates-io.md","repository":"https://github.com/serde-rs/serde","homepage":"https://serde.rs","documentation":"https://serde.rs/derive.html","edition":"2021","links":null,"default_run":null,"rust_version":"1.61"},{"name":"serde_json","version":"1.0.150","id":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","license":"MIT OR Apache-2.0","license_file":null,"description":"A JSON serialization file format","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"indexmap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memchr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zmij","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"automod","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.11","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"indoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ref-cast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.18","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.194","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.166","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_stacker","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.108","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["diff"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any())","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"serde_json","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compiletest","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/compiletest.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"debug","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/debug.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"lexical","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/lexical.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"map","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/map.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"regression","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/regression.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde_core/alloc"],"arbitrary_precision":[],"default":["std"],"float_roundtrip":[],"indexmap":["dep:indexmap"],"preserve_order":["indexmap","std"],"raw_value":[],"std":["memchr/std","serde_core/std"],"unbounded_depth":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.150/Cargo.toml","metadata":{"docs":{"rs":{"features":["preserve_order","raw_value","unbounded_depth"],"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}},"playground":{"features":["float_roundtrip","raw_value","unbounded_depth"]}},"publish":null,"authors":["Erick Tryzelaar ","David Tolnay "],"categories":["encoding","parser-implementations","no-std"],"keywords":["json","serde","serialization"],"readme":"README.md","repository":"https://github.com/serde-rs/json","homepage":null,"documentation":"https://docs.rs/serde_json","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"serde_path_to_error","version":"0.1.20","id":"registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.20","license":"MIT OR Apache-2.0","license_file":null,"description":"Path to the element that failed to deserialize","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.100","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(any())","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"serde_path_to_error","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_path_to_error-0.1.20/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"deserialize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_path_to_error-0.1.20/tests/deserialize.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serialize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_path_to_error-0.1.20/tests/serialize.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_path_to_error-0.1.20/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["encoding","no-std"],"keywords":["serde","serialization"],"readme":"README.md","repository":"https://github.com/dtolnay/path-to-error","homepage":null,"documentation":"https://docs.rs/serde_path_to_error","edition":"2021","links":null,"default_run":null,"rust_version":"1.61"},{"name":"sha2","version":"0.10.9","id":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","license":"MIT OR Apache-2.0","license_file":null,"description":"Pure Rust implementation of the SHA-2 hash function family\nincluding SHA-224, SHA-256, SHA-384, and SHA-512.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"digest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.7","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"digest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["dev"],"target":null,"registry":null},{"name":"hex-literal","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cpufeatures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))","registry":null},{"name":"sha2-asm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"sha2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sha2-0.10.9/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sha2-0.10.9/tests/mod.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"mod","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sha2-0.10.9/benches/mod.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{"asm":["sha2-asm"],"asm-aarch64":["asm"],"compress":[],"default":["std"],"force-soft":[],"force-soft-compact":[],"loongarch64_asm":[],"oid":["digest/oid"],"sha2-asm":["dep:sha2-asm"],"std":["digest/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sha2-0.10.9/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}}},"publish":null,"authors":["RustCrypto Developers"],"categories":["cryptography","no-std"],"keywords":["crypto","sha2","hash","digest"],"readme":"README.md","repository":"https://github.com/RustCrypto/hashes","homepage":null,"documentation":"https://docs.rs/sha2","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"shlex","version":"2.0.1","id":"registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Split a string into shell words, like Python's shlex.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"shlex","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/shlex-2.0.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"default":["std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/shlex-2.0.1/Cargo.toml","metadata":null,"publish":null,"authors":["comex ","Fenhl ","Adrian Taylor ","Alex Touchet ","Daniel Parks ","Garrett Berg "],"categories":["command-line-interface","parser-implementations"],"keywords":[],"readme":"README.md","repository":"https://github.com/comex/rust-shlex","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.46.0"},{"name":"signal-hook-registry","version":"1.4.8","id":"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.8","license":"MIT OR Apache-2.0","license_file":null,"description":"Backend crate for signal-hook","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"errno","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.2, <0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"signal-hook","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"signal_hook_registry","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/signal-hook-registry-1.4.8/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unregister_signal","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/signal-hook-registry-1.4.8/tests/unregister_signal.rs","edition":"2015","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/signal-hook-registry-1.4.8/Cargo.toml","metadata":null,"publish":null,"authors":["Michal 'vorner' Vaner ","Masaki Hara "],"categories":[],"keywords":["signal","unix","daemon"],"readme":"README.md","repository":"https://github.com/vorner/signal-hook","homepage":null,"documentation":"https://docs.rs/signal-hook-registry","edition":"2015","links":null,"default_run":null,"rust_version":"1.26"},{"name":"simd-adler32","version":"0.3.10","id":"registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.10","license":"MIT","license_file":null,"description":"A SIMD-accelerated Adler-32 hash algorithm implementation.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"adler2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"adler32","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"simd_adler32","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/simd-adler32-0.3.10/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"const-generics":[],"default":["std","const-generics"],"nightly":[],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/simd-adler32-0.3.10/Cargo.toml","metadata":null,"publish":null,"authors":["Marvin Countryman "],"categories":["algorithms","no-std"],"keywords":["simd","avx2","ssse3","adler","adler32"],"readme":"README.md","repository":"https://github.com/mcountryman/simd-adler32","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"siphasher","version":"1.0.3","id":"registry+https://github.com/rust-lang/crates.io-index#siphasher@1.0.3","license":"MIT/Apache-2.0","license_file":null,"description":"SipHash-2-4, SipHash-1-3 and 128-bit variants in pure Rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"siphasher","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/siphasher-1.0.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"default":["std"],"serde":["dep:serde"],"serde_json":["dep:serde_json"],"serde_no_std":["serde/alloc"],"serde_std":["std","serde/std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/siphasher-1.0.3/Cargo.toml","metadata":null,"publish":null,"authors":["Frank Denis "],"categories":["algorithms","cryptography"],"keywords":["crypto","hash","siphash"],"readme":"README.md","repository":"https://github.com/jedisct1/rust-siphash","homepage":"https://docs.rs/siphasher","documentation":"https://docs.rs/siphasher","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"slab","version":"0.4.12","id":"registry+https://github.com/rust-lang/crates.io-index#slab@0.4.12","license":"MIT","license_file":null,"description":"Pre-allocated storage for a uniform data type","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.95","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"slab","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/slab-0.4.12/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/slab-0.4.12/tests/serde.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"slab","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/slab-0.4.12/tests/slab.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"serde":["dep:serde"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/slab-0.4.12/Cargo.toml","metadata":null,"publish":null,"authors":["Carl Lerche "],"categories":["memory-management","data-structures","no-std"],"keywords":["slab","allocator","no_std"],"readme":"README.md","repository":"https://github.com/tokio-rs/slab","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.51"},{"name":"smallvec","version":"1.15.2","id":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","license":"MIT OR Apache-2.0","license_file":null,"description":"'Small vector' optimization: store up to a small number of items on the stack","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"malloc_size_of","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unty","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.0.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.1","kind":"dev","rename":"bincode1","optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"debugger_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"debugger_test_parser","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"smallvec","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/smallvec-1.15.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"arbitrary":["dep:arbitrary"],"bincode":["dep:bincode"],"const_generics":[],"const_new":["const_generics"],"debugger_visualizer":[],"drain_filter":[],"drain_keep_rest":["drain_filter"],"impl_bincode":["bincode","unty"],"malloc_size_of":["dep:malloc_size_of"],"may_dangle":[],"serde":["dep:serde"],"specialization":[],"union":[],"unty":["dep:unty"],"write":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/smallvec-1.15.2/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--generate-link-to-definition"]}}},"publish":null,"authors":["The Servo Project Developers"],"categories":["data-structures"],"keywords":["small","vec","vector","stack","no_std"],"readme":"README.md","repository":"https://github.com/servo/rust-smallvec","homepage":null,"documentation":"https://docs.rs/smallvec/","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"socket2","version":"0.6.5","id":"registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.5","license":"MIT OR Apache-2.0","license_file":null,"description":"Utilities for handling networking sockets with a maximal amount of configuration\npossible intended.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.172","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(unix, target_os = \"wasi\"))","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.60, <0.62","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_Networking_WinSock","Win32_System_IO","Win32_System_Threading","Win32_System_WindowsProgramming"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"socket2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/socket2-0.6.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"all":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/socket2-0.6.5/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"default-target":"x86_64-unknown-linux-gnu","targets":["aarch64-apple-ios","aarch64-linux-android","armv7-linux-androideabi","i686-linux-android","x86_64-apple-darwin","x86_64-pc-solaris","x86_64-pc-windows-msvc","x86_64-unknown-freebsd","x86_64-unknown-fuchsia","x86_64-unknown-illumos","x86_64-unknown-linux-gnu","x86_64-unknown-linux-musl","x86_64-unknown-netbsd","x86_64-unknown-redox"]}},"playground":{"features":["all"]},"cargo_check_external_types":{"allowed_external_types":["libc::socklen_t","libc::*::socklen_t","libc::sa_family_t","libc::*::sa_family_t","windows_sys::Win32::Networking::WinSock::socklen_t","windows_sys::Win32::Networking::WinSock::ADDRESS_FAMILY"]}},"publish":null,"authors":["Alex Crichton ","Thomas de Zeeuw "],"categories":["api-bindings","network-programming"],"keywords":["io","socket","network"],"readme":"README.md","repository":"https://github.com/rust-lang/socket2","homepage":"https://github.com/rust-lang/socket2","documentation":"https://docs.rs/socket2","edition":"2021","links":null,"default_run":null,"rust_version":"1.70"},{"name":"stable_deref_trait","version":"1.2.1","id":"registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1","license":"MIT OR Apache-2.0","license_file":null,"description":"An unsafe marker trait for types like Box and Rc that dereference to a stable address even when moved, and hence can be used with libraries such as owning_ref and rental.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"stable_deref_trait","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/stable_deref_trait-1.2.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["std"],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/stable_deref_trait-1.2.1/Cargo.toml","metadata":null,"publish":null,"authors":["Robert Grosse "],"categories":["memory-management","no-std"],"keywords":[],"readme":"README.md","repository":"https://github.com/storyyeller/stable_deref_trait","homepage":null,"documentation":"https://docs.rs/stable_deref_trait/1.2.1/stable_deref_trait","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"streaming-iterator","version":"0.1.9","id":"registry+https://github.com/rust-lang/crates.io-index#streaming-iterator@0.1.9","license":"MIT OR Apache-2.0","license_file":null,"description":"Streaming iterators","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"streaming_iterator","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/streaming-iterator-0.1.9/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"std":["alloc"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/streaming-iterator-0.1.9/Cargo.toml","metadata":{"docs":{"rs":{"features":["std"]}}},"publish":null,"authors":["Steven Fackler "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/sfackler/streaming-iterator","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.56"},{"name":"string_cache","version":"0.9.0","id":"registry+https://github.com/rust-lang/crates.io-index#string_cache@0.9.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A string interning library for Rust, developed as part of the Servo project.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"malloc_size_of","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"new_debug_unreachable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"parking_lot","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"precomputed-hash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"string_cache","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.9.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"simple","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.9.0/examples/simple.rs","edition":"2018","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"small-stack","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.9.0/tests/small-stack.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"default":["serde_support"],"malloc_size_of":["dep:malloc_size_of"],"serde":["dep:serde"],"serde_support":["serde"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.9.0/Cargo.toml","metadata":null,"publish":null,"authors":["The Servo Project Developers"],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/servo/string-cache","homepage":null,"documentation":"https://docs.rs/string_cache","edition":"2018","links":null,"default_run":null,"rust_version":"1.70.0"},{"name":"string_cache_codegen","version":"0.6.1","id":"registry+https://github.com/rust-lang/crates.io-index#string_cache_codegen@0.6.1","license":"MIT OR Apache-2.0","license_file":null,"description":"A codegen library for string-cache, developed as part of the Servo project.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"phf_generator","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"phf_shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"string_cache_codegen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache_codegen-0.6.1/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache_codegen-0.6.1/Cargo.toml","metadata":null,"publish":null,"authors":["The Servo Project Developers"],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/servo/string-cache","homepage":null,"documentation":"https://docs.rs/string_cache_codegen/","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"syn","version":"2.0.117","id":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","license":"MIT OR Apache-2.0","license_file":null,"description":"Parser for Rust source code","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.91","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unicode-ident","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"automod","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ref-cast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn-test-suite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"termcolor","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"reqwest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["blocking"],"target":"cfg(not(miri))","registry":null},{"name":"tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.16","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"syn","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"regression","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/regression.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_asyncness","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_asyncness.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_attribute","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_attribute.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_derive_input","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_derive_input.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_expr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_expr.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_generics","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_generics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_grouping","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_grouping.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_ident","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_ident.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_item","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_item.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_lit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_lit.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_meta","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_meta.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_parse_buffer.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_quote","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_parse_quote.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_parse_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_pat","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_pat.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_path","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_path.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_precedence","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_precedence.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_punctuated","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_punctuated.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_receiver","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_receiver.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_round_trip","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_round_trip.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_shebang","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_shebang.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_size","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_size.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_stmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_stmt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_token_trees","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_token_trees.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_ty","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_ty.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_unparenthesize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_unparenthesize.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_visibility","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/test_visibility.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"zzz_stable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/tests/zzz_stable.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/benches/file.rs","edition":"2021","required-features":["full","parsing"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"rust","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/benches/rust.rs","edition":"2021","required-features":["full","parsing"],"doc":false,"doctest":false,"test":false}],"features":{"clone-impls":[],"default":["derive","parsing","printing","clone-impls","proc-macro"],"derive":[],"extra-traits":[],"fold":[],"full":[],"parsing":[],"printing":["dep:quote"],"proc-macro":["proc-macro2/proc-macro","quote?/proc-macro"],"test":["syn-test-suite/all-features"],"visit":[],"visit-mut":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.117/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extend-css=src/gen/token.css","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}},"playground":{"features":["full","visit","visit-mut","fold","extra-traits"]}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::procedural-macro-helpers","parser-implementations"],"keywords":["macros","syn"],"readme":"README.md","repository":"https://github.com/dtolnay/syn","homepage":null,"documentation":"https://docs.rs/syn","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"syn","version":"3.0.3","id":"registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3","license":"MIT OR Apache-2.0","license_file":null,"description":"Parser for Rust source code","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.91","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.35","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unicode-ident","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"automod","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ref-cast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn-test-suite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"termcolor","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"rayon","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"reqwest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["blocking"],"target":"cfg(not(miri))","registry":null},{"name":"tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.16","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null},{"name":"walkdir","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"syn","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"regression","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/regression.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_attribute","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_attribute.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_derive_input","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_derive_input.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_expr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_expr.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_generics","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_generics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_grouping","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_grouping.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_ident","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_ident.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_item","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_item.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_lit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_lit.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_meta","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_meta.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_parse_buffer.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_quote","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_parse_quote.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_parse_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_parse_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_pat","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_pat.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_path","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_path.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_precedence","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_precedence.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_punctuated","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_punctuated.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_receiver","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_receiver.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_round_trip","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_round_trip.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_shebang","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_shebang.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_size","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_size.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_stmt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_stmt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_token_trees","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_token_trees.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_ty","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_ty.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_unparenthesize","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_unparenthesize.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_visibility","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/test_visibility.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"zzz_stable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/tests/zzz_stable.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/benches/file.rs","edition":"2021","required-features":["full","parsing"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"rust","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/benches/rust.rs","edition":"2021","required-features":["full","parsing"],"doc":false,"doctest":false,"test":false}],"features":{"clone-impls":[],"default":["derive","parsing","printing","clone-impls","proc-macro"],"derive":[],"extra-traits":[],"fold":[],"full":[],"parsing":[],"printing":["dep:quote"],"proc-macro":["proc-macro2/proc-macro","quote?/proc-macro"],"test":["syn-test-suite/all-features"],"visit":[],"visit-mut":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-3.0.3/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extend-css=src/gen/token.css","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org","--extern-html-root-url=proc_macro=https://doc.rust-lang.org"]}},"playground":{"features":["full","visit","visit-mut","fold","extra-traits"]}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::procedural-macro-helpers","parser-implementations"],"keywords":["macros","syn"],"readme":"README.md","repository":"https://github.com/dtolnay/syn","homepage":null,"documentation":"https://docs.rs/syn","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"sync_wrapper","version":"1.0.2","id":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","license":"Apache-2.0","license_file":null,"description":"A tool for enlisting the compiler's help in proving the absence of concurrency","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"sync_wrapper","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sync_wrapper-1.0.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"futures":["futures-core"],"futures-core":["dep:futures-core"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sync_wrapper-1.0.2/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["Actyx AG "],"categories":["concurrency"],"keywords":["concurrency"],"readme":"README.md","repository":"https://github.com/Actyx/sync_wrapper","homepage":"https://docs.rs/sync_wrapper","documentation":"https://docs.rs/sync_wrapper","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"synstructure","version":"0.13.2","id":"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2","license":"MIT","license_file":null,"description":"Helper methods and macros for custom derives","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.60","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["derive","parsing","printing","clone-impls","visit","extra-traits"],"target":null,"registry":null},{"name":"synstructure_test_traits","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"synstructure","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/synstructure-0.13.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"default":["proc-macro"],"proc-macro":["proc-macro2/proc-macro","syn/proc-macro","quote/proc-macro"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/synstructure-0.13.2/Cargo.toml","metadata":null,"publish":null,"authors":["Nika Layzell "],"categories":[],"keywords":["syn","macros","derive","expand_substructure","enum"],"readme":"README.md","repository":"https://github.com/mystor/synstructure","homepage":null,"documentation":"https://docs.rs/synstructure","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"tar","version":"0.4.46","id":"registry+https://github.com/rust-lang/crates.io-index#tar@0.4.46","license":"MIT OR Apache-2.0","license_file":null,"description":"A Rust implementation of a TAR file reader and writer. This library does not\ncurrently handle compression, but it is abstract over all I/O readers and\nwriters. Additionally, great lengths are taken to ensure that the entire\ncontents are never required to be entirely resident in memory all at once.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"filetime","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.8","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"astral-tokio-tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","rt"],"target":null,"registry":null},{"name":"tokio-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"xattr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tar","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"extract_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/examples/extract_file.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"list","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/examples/list.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"raw_list","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/examples/raw_list.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/examples/write.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"all","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/tests/all.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"entry","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/tests/entry.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["xattr"],"xattr":["dep:xattr"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.46/Cargo.toml","metadata":null,"publish":null,"authors":["Alex Crichton "],"categories":[],"keywords":["tar","tarfile","encoding"],"readme":"README.md","repository":"https://github.com/composefs/tar-rs","homepage":"https://github.com/composefs/tar-rs","documentation":"https://docs.rs/tar","edition":"2021","links":null,"default_run":null,"rust_version":"1.63"},{"name":"tendril","version":"0.5.1","id":"registry+https://github.com/rust-lang/crates.io-index#tendril@0.5.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Compact buffer/string type for zero-copy parsing","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"encoding_rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.12","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"new_debug_unreachable","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tendril","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"fuzz","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/examples/fuzz.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"futf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/benches/futf.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"tendril","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/benches/tendril.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"encoding_rs":["dep:encoding_rs"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tendril-0.5.1/Cargo.toml","metadata":null,"publish":null,"authors":["Keegan McAllister ","Simon Sapin ","Chris Morgan "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/servo/html5ever","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"termcolor","version":"1.4.1","id":"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1","license":"Unlicense OR MIT","license_file":null,"description":"A simple cross platform library for writing colored text to a terminal.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"winapi-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"termcolor","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/termcolor-1.4.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/termcolor-1.4.1/Cargo.toml","metadata":null,"publish":null,"authors":["Andrew Gallant "],"categories":[],"keywords":["windows","win","color","ansi","console"],"readme":"README.md","repository":"https://github.com/BurntSushi/termcolor","homepage":"https://github.com/BurntSushi/termcolor","documentation":"https://docs.rs/termcolor","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"time","version":"0.3.36","id":"registry+https://github.com/rust-lang/crates.io-index#time@0.3.36","license":"MIT OR Apache-2.0","license_file":null,"description":"Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std].","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"deranged","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.9","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["powerfmt"],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num-conv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"powerfmt","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.184","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"time-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.1.2","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"time-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.18","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num-conv","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck_macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.4","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rstest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.18.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rstest_reuse","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.184","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.68","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.126","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"time-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.18","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.68","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(__ui_tests)","registry":null},{"name":"js-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.58","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(target_family = \"wasm\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))","registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.1","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(bench)","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.98","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_family = \"unix\")","registry":null},{"name":"num_threads","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_family = \"unix\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"time","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.36/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.36/../tests/main.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benchmarks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.36/../benchmarks/main.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde?/alloc"],"default":["std"],"formatting":["dep:itoa","std","time-macros?/formatting"],"large-dates":["time-macros?/large-dates"],"local-offset":["std","dep:libc","dep:num_threads"],"macros":["dep:time-macros"],"parsing":["time-macros?/parsing"],"quickcheck":["dep:quickcheck","alloc","deranged/quickcheck"],"rand":["dep:rand","deranged/rand"],"serde":["dep:serde","time-macros?/serde","deranged/serde"],"serde-human-readable":["serde","formatting","parsing"],"serde-well-known":["serde","formatting","parsing"],"std":["alloc","deranged/std"],"wasm-bindgen":["dep:js-sys"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.36/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","__time_03_docs","--generate-link-to-definition"],"targets":["x86_64-unknown-linux-gnu"]}}},"publish":null,"authors":["Jacob Pratt ","Time contributors"],"categories":["date-and-time","no-std","parser-implementations","value-formatting"],"keywords":["date","time","calendar","duration"],"readme":"README.md","repository":"https://github.com/time-rs/time","homepage":"https://time-rs.github.io","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"time-core","version":"0.1.2","id":"registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2","license":"MIT OR Apache-2.0","license_file":null,"description":"This crate is an implementation detail and should not be relied upon directly.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"time_core","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-core-0.1.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/time-core-0.1.2/Cargo.toml","metadata":{"docs":{"rs":{"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["Jacob Pratt ","Time contributors"],"categories":["date-and-time"],"keywords":["date","time","calendar","duration"],"readme":null,"repository":"https://github.com/time-rs/time","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.67.0"},{"name":"tinystr","version":"0.8.3","id":"registry+https://github.com/rust-lang/crates.io-index#tinystr@0.8.3","license":"Unicode-3.0","license_file":null,"description":"A small ASCII-only bounded length string representation.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["use-std"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["alloc"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tinystr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/tests/serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"construct","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/benches/construct.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"overview","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/benches/overview.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/benches/read.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/benches/serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde_core?/alloc","zerovec?/alloc"],"databake":["dep:databake"],"default":["alloc"],"serde":["dep:serde_core"],"std":[],"zerovec":["dep:zerovec"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tinystr-0.8.3/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["data-structures"],"keywords":["small-string","ascii","string","str"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"tokio","version":"1.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","license":"MIT","license_file":null,"description":"An event-driven, non-blocking I/O platform for writing asynchronous I/O\nbacked applications.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bytes","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"parking_lot","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.12.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.11","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-macros","source":"registry+https://github.com/rust-lang/crates.io-index","req":"~2.7.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"async-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["async-await"],"target":null,"registry":null},{"name":"futures-concurrency","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^7.6.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.31","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"mockall","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["rt"],"target":null,"registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_family = \"wasm\", not(target_os = \"wasi\")))","registry":null},{"name":"tracing-mock","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.1.0-beta.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_has_atomic = \"64\"))","registry":null},{"name":"backtrace","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.58","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"io-uring","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.11","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.168","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"mio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["os-poll","os-ext"],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"slab","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"backtrace","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.58","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(tokio_unstable, target_os = \"linux\"))","registry":null},{"name":"socket2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["all"],"target":"cfg(any(not(target_family = \"wasm\"), all(target_os = \"wasi\", not(target_env = \"p1\"))))","registry":null},{"name":"loom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["futures","checkpoint"],"target":"cfg(loom)","registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(all(target_family = \"wasm\", target_os = \"unknown\")))","registry":null},{"name":"proptest","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_family = \"wasm\"))","registry":null},{"name":"socket2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_family = \"wasm\"))","registry":null},{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_family = \"wasm\"))","registry":null},{"name":"mio-aio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["tokio"],"target":"cfg(target_os = \"freebsd\")","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.168","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(target_os = \"wasi\")","registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.29","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":"cfg(tokio_unstable)","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.168","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"signal-hook-registry","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.168","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(unix)","registry":null},{"name":"nix","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.31.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["aio","fs","socket"],"target":"cfg(unix)","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.61","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null},{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.61","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_Security_Authorization"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tokio","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"_require_full","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/_require_full.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"async_send_sync","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/async_send_sync.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"buffered","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/buffered.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"coop_budget","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/coop_budget.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dump","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/dump.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"duplex_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/duplex_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_canonicalize_dir","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_canonicalize_dir.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_copy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_copy.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_dir","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_dir.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_file.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_link","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_link.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_open_options","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_open_options.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_open_options_windows","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_open_options_windows.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_remove_dir_all","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_remove_dir_all.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_remove_file","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_remove_file.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_rename","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_rename.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_symlink_dir_windows","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_symlink_dir_windows.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_symlink_file_windows","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_symlink_file_windows.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_try_exists","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_try_exists.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_cancel_open","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_cancel_open.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_completed_then_dropped_before_repoll","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_completed_then_dropped_before_repoll.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_file_read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_file_read.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_read.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_rename","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_rename.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_runtime_shutdown","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_runtime_shutdown.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_statx","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_statx.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_uring_statx_fd_leak_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_uring_statx_fd_leak_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"fs_write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/fs_write.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_async_fd","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_async_fd.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_async_fd_memory_leak","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_async_fd_memory_leak.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_async_read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_async_read.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_buf_reader","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_buf_reader.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_buf_writer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_buf_writer.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_chain","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_chain.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_copy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_copy.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_copy_bidirectional","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_copy_bidirectional.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_driver","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_driver.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_driver_drop","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_driver_drop.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_fill_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_fill_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_join","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_join.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_lines","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_lines.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_mem_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_mem_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_poll_aio","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_poll_aio.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_exact","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_exact.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_line","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_line.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_to_end","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_to_end.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_to_string","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_to_string.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_read_until","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_read_until.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_repeat","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_repeat.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_sink","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_sink.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_split","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_split.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_take","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_take.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_util_empty","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_util_empty.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write_all","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write_all.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write_all_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write_all_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write_buf","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write_buf.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"io_write_int","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/io_write_int.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"join_handle_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/join_handle_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_join","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_join.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_pin","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_pin.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_rename_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_rename_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_select","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_select.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"macros_try_join","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/macros_try_join.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_bind_resource","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_bind_resource.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_lookup_host","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_lookup_host.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_named_pipe","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_named_pipe.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_quickack","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_quickack.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"net_unix_pipe","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/net_unix_pipe.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"no_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/no_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_arg0","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_arg0.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_change_of_runtime","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_change_of_runtime.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_issue_2174","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_issue_2174.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_issue_42","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_issue_42.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_issue_7144","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_issue_7144.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_kill_after_wait","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_kill_after_wait.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_kill_on_drop","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_kill_on_drop.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_raw_handle","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_raw_handle.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"process_smoke","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/process_smoke.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_basic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_basic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_common","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_common.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_common_before_park","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_common_before_park.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_handle","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_handle.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_handle_block_on","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_handle_block_on.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_local","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_local.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_metrics","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_metrics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_poll_callbacks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_poll_callbacks.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_shutdown_err","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_shutdown_err.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_spawn_blocking","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_spawn_blocking.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_threaded","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_threaded.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_time_start_paused","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_time_start_paused.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_unstable_eager_driver_handoff","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_unstable_eager_driver_handoff.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_unstable_metrics","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_unstable_metrics.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"rt_worker_index","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/rt_worker_index.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_ctrl_c","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_ctrl_c.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_drop_recv","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_drop_recv.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_drop_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_drop_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_drop_signal","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_drop_signal.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_info","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_info.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_multi_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_multi_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_no_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_no_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_notify_both","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_notify_both.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_realtime","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_realtime.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_twice","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_twice.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"signal_usr1","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/signal_usr1.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_barrier","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_barrier.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_broadcast","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_broadcast.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_broadcast_weak","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_broadcast_weak.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_errors","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_errors.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_mpsc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_mpsc.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_mpsc_weak","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_mpsc_weak.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_mutex","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_mutex.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_mutex_owned","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_mutex_owned.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_notify","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_notify.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_notify_owned","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_notify_owned.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_once_cell","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_once_cell.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_oneshot","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_oneshot.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_rwlock","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_rwlock.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_semaphore","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_semaphore.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_semaphore_owned","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_semaphore_owned.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_set_once","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_set_once.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"sync_watch","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/sync_watch.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_abort","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_abort.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_blocking","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_blocking.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_builder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_builder.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_hooks","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_hooks.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_id","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_id.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_join_set","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_join_set.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_local","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_local.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_local_set","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_local_set.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_trace_self","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_trace_self.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"task_yield_now","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/task_yield_now.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_accept","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_accept.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_connect","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_connect.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_echo","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_echo.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_into_split","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_into_split.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_into_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_into_std.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_peek","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_peek.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_shutdown","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_shutdown.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_socket","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_socket.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_split","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_split.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tcp_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tcp_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_clock","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/test_clock.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_alt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_alt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_interval","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_interval.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_panic","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_panic.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_pause","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_pause.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_rt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_rt.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_sleep","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_sleep.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_timeout","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_timeout.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"time_wasm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/time_wasm.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tracing_sync","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tracing_sync.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tracing_task","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tracing_task.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tracing_time","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/tracing_time.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"udp","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/udp.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_cred","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_cred.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_datagram","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_datagram.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_socket","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_socket.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_split","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_split.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"uds_stream","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/uds_stream.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unwindsafe","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/tests/unwindsafe.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"bytes":["dep:bytes"],"default":[],"fs":[],"full":["fs","io-util","io-std","macros","net","parking_lot","process","rt","rt-multi-thread","signal","sync","time"],"io-std":[],"io-uring":["dep:io-uring","libc","mio/os-poll","mio/os-ext","dep:slab"],"io-util":["bytes"],"libc":["dep:libc"],"macros":["tokio-macros"],"mio":["dep:mio"],"net":["libc","mio/os-poll","mio/os-ext","mio/net","socket2","windows-sys/Win32_Foundation","windows-sys/Win32_Security","windows-sys/Win32_Storage_FileSystem","windows-sys/Win32_System_Pipes","windows-sys/Win32_System_SystemServices"],"parking_lot":["dep:parking_lot"],"process":["bytes","libc","mio/os-poll","mio/os-ext","mio/net","signal-hook-registry","windows-sys/Win32_Foundation","windows-sys/Win32_System_Threading","windows-sys/Win32_System_WindowsProgramming"],"rt":[],"rt-multi-thread":["rt"],"schedule-latency":[],"signal":["libc","mio/os-poll","mio/net","mio/os-ext","signal-hook-registry","windows-sys/Win32_Foundation","windows-sys/Win32_System_Console"],"signal-hook-registry":["dep:signal-hook-registry"],"socket2":["dep:socket2"],"sync":[],"taskdump":["dep:backtrace"],"test-util":["rt","sync","time"],"time":[],"tokio-macros":["dep:tokio-macros"],"tracing":["dep:tracing"],"windows-sys":["dep:windows-sys"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs","--cfg","tokio_unstable"],"rustc-args":["--cfg","tokio_unstable"]}},"playground":{"features":["full","test-util"]},"cargo_check_external_types":{"allowed_external_types":["bytes::buf::buf_impl::Buf","bytes::buf::buf_mut::BufMut","tokio_macros::*"]}},"publish":null,"authors":["Tokio Contributors "],"categories":["asynchronous","network-programming"],"keywords":["io","async","non-blocking","futures"],"readme":"README.md","repository":"https://github.com/tokio-rs/tokio","homepage":"https://tokio.rs","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"tokio-macros","version":"2.7.2","id":"registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.7.2","license":"MIT","license_file":null,"description":"Tokio's proc macros.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.60","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["full"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["full","test-util"],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"tokio_macros","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-macros-2.7.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-macros-2.7.2/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["Tokio Contributors "],"categories":["asynchronous"],"keywords":[],"readme":"README.md","repository":"https://github.com/tokio-rs/tokio","homepage":"https://tokio.rs","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"tower","version":"0.5.3","id":"registry+https://github.com/rust-lang/crates.io-index#tower@0.5.3","license":"MIT","license_file":null,"description":"Tower is a library of modular and reusable components for building robust\nclients and servers.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"hdrhistogram","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^7.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"indexmap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"pin-project-lite","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.7","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"slab","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sync_wrapper","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.7.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-service","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"futures-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["async-await-macro"],"target":null,"registry":null},{"name":"hdrhistogram","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^7.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","sync","test-util","rt-multi-thread"],"target":null,"registry":null},{"name":"tokio-stream","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tower-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tracing","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["std"],"target":null,"registry":null},{"name":"tracing-subscriber","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["fmt","ansi"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tower","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"tower-balance","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/examples/tower-balance.rs","edition":"2018","required-features":["full"],"doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"balance","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/balance/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"buffer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/buffer/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"builder","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/builder.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"hedge","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/hedge/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"limit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/limit/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"load_shed","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/load_shed/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"ready_cache","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/ready_cache/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"retry","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/retry/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"spawn_ready","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/spawn_ready/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"steer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/steer/main.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"support","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/support.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/tests/util/main.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"balance":["discover","load","ready-cache","make","slab","util"],"buffer":["tokio/sync","tokio/rt","tokio-util","tracing","pin-project-lite"],"discover":["futures-core","pin-project-lite"],"filter":["futures-util","pin-project-lite"],"full":["balance","buffer","discover","filter","hedge","limit","load","load-shed","make","ready-cache","reconnect","retry","spawn-ready","steer","timeout","util"],"futures-core":["dep:futures-core"],"futures-util":["dep:futures-util"],"hdrhistogram":["dep:hdrhistogram"],"hedge":["util","filter","futures-util","hdrhistogram","tokio/time","tracing"],"indexmap":["dep:indexmap"],"limit":["tokio/time","tokio/sync","tokio-util","tracing","pin-project-lite"],"load":["tokio/time","tracing","pin-project-lite"],"load-shed":["pin-project-lite"],"log":["tracing/log"],"make":["pin-project-lite","tokio"],"pin-project-lite":["dep:pin-project-lite"],"ready-cache":["futures-core","futures-util","indexmap","tokio/sync","tracing","pin-project-lite"],"reconnect":["make","tracing"],"retry":["tokio/time","util"],"slab":["dep:slab"],"spawn-ready":["futures-util","tokio/sync","tokio/rt","util","tracing"],"steer":[],"sync_wrapper":["dep:sync_wrapper"],"timeout":["pin-project-lite","tokio/time"],"tokio":["dep:tokio"],"tokio-stream":[],"tokio-util":["dep:tokio-util"],"tracing":["dep:tracing"],"util":["futures-core","futures-util","pin-project-lite","sync_wrapper"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-0.5.3/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"]}},"playground":{"features":["full"]}},"publish":null,"authors":["Tower Maintainers "],"categories":["asynchronous","network-programming"],"keywords":["io","async","non-blocking","futures","service"],"readme":"README.md","repository":"https://github.com/tower-rs/tower","homepage":"https://github.com/tower-rs/tower","documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":"1.64.0"},{"name":"tower-layer","version":"0.3.3","id":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","license":"MIT","license_file":null,"description":"Decorates a `Service` to allow easy composition between `Service`s.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tower_layer","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-layer-0.3.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-layer-0.3.3/Cargo.toml","metadata":null,"publish":null,"authors":["Tower Maintainers "],"categories":["asynchronous","network-programming"],"keywords":[],"readme":"README.md","repository":"https://github.com/tower-rs/tower","homepage":"https://github.com/tower-rs/tower","documentation":"https://docs.rs/tower-layer/0.3.3","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"tower-service","version":"0.3.3","id":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","license":"MIT","license_file":null,"description":"Trait representing an asynchronous, request / response based, client or server.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.22","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"http","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tokio","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.6.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["macros","time"],"target":null,"registry":null},{"name":"tower-layer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tower_service","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-service-0.3.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-service-0.3.3/Cargo.toml","metadata":null,"publish":null,"authors":["Tower Maintainers "],"categories":["asynchronous","network-programming"],"keywords":[],"readme":"README.md","repository":"https://github.com/tower-rs/tower","homepage":"https://github.com/tower-rs/tower","documentation":"https://docs.rs/tower-service/0.3.3","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter","version":"0.25.10","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter@0.25.10","license":"MIT","license_file":null,"description":"Rust bindings to the Tree-sitter parsing library","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"regex","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.11.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["unicode"],"target":null,"registry":null},{"name":"regex-syntax","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.5","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"streaming-iterator","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasmtime-c-api-impl","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^29.0.1","kind":null,"rename":"wasmtime-c-api","optional":true,"uses_default_features":false,"features":["cranelift","gc-drc"],"target":null,"registry":null},{"name":"bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.71.1","kind":"build","rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.10","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.137","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":["preserve_order"],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-0.25.10/binding_rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-0.25.10/binding_rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"bindgen":["dep:bindgen"],"default":["std"],"std":["regex/std","regex/perf","regex-syntax/unicode"],"wasm":["std","wasmtime-c-api"],"wasmtime-c-api":["dep:wasmtime-c-api"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-0.25.10/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"rustdoc-args":["--cfg","docsrs"],"targets":["x86_64-unknown-linux-gnu","x86_64-pc-windows-gnu"]}}},"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["api-bindings","external-ffi-bindings","parsing","text-editors"],"keywords":["incremental","parsing"],"readme":"binding_rust/README.md","repository":"https://github.com/tree-sitter/tree-sitter","homepage":"https://tree-sitter.github.io/tree-sitter","documentation":null,"edition":"2021","links":"tree-sitter","default_run":null,"rust_version":"1.76"},{"name":"tree-sitter-c","version":"0.24.2","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-c@0.24.2","license":"MIT","license_file":null,"description":"C grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_c","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-c-0.24.2/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-c-0.24.2/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-c-0.24.2/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","c"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-c","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-cpp","version":"0.23.4","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-cpp@0.23.4","license":"MIT","license_file":null,"description":"C++ grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_cpp","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-cpp-0.23.4/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-cpp-0.23.4/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-cpp-0.23.4/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","cpp"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-cpp","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-css","version":"0.25.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-css@0.25.0","license":"MIT","license_file":null,"description":"CSS grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_css","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-css-0.25.0/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-css-0.25.0/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-css-0.25.0/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parser-implementations","parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","css"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-css","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-fortran","version":"0.6.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-fortran@0.6.0","license":"MIT","license_file":null,"description":"fortran grammar for the tree-sitter parsing library","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.26.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_fortran","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-fortran-0.6.0/bindings/rust/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-fortran-0.6.0/bindings/rust/build.rs","edition":"2018","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-fortran-0.6.0/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","fortran"],"readme":"README.md","repository":"https://github.com/stadelmanma/tree-sitter-fortran","homepage":null,"documentation":null,"edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-go","version":"0.25.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-go@0.25.0","license":"MIT","license_file":null,"description":"Go grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_go","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-go-0.25.0/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-go-0.25.0/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-go-0.25.0/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parser-implementations","parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","go"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-go","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-html","version":"0.23.2","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-html@0.23.2","license":"MIT","license_file":null,"description":"HTML grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_html","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-html-0.23.2/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-html-0.23.2/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-html-0.23.2/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","html"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-html","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-javascript","version":"0.25.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-javascript@0.25.0","license":"MIT","license_file":null,"description":"JavaScript grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_javascript","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-javascript-0.25.0/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-javascript-0.25.0/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-javascript-0.25.0/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parser-implementations","parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","javascript"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-javascript","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-language","version":"0.1.8","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","license":"MIT","license_file":null,"description":"The tree-sitter Language type, used by the library and by language implementations","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_language","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-language-0.1.8/src/language.rs","edition":"2024","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-language-0.1.8/build.rs","edition":"2024","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-language-0.1.8/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["api-bindings","development-tools::ffi","parsing"],"keywords":["incremental","parsing"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter","homepage":"https://tree-sitter.github.io/tree-sitter","documentation":"https://docs.rs/tree-sitter-language","edition":"2024","links":"tree-sitter-language","default_run":null,"rust_version":"1.90"},{"name":"tree-sitter-python","version":"0.25.0","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-python@0.25.0","license":"MIT","license_file":null,"description":"Python grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_python","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-python-0.25.0/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-python-0.25.0/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-python-0.25.0/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parser-implementations","parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","python"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-python","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-rust","version":"0.24.2","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-rust@0.24.2","license":"MIT","license_file":null,"description":"Rust grammar for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.25","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_rust","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-rust-0.24.2/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-rust-0.24.2/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-rust-0.24.2/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","rust"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-rust","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"tree-sitter-typescript","version":"0.23.2","id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-typescript@0.23.2","license":"MIT","license_file":null,"description":"TypeScript and TSX grammars for tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tree-sitter-language","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tree-sitter","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.24","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"cc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"tree_sitter_typescript","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-typescript-0.23.2/bindings/rust/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-typescript-0.23.2/bindings/rust/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tree-sitter-typescript-0.23.2/Cargo.toml","metadata":null,"publish":null,"authors":["Max Brunsfeld ","Amaan Qureshi "],"categories":["parsing","text-editors"],"keywords":["incremental","parsing","tree-sitter","typescript","tsx"],"readme":"README.md","repository":"https://github.com/tree-sitter/tree-sitter-typescript","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"typenum","version":"1.20.1","id":"registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Typenum is a Rust library for type-level numbers evaluated at\n compile time. It currently supports bits, unsigned integers, and signed\n integers. It also provides a type-level array of type-level numbers, but its\n implementation is incomplete.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"scale-info","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"typenum","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/typenum-1.20.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"generated","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/typenum-1.20.1/tests/generated.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"const-generics":[],"i128":[],"scale-info":["dep:scale-info"],"scale_info":["scale-info/derive"],"strict":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/typenum-1.20.1/Cargo.toml","metadata":{"docs":{"rs":{"features":["i128","const-generics"],"rustdoc-args":["--cfg","docsrs"]}},"playground":{"features":["i128","const-generics"]}},"publish":null,"authors":[],"categories":["no-std"],"keywords":[],"readme":"README.md","repository":"https://github.com/paholg/typenum","homepage":null,"documentation":"https://docs.rs/typenum","edition":"2018","links":null,"default_run":null,"rust_version":"1.41.0"},{"name":"unicase","version":"2.9.0","id":"registry+https://github.com/rust-lang/crates.io-index#unicase@2.9.0","license":"MIT OR Apache-2.0","license_file":null,"description":"A case-insensitive wrapper around strings.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"unicase","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicase-2.9.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"nightly":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicase-2.9.0/Cargo.toml","metadata":null,"publish":null,"authors":["Sean McArthur "],"categories":["internationalization","text-processing","no-std"],"keywords":["lowercase","case","case-insensitive","case-folding","no_std"],"readme":"README.md","repository":"https://github.com/seanmonstar/unicase","homepage":null,"documentation":"https://docs.rs/unicase","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"unicode-ident","version":"1.0.24","id":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","license":"(MIT OR Apache-2.0) AND Unicode-3.0","license_file":null,"description":"Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"fst","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"roaring","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ucd-trie","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"unicode-xid","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.6","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"unicode_ident","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"compare","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/tests/compare.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"static_size","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/tests/static_size.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"xid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/benches/xid.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.24/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org","--extern-html-root-url=alloc=https://doc.rust-lang.org","--extern-html-root-url=std=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["development-tools::procedural-macro-helpers","no-std","no-std::no-alloc"],"keywords":["unicode","xid"],"readme":"README.md","repository":"https://github.com/dtolnay/unicode-ident","homepage":null,"documentation":"https://docs.rs/unicode-ident","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"unicode-width","version":"0.1.14","id":"registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.14","license":"MIT OR Apache-2.0","license_file":null,"description":"Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"compiler_builtins","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-std","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"std","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"unicode_width","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-width-0.1.14/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"tests","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-width-0.1.14/tests/tests.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"benches","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-width-0.1.14/benches/benches.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"cjk":[],"compiler_builtins":["dep:compiler_builtins"],"core":["dep:core"],"default":["cjk"],"no_std":[],"rustc-dep-of-std":["std","core","compiler_builtins"],"std":["dep:std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-width-0.1.14/Cargo.toml","metadata":null,"publish":null,"authors":["kwantam ","Manish Goregaokar "],"categories":["command-line-interface","internationalization","no-std::no-alloc","text-processing"],"keywords":["text","width","unicode"],"readme":"README.md","repository":"https://github.com/unicode-rs/unicode-width","homepage":"https://github.com/unicode-rs/unicode-width","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"url","version":"2.5.8","id":"registry+https://github.com/rust-lang/crates.io-index#url@2.5.8","license":"MIT OR Apache-2.0","license_file":null,"description":"URL library for Rust, based on the WHATWG URL Standard","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"form_urlencoded","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"idna","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc","compiled_data"],"target":null,"registry":null},{"name":"percent-encoding","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.3.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bencher","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"url","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.8/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"unit","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.8/tests/unit.rs","edition":"2018","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"url_wpt","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.8/tests/wpt.rs","edition":"2018","doc":false,"doctest":false,"test":true}],"features":{"debugger_visualizer":[],"default":["std"],"expose_internals":[],"serde":["dep:serde","dep:serde_derive"],"std":["idna/std","percent-encoding/std","form_urlencoded/std","serde?/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.8/Cargo.toml","metadata":{"docs":{"rs":{"features":["serde"],"rustdoc-args":["--generate-link-to-definition"]}},"playground":{"features":["serde"]}},"publish":null,"authors":["The rust-url developers"],"categories":["parser-implementations","web-programming","encoding","no-std"],"keywords":["url","parser"],"readme":"README.md","repository":"https://github.com/servo/rust-url","homepage":null,"documentation":"https://docs.rs/url","edition":"2018","links":null,"default_run":null,"rust_version":"1.63"},{"name":"utf8_iter","version":"1.0.4","id":"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","license":"Apache-2.0 OR MIT","license_file":null,"description":"Iterator by char over potentially-invalid UTF-8 in &[u8]","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"utf8_iter","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/utf8_iter-1.0.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/utf8_iter-1.0.4/Cargo.toml","metadata":null,"publish":null,"authors":["Henri Sivonen "],"categories":["text-processing","encoding","internationalization"],"keywords":["encoding","UTF-8","unicode","iterator"],"readme":"README.md","repository":"https://github.com/hsivonen/utf8_iter","homepage":"https://docs.rs/utf8_iter/","documentation":"https://docs.rs/utf8_iter/","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"uuid","version":"1.18.1","id":"registry+https://github.com/rust-lang/crates.io-index#uuid@1.18.1","license":"Apache-2.0 OR MIT","license_file":null,"description":"A library to generate and parse UUIDs.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.1.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"atomic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"borsh","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"borsh-derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bytemuck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.18.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"md-5","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.56","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"sha1_smol","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"slog","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uuid-macro-internal","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.18.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"zerocopy","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.79","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.56","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.52","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"uuid-rng-internal","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.18.1","kind":null,"rename":"uuid-rng-internal-lib","optional":true,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["msrv"],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"wasm-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"wasm-bindgen-test","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))","registry":null},{"name":"js-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"), target_feature = \"atomics\"))","registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(not(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"))))","registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":"cfg(not(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"))))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"uuid","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.18.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"arbitrary":["dep:arbitrary"],"atomic":["dep:atomic"],"borsh":["dep:borsh","dep:borsh-derive"],"bytemuck":["dep:bytemuck"],"default":["std"],"fast-rng":["rng","dep:rand"],"js":["dep:wasm-bindgen","dep:js-sys"],"macro-diagnostics":["dep:uuid-macro-internal"],"md5":["dep:md-5"],"rng":["dep:getrandom"],"rng-getrandom":["rng","dep:getrandom","uuid-rng-internal-lib","uuid-rng-internal-lib/getrandom"],"rng-rand":["rng","dep:rand","uuid-rng-internal-lib","uuid-rng-internal-lib/rand"],"serde":["dep:serde"],"sha1":["dep:sha1_smol"],"slog":["dep:slog"],"std":["wasm-bindgen?/std","js-sys?/std"],"uuid-rng-internal-lib":["dep:uuid-rng-internal-lib"],"v1":["atomic"],"v3":["md5"],"v4":["rng"],"v5":["sha1"],"v6":["atomic"],"v7":["rng"],"v8":[],"zerocopy":["dep:zerocopy"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.18.1/Cargo.toml","metadata":{"docs":{"rs":{"rustc-args":["--cfg","uuid_unstable"],"rustdoc-args":["--cfg","uuid_unstable"],"targets":["x86_64-unknown-linux-gnu"],"features":["serde","arbitrary","slog","borsh","v1","v3","v4","v5","v6","v7","v8"]}},"playground":{"features":["serde","v1","v3","v4","v5","v6","v7","v8"]}},"publish":null,"authors":["Ashley Mannix","Dylan DPC","Hunar Roop Kahlon"],"categories":["data-structures","no-std","parser-implementations","wasm"],"keywords":["guid","unique","uuid"],"readme":"README.md","repository":"https://github.com/uuid-rs/uuid","homepage":"https://github.com/uuid-rs/uuid","documentation":"https://docs.rs/uuid","edition":"2018","links":null,"default_run":null,"rust_version":"1.63.0"},{"name":"version_check","version":"0.9.5","id":"registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5","license":"MIT/Apache-2.0","license_file":null,"description":"Tiny crate to check the version of the installed/running rustc.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"version_check","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/version_check-0.9.5/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/version_check-0.9.5/Cargo.toml","metadata":null,"publish":null,"authors":["Sergio Benitez "],"categories":[],"keywords":["version","rustc","minimum","check"],"readme":"README.md","repository":"https://github.com/SergioBenitez/version_check","homepage":null,"documentation":"https://docs.rs/version_check/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"walkdir","version":"2.5.0","id":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","license":"Unlicense/MIT","license_file":null,"description":"Recursively walk a directory.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"same-file","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"doc-comment","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"winapi-util","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"walkdir","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/walkdir-2.5.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/walkdir-2.5.0/Cargo.toml","metadata":null,"publish":null,"authors":["Andrew Gallant "],"categories":["filesystem"],"keywords":["directory","recursive","walk","iterator"],"readme":"README.md","repository":"https://github.com/BurntSushi/walkdir","homepage":"https://github.com/BurntSushi/walkdir","documentation":"https://docs.rs/walkdir/","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"wasi","version":"0.11.1+wasi-snapshot-preview1","id":"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.1+wasi-snapshot-preview1","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"Experimental WASI API bindings for Rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasi","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasi-0.11.1+wasi-snapshot-preview1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true}],"features":{"core":["dep:core"],"default":["std"],"rustc-dep-of-std":["core","rustc-std-workspace-alloc"],"rustc-std-workspace-alloc":["dep:rustc-std-workspace-alloc"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasi-0.11.1+wasi-snapshot-preview1/Cargo.toml","metadata":null,"publish":null,"authors":["The Cranelift Project Developers"],"categories":["no-std","wasm"],"keywords":["webassembly","wasm"],"readme":"README.md","repository":"https://github.com/bytecodealliance/wasi","homepage":null,"documentation":"https://docs.rs/wasi","edition":"2018","links":null,"default_run":null,"rust_version":null},{"name":"wasip2","version":"1.0.4+wasi-0.2.12","id":"registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"WASIp2 API bindings for Rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"alloc","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wit-bindgen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.57.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasip2","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["cdylib"],"name":"cli-command","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/cli-command.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["cdylib"],"name":"cli-command-no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/cli-command-no_std.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"hello-world","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/hello-world.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["bin"],"name":"hello-world-no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/hello-world-no_std.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["cdylib"],"name":"http-proxy","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/http-proxy.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},{"kind":["example"],"crate_types":["cdylib"],"name":"http-proxy-no_std","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/examples/http-proxy-no_std.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":["dep:alloc"],"bitflags":["wit-bindgen/bitflags"],"core":["dep:core"],"default":["std","bitflags"],"rustc-dep-of-std":["core","alloc","wit-bindgen/rustc-dep-of-std"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasip2-1.0.4+wasi-0.2.12/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["no-std","wasm"],"keywords":["webassembly","wasm"],"readme":"README.md","repository":"https://github.com/bytecodealliance/wasi-rs","homepage":null,"documentation":"https://docs.rs/wasip2","edition":"2021","links":null,"default_run":null,"rust_version":"1.87.0"},{"name":"wasm-bindgen","version":"0.2.125","id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125","license":"MIT OR Apache-2.0","license_file":null,"description":"Easy support for interacting between JS and Rust.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"cfg-if","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"once_cell","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.12","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasm-bindgen-macro","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasm-bindgen-shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"once_cell","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustversion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.6","kind":"build","rename":"rustversion-compat","optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"paste","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm32\")","registry":null},{"name":"paste","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm64\")","registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(target_arch = \"wasm64\")","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasm_bindgen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-0.2.125/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-0.2.125/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"default":["std"],"enable-interning":["std"],"gg-alloc":[],"msrv":[],"rustversion":[],"serde":["dep:serde"],"serde-serialize":["serde","serde_json","std"],"serde_json":["dep:serde_json"],"spans":[],"std":[],"strict-macro":["wasm-bindgen-macro/strict-macro"],"xxx_debug_only_print_generated_code":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-0.2.125/Cargo.toml","metadata":{"docs":{"rs":{"features":["serde-serialize"]}}},"publish":null,"authors":["The wasm-bindgen Developers"],"categories":["wasm"],"keywords":[],"readme":"README.md","repository":"https://github.com/wasm-bindgen/wasm-bindgen","homepage":"https://wasm-bindgen.github.io/wasm-bindgen","documentation":"https://docs.rs/wasm-bindgen","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"wasm-bindgen-macro","version":"0.2.125","id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.125","license":"MIT OR Apache-2.0","license_file":null,"description":"Definition of the `#[wasm_bindgen]` attribute, an internal dependency\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wasm-bindgen-macro-support","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"trybuild","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"wasm_bindgen_macro","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-macro-0.2.125/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"strict-macro":["wasm-bindgen-macro-support/strict-macro"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-macro-0.2.125/Cargo.toml","metadata":null,"publish":null,"authors":["The wasm-bindgen Developers"],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro","homepage":"https://wasm-bindgen.github.io/wasm-bindgen/","documentation":"https://docs.rs/wasm-bindgen","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"wasm-bindgen-macro-support","version":"0.2.125","id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.125","license":"MIT OR Apache-2.0","license_file":null,"description":"Implementation APIs for the `#[wasm_bindgen]` attribute","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"bumpalo","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["visit","visit-mut","full","extra-traits"],"target":null,"registry":null},{"name":"wasm-bindgen-shared","source":"registry+https://github.com/rust-lang/crates.io-index","req":"=0.2.125","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasm_bindgen_macro_support","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-macro-support-0.2.125/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"extra-traits":["syn/extra-traits"],"strict-macro":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-macro-support-0.2.125/Cargo.toml","metadata":null,"publish":null,"authors":["The wasm-bindgen Developers"],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro-support","homepage":"https://wasm-bindgen.github.io/wasm-bindgen/","documentation":"https://docs.rs/wasm-bindgen","edition":"2021","links":null,"default_run":null,"rust_version":"1.77"},{"name":"wasm-bindgen-shared","version":"0.2.125","id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125","license":"MIT OR Apache-2.0","license_file":null,"description":"Shared support between wasm-bindgen and wasm-bindgen cli, an internal\ndependency.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"unicode-ident","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.5","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wasm_bindgen_shared","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-shared-0.2.125/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-shared-0.2.125/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasm-bindgen-shared-0.2.125/Cargo.toml","metadata":null,"publish":null,"authors":["The wasm-bindgen Developers"],"categories":[],"keywords":[],"readme":null,"repository":"https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/shared","homepage":"https://wasm-bindgen.github.io/wasm-bindgen/","documentation":"https://docs.rs/wasm-bindgen-shared","edition":"2021","links":"wasm_bindgen","default_run":null,"rust_version":"1.77"},{"name":"web_atoms","version":"0.2.6","id":"registry+https://github.com/rust-lang/crates.io-index#web_atoms@0.2.6","license":"MIT OR Apache-2.0","license_file":null,"description":"Atoms for xml5ever and html5ever","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"phf","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"string_cache","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"phf_codegen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"string_cache_codegen","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.6.1","kind":"build","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"web_atoms","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/web_atoms-0.2.6/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/web_atoms-0.2.6/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"serde":["string_cache/serde_support"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/web_atoms-0.2.6/Cargo.toml","metadata":null,"publish":null,"authors":["The html5ever Project Developers"],"categories":["web-programming"],"keywords":[],"readme":null,"repository":"https://github.com/servo/html5ever","homepage":null,"documentation":"https://docs.rs/web_atoms","edition":"2021","links":null,"default_run":null,"rust_version":"1.71.0"},{"name":"winapi","version":"0.3.9","id":"registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9","license":"MIT/Apache-2.0","license_file":null,"description":"Raw FFI bindings for all of Windows API.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"winapi-i686-pc-windows-gnu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"i686-pc-windows-gnu","registry":null},{"name":"winapi-x86_64-pc-windows-gnu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"x86_64-pc-windows-gnu","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"winapi","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-0.3.9/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-0.3.9/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{"accctrl":[],"aclapi":[],"activation":[],"adhoc":[],"appmgmt":[],"audioclient":[],"audiosessiontypes":[],"avrt":[],"basetsd":[],"bcrypt":[],"bits":[],"bits10_1":[],"bits1_5":[],"bits2_0":[],"bits2_5":[],"bits3_0":[],"bits4_0":[],"bits5_0":[],"bitscfg":[],"bitsmsg":[],"bluetoothapis":[],"bluetoothleapis":[],"bthdef":[],"bthioctl":[],"bthledef":[],"bthsdpdef":[],"bugcodes":[],"cderr":[],"cfg":[],"cfgmgr32":[],"cguid":[],"combaseapi":[],"coml2api":[],"commapi":[],"commctrl":[],"commdlg":[],"commoncontrols":[],"consoleapi":[],"corecrt":[],"corsym":[],"d2d1":[],"d2d1_1":[],"d2d1_2":[],"d2d1_3":[],"d2d1effectauthor":[],"d2d1effects":[],"d2d1effects_1":[],"d2d1effects_2":[],"d2d1svg":[],"d2dbasetypes":[],"d3d":[],"d3d10":[],"d3d10_1":[],"d3d10_1shader":[],"d3d10effect":[],"d3d10misc":[],"d3d10sdklayers":[],"d3d10shader":[],"d3d11":[],"d3d11_1":[],"d3d11_2":[],"d3d11_3":[],"d3d11_4":[],"d3d11on12":[],"d3d11sdklayers":[],"d3d11shader":[],"d3d11tokenizedprogramformat":[],"d3d12":[],"d3d12sdklayers":[],"d3d12shader":[],"d3d9":[],"d3d9caps":[],"d3d9types":[],"d3dcommon":[],"d3dcompiler":[],"d3dcsx":[],"d3dkmdt":[],"d3dkmthk":[],"d3dukmdt":[],"d3dx10core":[],"d3dx10math":[],"d3dx10mesh":[],"datetimeapi":[],"davclnt":[],"dbghelp":[],"dbt":[],"dcommon":[],"dcomp":[],"dcompanimation":[],"dcomptypes":[],"dde":[],"ddraw":[],"ddrawi":[],"ddrawint":[],"debug":["impl-debug"],"debugapi":[],"devguid":[],"devicetopology":[],"devpkey":[],"devpropdef":[],"dinput":[],"dinputd":[],"dispex":[],"dmksctl":[],"dmusicc":[],"docobj":[],"documenttarget":[],"dot1x":[],"dpa_dsa":[],"dpapi":[],"dsgetdc":[],"dsound":[],"dsrole":[],"dvp":[],"dwmapi":[],"dwrite":[],"dwrite_1":[],"dwrite_2":[],"dwrite_3":[],"dxdiag":[],"dxfile":[],"dxgi":[],"dxgi1_2":[],"dxgi1_3":[],"dxgi1_4":[],"dxgi1_5":[],"dxgi1_6":[],"dxgidebug":[],"dxgiformat":[],"dxgitype":[],"dxva2api":[],"dxvahd":[],"eaptypes":[],"enclaveapi":[],"endpointvolume":[],"errhandlingapi":[],"everything":[],"evntcons":[],"evntprov":[],"evntrace":[],"excpt":[],"exdisp":[],"fibersapi":[],"fileapi":[],"functiondiscoverykeys_devpkey":[],"gl-gl":[],"guiddef":[],"handleapi":[],"heapapi":[],"hidclass":[],"hidpi":[],"hidsdi":[],"hidusage":[],"highlevelmonitorconfigurationapi":[],"hstring":[],"http":[],"ifdef":[],"ifmib":[],"imm":[],"impl-debug":[],"impl-default":[],"in6addr":[],"inaddr":[],"inspectable":[],"interlockedapi":[],"intsafe":[],"ioapiset":[],"ipexport":[],"iphlpapi":[],"ipifcons":[],"ipmib":[],"iprtrmib":[],"iptypes":[],"jobapi":[],"jobapi2":[],"knownfolders":[],"ks":[],"ksmedia":[],"ktmtypes":[],"ktmw32":[],"l2cmn":[],"libloaderapi":[],"limits":[],"lmaccess":[],"lmalert":[],"lmapibuf":[],"lmat":[],"lmcons":[],"lmdfs":[],"lmerrlog":[],"lmjoin":[],"lmmsg":[],"lmremutl":[],"lmrepl":[],"lmserver":[],"lmshare":[],"lmstats":[],"lmsvc":[],"lmuse":[],"lmwksta":[],"lowlevelmonitorconfigurationapi":[],"lsalookup":[],"memoryapi":[],"minschannel":[],"minwinbase":[],"minwindef":[],"mmdeviceapi":[],"mmeapi":[],"mmreg":[],"mmsystem":[],"mprapidef":[],"msaatext":[],"mscat":[],"mschapp":[],"mssip":[],"mstcpip":[],"mswsock":[],"mswsockdef":[],"namedpipeapi":[],"namespaceapi":[],"nb30":[],"ncrypt":[],"netioapi":[],"nldef":[],"ntddndis":[],"ntddscsi":[],"ntddser":[],"ntdef":[],"ntlsa":[],"ntsecapi":[],"ntstatus":[],"oaidl":[],"objbase":[],"objidl":[],"objidlbase":[],"ocidl":[],"ole2":[],"oleauto":[],"olectl":[],"oleidl":[],"opmapi":[],"pdh":[],"perflib":[],"physicalmonitorenumerationapi":[],"playsoundapi":[],"portabledevice":[],"portabledeviceapi":[],"portabledevicetypes":[],"powerbase":[],"powersetting":[],"powrprof":[],"processenv":[],"processsnapshot":[],"processthreadsapi":[],"processtopologyapi":[],"profileapi":[],"propidl":[],"propkey":[],"propkeydef":[],"propsys":[],"prsht":[],"psapi":[],"qos":[],"realtimeapiset":[],"reason":[],"restartmanager":[],"restrictederrorinfo":[],"rmxfguid":[],"roapi":[],"robuffer":[],"roerrorapi":[],"rpc":[],"rpcdce":[],"rpcndr":[],"rtinfo":[],"sapi":[],"sapi51":[],"sapi53":[],"sapiddk":[],"sapiddk51":[],"schannel":[],"sddl":[],"securityappcontainer":[],"securitybaseapi":[],"servprov":[],"setupapi":[],"shellapi":[],"shellscalingapi":[],"shlobj":[],"shobjidl":[],"shobjidl_core":[],"shtypes":[],"softpub":[],"spapidef":[],"spellcheck":[],"sporder":[],"sql":[],"sqlext":[],"sqltypes":[],"sqlucode":[],"sspi":[],"std":[],"stralign":[],"stringapiset":[],"strmif":[],"subauth":[],"synchapi":[],"sysinfoapi":[],"systemtopologyapi":[],"taskschd":[],"tcpestats":[],"tcpmib":[],"textstor":[],"threadpoolapiset":[],"threadpoollegacyapiset":[],"timeapi":[],"timezoneapi":[],"tlhelp32":[],"transportsettingcommon":[],"tvout":[],"udpmib":[],"unknwnbase":[],"urlhist":[],"urlmon":[],"usb":[],"usbioctl":[],"usbiodef":[],"usbscan":[],"usbspec":[],"userenv":[],"usp10":[],"utilapiset":[],"uxtheme":[],"vadefs":[],"vcruntime":[],"vsbackup":[],"vss":[],"vsserror":[],"vswriter":[],"wbemads":[],"wbemcli":[],"wbemdisp":[],"wbemprov":[],"wbemtran":[],"wct":[],"werapi":[],"winbase":[],"wincodec":[],"wincodecsdk":[],"wincon":[],"wincontypes":[],"wincred":[],"wincrypt":[],"windef":[],"windot11":[],"windowsceip":[],"windowsx":[],"winefs":[],"winerror":[],"winevt":[],"wingdi":[],"winhttp":[],"wininet":[],"winineti":[],"winioctl":[],"winnetwk":[],"winnls":[],"winnt":[],"winreg":[],"winsafer":[],"winscard":[],"winsmcrd":[],"winsock2":[],"winspool":[],"winstring":[],"winsvc":[],"wintrust":[],"winusb":[],"winusbio":[],"winuser":[],"winver":[],"wlanapi":[],"wlanihv":[],"wlanihvtypes":[],"wlantypes":[],"wlclient":[],"wmistr":[],"wnnc":[],"wow64apiset":[],"wpdmtpextensions":[],"ws2bth":[],"ws2def":[],"ws2ipdef":[],"ws2spi":[],"ws2tcpip":[],"wtsapi32":[],"wtypes":[],"wtypesbase":[],"xinput":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-0.3.9/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","features":["everything","impl-debug","impl-default"],"targets":["aarch64-pc-windows-msvc","i686-pc-windows-msvc","x86_64-pc-windows-msvc"]}}},"publish":null,"authors":["Peter Atashian "],"categories":["external-ffi-bindings","no-std","os::windows-apis"],"keywords":["windows","ffi","win32","com","directx"],"readme":"README.md","repository":"https://github.com/retep998/winapi-rs","homepage":null,"documentation":"https://docs.rs/winapi/","edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"winapi-i686-pc-windows-gnu","version":"0.4.0","id":"registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0","license":"MIT/Apache-2.0","license_file":null,"description":"Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"winapi_i686_pc_windows_gnu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-i686-pc-windows-gnu-0.4.0/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml","metadata":null,"publish":null,"authors":["Peter Atashian "],"categories":[],"keywords":["windows"],"readme":null,"repository":"https://github.com/retep998/winapi-rs","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"winapi-util","version":"0.1.11","id":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","license":"Unlicense OR MIT","license_file":null,"description":"A dumping ground for high level safe wrappers over windows-sys.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"windows-sys","source":"registry+https://github.com/rust-lang/crates.io-index","req":">=0.48.0, <=0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["Win32_Foundation","Win32_Storage_FileSystem","Win32_System_Console","Win32_System_SystemInformation"],"target":"cfg(windows)","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"winapi_util","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-util-0.1.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-util-0.1.11/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-pc-windows-msvc"]}}},"publish":null,"authors":["Andrew Gallant "],"categories":["os::windows-apis","external-ffi-bindings"],"keywords":["windows","windows-sys","util","win"],"readme":"README.md","repository":"https://github.com/BurntSushi/winapi-util","homepage":"https://github.com/BurntSushi/winapi-util","documentation":"https://docs.rs/winapi-util","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"winapi-x86_64-pc-windows-gnu","version":"0.4.0","id":"registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0","license":"MIT/Apache-2.0","license_file":null,"description":"Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"winapi_x86_64_pc_windows_gnu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs","edition":"2015","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml","metadata":null,"publish":null,"authors":["Peter Atashian "],"categories":[],"keywords":["windows"],"readme":null,"repository":"https://github.com/retep998/winapi-rs","homepage":null,"documentation":null,"edition":"2015","links":null,"default_run":null,"rust_version":null},{"name":"windows-link","version":"0.2.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Linking for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_link","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-link-0.2.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-link-0.2.1/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"windows-sys","version":"0.60.2","id":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.60.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Rust for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"windows-targets","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.2","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-sys-0.60.2/src/lib.rs","edition":"2021","doc":true,"doctest":false,"test":false}],"features":{"Wdk":["Win32_Foundation"],"Wdk_Devices":["Wdk"],"Wdk_Devices_Bluetooth":["Wdk_Devices"],"Wdk_Devices_HumanInterfaceDevice":["Wdk_Devices"],"Wdk_Foundation":["Wdk"],"Wdk_Graphics":["Wdk"],"Wdk_Graphics_Direct3D":["Wdk_Graphics"],"Wdk_NetworkManagement":["Wdk"],"Wdk_NetworkManagement_Ndis":["Wdk_NetworkManagement"],"Wdk_NetworkManagement_WindowsFilteringPlatform":["Wdk_NetworkManagement"],"Wdk_Storage":["Wdk"],"Wdk_Storage_FileSystem":["Wdk_Storage"],"Wdk_Storage_FileSystem_Minifilters":["Wdk_Storage_FileSystem"],"Wdk_System":["Wdk"],"Wdk_System_IO":["Wdk_System"],"Wdk_System_Memory":["Wdk_System"],"Wdk_System_OfflineRegistry":["Wdk_System"],"Wdk_System_Registry":["Wdk_System"],"Wdk_System_SystemInformation":["Wdk_System"],"Wdk_System_SystemServices":["Wdk_System"],"Wdk_System_Threading":["Wdk_System"],"Win32":["Win32_Foundation"],"Win32_Data":["Win32"],"Win32_Data_HtmlHelp":["Win32_Data"],"Win32_Data_RightsManagement":["Win32_Data"],"Win32_Devices":["Win32"],"Win32_Devices_AllJoyn":["Win32_Devices"],"Win32_Devices_Beep":["Win32_Devices"],"Win32_Devices_BiometricFramework":["Win32_Devices"],"Win32_Devices_Bluetooth":["Win32_Devices"],"Win32_Devices_Cdrom":["Win32_Devices"],"Win32_Devices_Communication":["Win32_Devices"],"Win32_Devices_DeviceAndDriverInstallation":["Win32_Devices"],"Win32_Devices_DeviceQuery":["Win32_Devices"],"Win32_Devices_Display":["Win32_Devices"],"Win32_Devices_Dvd":["Win32_Devices"],"Win32_Devices_Enumeration":["Win32_Devices"],"Win32_Devices_Enumeration_Pnp":["Win32_Devices_Enumeration"],"Win32_Devices_Fax":["Win32_Devices"],"Win32_Devices_HumanInterfaceDevice":["Win32_Devices"],"Win32_Devices_Nfc":["Win32_Devices"],"Win32_Devices_Nfp":["Win32_Devices"],"Win32_Devices_PortableDevices":["Win32_Devices"],"Win32_Devices_Properties":["Win32_Devices"],"Win32_Devices_Pwm":["Win32_Devices"],"Win32_Devices_Sensors":["Win32_Devices"],"Win32_Devices_SerialCommunication":["Win32_Devices"],"Win32_Devices_Tapi":["Win32_Devices"],"Win32_Devices_Usb":["Win32_Devices"],"Win32_Devices_WebServicesOnDevices":["Win32_Devices"],"Win32_Foundation":["Win32"],"Win32_Gaming":["Win32"],"Win32_Globalization":["Win32"],"Win32_Graphics":["Win32"],"Win32_Graphics_Dwm":["Win32_Graphics"],"Win32_Graphics_Gdi":["Win32_Graphics"],"Win32_Graphics_GdiPlus":["Win32_Graphics"],"Win32_Graphics_Hlsl":["Win32_Graphics"],"Win32_Graphics_OpenGL":["Win32_Graphics"],"Win32_Graphics_Printing":["Win32_Graphics"],"Win32_Graphics_Printing_PrintTicket":["Win32_Graphics_Printing"],"Win32_Management":["Win32"],"Win32_Management_MobileDeviceManagementRegistration":["Win32_Management"],"Win32_Media":["Win32"],"Win32_Media_Audio":["Win32_Media"],"Win32_Media_DxMediaObjects":["Win32_Media"],"Win32_Media_KernelStreaming":["Win32_Media"],"Win32_Media_Multimedia":["Win32_Media"],"Win32_Media_Streaming":["Win32_Media"],"Win32_Media_WindowsMediaFormat":["Win32_Media"],"Win32_NetworkManagement":["Win32"],"Win32_NetworkManagement_Dhcp":["Win32_NetworkManagement"],"Win32_NetworkManagement_Dns":["Win32_NetworkManagement"],"Win32_NetworkManagement_InternetConnectionWizard":["Win32_NetworkManagement"],"Win32_NetworkManagement_IpHelper":["Win32_NetworkManagement"],"Win32_NetworkManagement_Multicast":["Win32_NetworkManagement"],"Win32_NetworkManagement_Ndis":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetBios":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetManagement":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetShell":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetworkDiagnosticsFramework":["Win32_NetworkManagement"],"Win32_NetworkManagement_P2P":["Win32_NetworkManagement"],"Win32_NetworkManagement_QoS":["Win32_NetworkManagement"],"Win32_NetworkManagement_Rras":["Win32_NetworkManagement"],"Win32_NetworkManagement_Snmp":["Win32_NetworkManagement"],"Win32_NetworkManagement_WNet":["Win32_NetworkManagement"],"Win32_NetworkManagement_WebDav":["Win32_NetworkManagement"],"Win32_NetworkManagement_WiFi":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsConnectionManager":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsFilteringPlatform":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsFirewall":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsNetworkVirtualization":["Win32_NetworkManagement"],"Win32_Networking":["Win32"],"Win32_Networking_ActiveDirectory":["Win32_Networking"],"Win32_Networking_Clustering":["Win32_Networking"],"Win32_Networking_HttpServer":["Win32_Networking"],"Win32_Networking_Ldap":["Win32_Networking"],"Win32_Networking_WebSocket":["Win32_Networking"],"Win32_Networking_WinHttp":["Win32_Networking"],"Win32_Networking_WinInet":["Win32_Networking"],"Win32_Networking_WinSock":["Win32_Networking"],"Win32_Networking_WindowsWebServices":["Win32_Networking"],"Win32_Security":["Win32"],"Win32_Security_AppLocker":["Win32_Security"],"Win32_Security_Authentication":["Win32_Security"],"Win32_Security_Authentication_Identity":["Win32_Security_Authentication"],"Win32_Security_Authorization":["Win32_Security"],"Win32_Security_Credentials":["Win32_Security"],"Win32_Security_Cryptography":["Win32_Security"],"Win32_Security_Cryptography_Catalog":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_Certificates":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_Sip":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_UI":["Win32_Security_Cryptography"],"Win32_Security_DiagnosticDataQuery":["Win32_Security"],"Win32_Security_DirectoryServices":["Win32_Security"],"Win32_Security_EnterpriseData":["Win32_Security"],"Win32_Security_ExtensibleAuthenticationProtocol":["Win32_Security"],"Win32_Security_Isolation":["Win32_Security"],"Win32_Security_LicenseProtection":["Win32_Security"],"Win32_Security_NetworkAccessProtection":["Win32_Security"],"Win32_Security_WinTrust":["Win32_Security"],"Win32_Security_WinWlx":["Win32_Security"],"Win32_Storage":["Win32"],"Win32_Storage_Cabinets":["Win32_Storage"],"Win32_Storage_CloudFilters":["Win32_Storage"],"Win32_Storage_Compression":["Win32_Storage"],"Win32_Storage_DistributedFileSystem":["Win32_Storage"],"Win32_Storage_FileHistory":["Win32_Storage"],"Win32_Storage_FileSystem":["Win32_Storage"],"Win32_Storage_Imapi":["Win32_Storage"],"Win32_Storage_IndexServer":["Win32_Storage"],"Win32_Storage_InstallableFileSystems":["Win32_Storage"],"Win32_Storage_IscsiDisc":["Win32_Storage"],"Win32_Storage_Jet":["Win32_Storage"],"Win32_Storage_Nvme":["Win32_Storage"],"Win32_Storage_OfflineFiles":["Win32_Storage"],"Win32_Storage_OperationRecorder":["Win32_Storage"],"Win32_Storage_Packaging":["Win32_Storage"],"Win32_Storage_Packaging_Appx":["Win32_Storage_Packaging"],"Win32_Storage_ProjectedFileSystem":["Win32_Storage"],"Win32_Storage_StructuredStorage":["Win32_Storage"],"Win32_Storage_Vhd":["Win32_Storage"],"Win32_Storage_Xps":["Win32_Storage"],"Win32_System":["Win32"],"Win32_System_AddressBook":["Win32_System"],"Win32_System_Antimalware":["Win32_System"],"Win32_System_ApplicationInstallationAndServicing":["Win32_System"],"Win32_System_ApplicationVerifier":["Win32_System"],"Win32_System_ClrHosting":["Win32_System"],"Win32_System_Com":["Win32_System"],"Win32_System_Com_Marshal":["Win32_System_Com"],"Win32_System_Com_StructuredStorage":["Win32_System_Com"],"Win32_System_Com_Urlmon":["Win32_System_Com"],"Win32_System_ComponentServices":["Win32_System"],"Win32_System_Console":["Win32_System"],"Win32_System_CorrelationVector":["Win32_System"],"Win32_System_DataExchange":["Win32_System"],"Win32_System_DeploymentServices":["Win32_System"],"Win32_System_DeveloperLicensing":["Win32_System"],"Win32_System_Diagnostics":["Win32_System"],"Win32_System_Diagnostics_Ceip":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_Debug":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_Debug_Extensions":["Win32_System_Diagnostics_Debug"],"Win32_System_Diagnostics_Etw":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_ProcessSnapshotting":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_ToolHelp":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_TraceLogging":["Win32_System_Diagnostics"],"Win32_System_DistributedTransactionCoordinator":["Win32_System"],"Win32_System_Environment":["Win32_System"],"Win32_System_ErrorReporting":["Win32_System"],"Win32_System_EventCollector":["Win32_System"],"Win32_System_EventLog":["Win32_System"],"Win32_System_EventNotificationService":["Win32_System"],"Win32_System_GroupPolicy":["Win32_System"],"Win32_System_HostCompute":["Win32_System"],"Win32_System_HostComputeNetwork":["Win32_System"],"Win32_System_HostComputeSystem":["Win32_System"],"Win32_System_Hypervisor":["Win32_System"],"Win32_System_IO":["Win32_System"],"Win32_System_Iis":["Win32_System"],"Win32_System_Ioctl":["Win32_System"],"Win32_System_JobObjects":["Win32_System"],"Win32_System_Js":["Win32_System"],"Win32_System_Kernel":["Win32_System"],"Win32_System_LibraryLoader":["Win32_System"],"Win32_System_Mailslots":["Win32_System"],"Win32_System_Mapi":["Win32_System"],"Win32_System_Memory":["Win32_System"],"Win32_System_Memory_NonVolatile":["Win32_System_Memory"],"Win32_System_MessageQueuing":["Win32_System"],"Win32_System_MixedReality":["Win32_System"],"Win32_System_Ole":["Win32_System"],"Win32_System_PasswordManagement":["Win32_System"],"Win32_System_Performance":["Win32_System"],"Win32_System_Performance_HardwareCounterProfiling":["Win32_System_Performance"],"Win32_System_Pipes":["Win32_System"],"Win32_System_Power":["Win32_System"],"Win32_System_ProcessStatus":["Win32_System"],"Win32_System_Recovery":["Win32_System"],"Win32_System_Registry":["Win32_System"],"Win32_System_RemoteDesktop":["Win32_System"],"Win32_System_RemoteManagement":["Win32_System"],"Win32_System_RestartManager":["Win32_System"],"Win32_System_Restore":["Win32_System"],"Win32_System_Rpc":["Win32_System"],"Win32_System_Search":["Win32_System"],"Win32_System_Search_Common":["Win32_System_Search"],"Win32_System_SecurityCenter":["Win32_System"],"Win32_System_Services":["Win32_System"],"Win32_System_SetupAndMigration":["Win32_System"],"Win32_System_Shutdown":["Win32_System"],"Win32_System_StationsAndDesktops":["Win32_System"],"Win32_System_SubsystemForLinux":["Win32_System"],"Win32_System_SystemInformation":["Win32_System"],"Win32_System_SystemServices":["Win32_System"],"Win32_System_Threading":["Win32_System"],"Win32_System_Time":["Win32_System"],"Win32_System_TpmBaseServices":["Win32_System"],"Win32_System_UserAccessLogging":["Win32_System"],"Win32_System_Variant":["Win32_System"],"Win32_System_VirtualDosMachines":["Win32_System"],"Win32_System_WindowsProgramming":["Win32_System"],"Win32_System_Wmi":["Win32_System"],"Win32_UI":["Win32"],"Win32_UI_Accessibility":["Win32_UI"],"Win32_UI_ColorSystem":["Win32_UI"],"Win32_UI_Controls":["Win32_UI"],"Win32_UI_Controls_Dialogs":["Win32_UI_Controls"],"Win32_UI_HiDpi":["Win32_UI"],"Win32_UI_Input":["Win32_UI"],"Win32_UI_Input_Ime":["Win32_UI_Input"],"Win32_UI_Input_KeyboardAndMouse":["Win32_UI_Input"],"Win32_UI_Input_Pointer":["Win32_UI_Input"],"Win32_UI_Input_Touch":["Win32_UI_Input"],"Win32_UI_Input_XboxController":["Win32_UI_Input"],"Win32_UI_InteractionContext":["Win32_UI"],"Win32_UI_Magnification":["Win32_UI"],"Win32_UI_Shell":["Win32_UI"],"Win32_UI_Shell_Common":["Win32_UI_Shell"],"Win32_UI_Shell_PropertiesSystem":["Win32_UI_Shell"],"Win32_UI_TabletPC":["Win32_UI"],"Win32_UI_TextServices":["Win32_UI"],"Win32_UI_WindowsAndMessaging":["Win32_UI"],"Win32_Web":["Win32"],"Win32_Web_InternetExplorer":["Win32_Web"],"default":[],"docs":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-sys-0.60.2/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true,"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":["Microsoft"],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows-sys","version":"0.61.2","id":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","license":"MIT OR Apache-2.0","license_file":null,"description":"Rust for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"windows-link","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_sys","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-sys-0.61.2/src/lib.rs","edition":"2021","doc":true,"doctest":false,"test":false}],"features":{"Wdk":["Win32_Foundation"],"Wdk_Devices":["Wdk"],"Wdk_Devices_Bluetooth":["Wdk_Devices"],"Wdk_Devices_HumanInterfaceDevice":["Wdk_Devices"],"Wdk_Foundation":["Wdk"],"Wdk_Graphics":["Wdk"],"Wdk_Graphics_Direct3D":["Wdk_Graphics"],"Wdk_NetworkManagement":["Wdk"],"Wdk_NetworkManagement_Ndis":["Wdk_NetworkManagement"],"Wdk_NetworkManagement_WindowsFilteringPlatform":["Wdk_NetworkManagement"],"Wdk_Storage":["Wdk"],"Wdk_Storage_FileSystem":["Wdk_Storage"],"Wdk_Storage_FileSystem_Minifilters":["Wdk_Storage_FileSystem"],"Wdk_System":["Wdk"],"Wdk_System_IO":["Wdk_System"],"Wdk_System_Memory":["Wdk_System"],"Wdk_System_OfflineRegistry":["Wdk_System"],"Wdk_System_Registry":["Wdk_System"],"Wdk_System_SystemInformation":["Wdk_System"],"Wdk_System_SystemServices":["Wdk_System"],"Wdk_System_Threading":["Wdk_System"],"Win32":["Win32_Foundation"],"Win32_Data":["Win32"],"Win32_Data_HtmlHelp":["Win32_Data"],"Win32_Data_RightsManagement":["Win32_Data"],"Win32_Devices":["Win32"],"Win32_Devices_AllJoyn":["Win32_Devices"],"Win32_Devices_Beep":["Win32_Devices"],"Win32_Devices_BiometricFramework":["Win32_Devices"],"Win32_Devices_Bluetooth":["Win32_Devices"],"Win32_Devices_Cdrom":["Win32_Devices"],"Win32_Devices_Communication":["Win32_Devices"],"Win32_Devices_DeviceAndDriverInstallation":["Win32_Devices"],"Win32_Devices_DeviceQuery":["Win32_Devices"],"Win32_Devices_Display":["Win32_Devices"],"Win32_Devices_Dvd":["Win32_Devices"],"Win32_Devices_Enumeration":["Win32_Devices"],"Win32_Devices_Enumeration_Pnp":["Win32_Devices_Enumeration"],"Win32_Devices_Fax":["Win32_Devices"],"Win32_Devices_HumanInterfaceDevice":["Win32_Devices"],"Win32_Devices_Nfc":["Win32_Devices"],"Win32_Devices_Nfp":["Win32_Devices"],"Win32_Devices_PortableDevices":["Win32_Devices"],"Win32_Devices_Properties":["Win32_Devices"],"Win32_Devices_Pwm":["Win32_Devices"],"Win32_Devices_Sensors":["Win32_Devices"],"Win32_Devices_SerialCommunication":["Win32_Devices"],"Win32_Devices_Tapi":["Win32_Devices"],"Win32_Devices_Usb":["Win32_Devices"],"Win32_Devices_WebServicesOnDevices":["Win32_Devices"],"Win32_Foundation":["Win32"],"Win32_Gaming":["Win32"],"Win32_Globalization":["Win32"],"Win32_Graphics":["Win32"],"Win32_Graphics_Dwm":["Win32_Graphics"],"Win32_Graphics_Gdi":["Win32_Graphics"],"Win32_Graphics_GdiPlus":["Win32_Graphics"],"Win32_Graphics_Hlsl":["Win32_Graphics"],"Win32_Graphics_OpenGL":["Win32_Graphics"],"Win32_Graphics_Printing":["Win32_Graphics"],"Win32_Graphics_Printing_PrintTicket":["Win32_Graphics_Printing"],"Win32_Management":["Win32"],"Win32_Management_MobileDeviceManagementRegistration":["Win32_Management"],"Win32_Media":["Win32"],"Win32_Media_Audio":["Win32_Media"],"Win32_Media_DxMediaObjects":["Win32_Media"],"Win32_Media_KernelStreaming":["Win32_Media"],"Win32_Media_Multimedia":["Win32_Media"],"Win32_Media_Streaming":["Win32_Media"],"Win32_Media_WindowsMediaFormat":["Win32_Media"],"Win32_NetworkManagement":["Win32"],"Win32_NetworkManagement_Dhcp":["Win32_NetworkManagement"],"Win32_NetworkManagement_Dns":["Win32_NetworkManagement"],"Win32_NetworkManagement_InternetConnectionWizard":["Win32_NetworkManagement"],"Win32_NetworkManagement_IpHelper":["Win32_NetworkManagement"],"Win32_NetworkManagement_Multicast":["Win32_NetworkManagement"],"Win32_NetworkManagement_Ndis":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetBios":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetManagement":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetShell":["Win32_NetworkManagement"],"Win32_NetworkManagement_NetworkDiagnosticsFramework":["Win32_NetworkManagement"],"Win32_NetworkManagement_P2P":["Win32_NetworkManagement"],"Win32_NetworkManagement_QoS":["Win32_NetworkManagement"],"Win32_NetworkManagement_Rras":["Win32_NetworkManagement"],"Win32_NetworkManagement_Snmp":["Win32_NetworkManagement"],"Win32_NetworkManagement_WNet":["Win32_NetworkManagement"],"Win32_NetworkManagement_WebDav":["Win32_NetworkManagement"],"Win32_NetworkManagement_WiFi":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsConnectionManager":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsFilteringPlatform":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsFirewall":["Win32_NetworkManagement"],"Win32_NetworkManagement_WindowsNetworkVirtualization":["Win32_NetworkManagement"],"Win32_Networking":["Win32"],"Win32_Networking_ActiveDirectory":["Win32_Networking"],"Win32_Networking_Clustering":["Win32_Networking"],"Win32_Networking_HttpServer":["Win32_Networking"],"Win32_Networking_Ldap":["Win32_Networking"],"Win32_Networking_WebSocket":["Win32_Networking"],"Win32_Networking_WinHttp":["Win32_Networking"],"Win32_Networking_WinInet":["Win32_Networking"],"Win32_Networking_WinSock":["Win32_Networking"],"Win32_Networking_WindowsWebServices":["Win32_Networking"],"Win32_Security":["Win32"],"Win32_Security_AppLocker":["Win32_Security"],"Win32_Security_Authentication":["Win32_Security"],"Win32_Security_Authentication_Identity":["Win32_Security_Authentication"],"Win32_Security_Authorization":["Win32_Security"],"Win32_Security_Credentials":["Win32_Security"],"Win32_Security_Cryptography":["Win32_Security"],"Win32_Security_Cryptography_Catalog":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_Certificates":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_Sip":["Win32_Security_Cryptography"],"Win32_Security_Cryptography_UI":["Win32_Security_Cryptography"],"Win32_Security_DiagnosticDataQuery":["Win32_Security"],"Win32_Security_DirectoryServices":["Win32_Security"],"Win32_Security_EnterpriseData":["Win32_Security"],"Win32_Security_ExtensibleAuthenticationProtocol":["Win32_Security"],"Win32_Security_Isolation":["Win32_Security"],"Win32_Security_LicenseProtection":["Win32_Security"],"Win32_Security_NetworkAccessProtection":["Win32_Security"],"Win32_Security_WinTrust":["Win32_Security"],"Win32_Security_WinWlx":["Win32_Security"],"Win32_Storage":["Win32"],"Win32_Storage_Cabinets":["Win32_Storage"],"Win32_Storage_CloudFilters":["Win32_Storage"],"Win32_Storage_Compression":["Win32_Storage"],"Win32_Storage_DistributedFileSystem":["Win32_Storage"],"Win32_Storage_FileHistory":["Win32_Storage"],"Win32_Storage_FileSystem":["Win32_Storage"],"Win32_Storage_Imapi":["Win32_Storage"],"Win32_Storage_IndexServer":["Win32_Storage"],"Win32_Storage_InstallableFileSystems":["Win32_Storage"],"Win32_Storage_IscsiDisc":["Win32_Storage"],"Win32_Storage_Jet":["Win32_Storage"],"Win32_Storage_Nvme":["Win32_Storage"],"Win32_Storage_OfflineFiles":["Win32_Storage"],"Win32_Storage_OperationRecorder":["Win32_Storage"],"Win32_Storage_Packaging":["Win32_Storage"],"Win32_Storage_Packaging_Appx":["Win32_Storage_Packaging"],"Win32_Storage_ProjectedFileSystem":["Win32_Storage"],"Win32_Storage_StructuredStorage":["Win32_Storage"],"Win32_Storage_Vhd":["Win32_Storage"],"Win32_Storage_Xps":["Win32_Storage"],"Win32_System":["Win32"],"Win32_System_AddressBook":["Win32_System"],"Win32_System_Antimalware":["Win32_System"],"Win32_System_ApplicationInstallationAndServicing":["Win32_System"],"Win32_System_ApplicationVerifier":["Win32_System"],"Win32_System_ClrHosting":["Win32_System"],"Win32_System_Com":["Win32_System"],"Win32_System_Com_Marshal":["Win32_System_Com"],"Win32_System_Com_StructuredStorage":["Win32_System_Com"],"Win32_System_Com_Urlmon":["Win32_System_Com"],"Win32_System_ComponentServices":["Win32_System"],"Win32_System_Console":["Win32_System"],"Win32_System_CorrelationVector":["Win32_System"],"Win32_System_DataExchange":["Win32_System"],"Win32_System_DeploymentServices":["Win32_System"],"Win32_System_DeveloperLicensing":["Win32_System"],"Win32_System_Diagnostics":["Win32_System"],"Win32_System_Diagnostics_Ceip":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_Debug":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_Debug_Extensions":["Win32_System_Diagnostics_Debug"],"Win32_System_Diagnostics_Etw":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_ProcessSnapshotting":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_ToolHelp":["Win32_System_Diagnostics"],"Win32_System_Diagnostics_TraceLogging":["Win32_System_Diagnostics"],"Win32_System_DistributedTransactionCoordinator":["Win32_System"],"Win32_System_Environment":["Win32_System"],"Win32_System_ErrorReporting":["Win32_System"],"Win32_System_EventCollector":["Win32_System"],"Win32_System_EventLog":["Win32_System"],"Win32_System_EventNotificationService":["Win32_System"],"Win32_System_GroupPolicy":["Win32_System"],"Win32_System_HostCompute":["Win32_System"],"Win32_System_HostComputeNetwork":["Win32_System"],"Win32_System_HostComputeSystem":["Win32_System"],"Win32_System_Hypervisor":["Win32_System"],"Win32_System_IO":["Win32_System"],"Win32_System_Iis":["Win32_System"],"Win32_System_Ioctl":["Win32_System"],"Win32_System_JobObjects":["Win32_System"],"Win32_System_Js":["Win32_System"],"Win32_System_Kernel":["Win32_System"],"Win32_System_LibraryLoader":["Win32_System"],"Win32_System_Mailslots":["Win32_System"],"Win32_System_Mapi":["Win32_System"],"Win32_System_Memory":["Win32_System"],"Win32_System_Memory_NonVolatile":["Win32_System_Memory"],"Win32_System_MessageQueuing":["Win32_System"],"Win32_System_MixedReality":["Win32_System"],"Win32_System_Ole":["Win32_System"],"Win32_System_PasswordManagement":["Win32_System"],"Win32_System_Performance":["Win32_System"],"Win32_System_Performance_HardwareCounterProfiling":["Win32_System_Performance"],"Win32_System_Pipes":["Win32_System"],"Win32_System_Power":["Win32_System"],"Win32_System_ProcessStatus":["Win32_System"],"Win32_System_Recovery":["Win32_System"],"Win32_System_Registry":["Win32_System"],"Win32_System_RemoteDesktop":["Win32_System"],"Win32_System_RemoteManagement":["Win32_System"],"Win32_System_RestartManager":["Win32_System"],"Win32_System_Restore":["Win32_System"],"Win32_System_Rpc":["Win32_System"],"Win32_System_Search":["Win32_System"],"Win32_System_Search_Common":["Win32_System_Search"],"Win32_System_SecurityCenter":["Win32_System"],"Win32_System_Services":["Win32_System"],"Win32_System_SetupAndMigration":["Win32_System"],"Win32_System_Shutdown":["Win32_System"],"Win32_System_StationsAndDesktops":["Win32_System"],"Win32_System_SubsystemForLinux":["Win32_System"],"Win32_System_SystemInformation":["Win32_System"],"Win32_System_SystemServices":["Win32_System"],"Win32_System_Threading":["Win32_System"],"Win32_System_Time":["Win32_System"],"Win32_System_TpmBaseServices":["Win32_System"],"Win32_System_UserAccessLogging":["Win32_System"],"Win32_System_Variant":["Win32_System"],"Win32_System_VirtualDosMachines":["Win32_System"],"Win32_System_WindowsProgramming":["Win32_System"],"Win32_System_Wmi":["Win32_System"],"Win32_UI":["Win32"],"Win32_UI_Accessibility":["Win32_UI"],"Win32_UI_ColorSystem":["Win32_UI"],"Win32_UI_Controls":["Win32_UI"],"Win32_UI_Controls_Dialogs":["Win32_UI_Controls"],"Win32_UI_HiDpi":["Win32_UI"],"Win32_UI_Input":["Win32_UI"],"Win32_UI_Input_Ime":["Win32_UI_Input"],"Win32_UI_Input_KeyboardAndMouse":["Win32_UI_Input"],"Win32_UI_Input_Pointer":["Win32_UI_Input"],"Win32_UI_Input_Touch":["Win32_UI_Input"],"Win32_UI_Input_XboxController":["Win32_UI_Input"],"Win32_UI_InteractionContext":["Win32_UI"],"Win32_UI_Magnification":["Win32_UI"],"Win32_UI_Shell":["Win32_UI"],"Win32_UI_Shell_Common":["Win32_UI_Shell"],"Win32_UI_Shell_PropertiesSystem":["Win32_UI_Shell"],"Win32_UI_TabletPC":["Win32_UI"],"Win32_UI_TextServices":["Win32_UI"],"Win32_UI_WindowsAndMessaging":["Win32_UI"],"Win32_Web":["Win32"],"Win32_Web_InternetExplorer":["Win32_Web"],"default":[],"docs":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-sys-0.61.2/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[],"all-features":true}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71"},{"name":"windows-targets","version":"0.53.5","id":"registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.53.5","license":"MIT OR Apache-2.0","license_file":null,"description":"Import libs for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"windows_aarch64_gnullvm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"aarch64-pc-windows-gnullvm","registry":null},{"name":"windows_x86_64_msvc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(any(target_arch = \"x86_64\", target_arch = \"arm64ec\"), target_env = \"msvc\", not(windows_raw_dylib)))","registry":null},{"name":"windows_aarch64_msvc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))","registry":null},{"name":"windows_i686_gnu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"x86\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))","registry":null},{"name":"windows_i686_msvc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"x86\", target_env = \"msvc\", not(windows_raw_dylib)))","registry":null},{"name":"windows_x86_64_gnu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(all(target_arch = \"x86_64\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))","registry":null},{"name":"windows-link","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(windows_raw_dylib)","registry":null},{"name":"windows_i686_gnullvm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"i686-pc-windows-gnullvm","registry":null},{"name":"windows_x86_64_gnullvm","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.53.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"x86_64-pc-windows-gnullvm","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_targets","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-targets-0.53.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows-targets-0.53.5/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_aarch64_gnullvm","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_aarch64_gnullvm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_gnullvm-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_gnullvm-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_gnullvm-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_aarch64_msvc","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_aarch64_msvc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_msvc-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_msvc-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_aarch64_msvc-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_i686_gnu","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_i686_gnu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnu-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnu-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnu-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_i686_gnullvm","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnullvm@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_i686_gnullvm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnullvm-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnullvm-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_gnullvm-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_i686_msvc","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_i686_msvc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_msvc-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_msvc-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_i686_msvc-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_x86_64_gnu","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_x86_64_gnu","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnu-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnu-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnu-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_x86_64_gnullvm","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_x86_64_gnullvm","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnullvm-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnullvm-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnullvm-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"windows_x86_64_msvc","version":"0.53.1","id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.53.1","license":"MIT OR Apache-2.0","license_file":null,"description":"Import lib for Windows","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"windows_x86_64_msvc","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_msvc-0.53.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_msvc-0.53.1/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_msvc-0.53.1/Cargo.toml","metadata":{"docs":{"rs":{"default-target":"x86_64-pc-windows-msvc","targets":[]}}},"publish":null,"authors":[],"categories":["os::windows-apis"],"keywords":[],"readme":"readme.md","repository":"https://github.com/microsoft/windows-rs","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.60"},{"name":"wit-bindgen","version":"0.57.1","id":"registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1","license":"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT","license_file":null,"description":"Rust bindings generator and runtime support for WIT and the component model.\nUsed when compiling Rust programs to the component model.\n","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"rustc-std-workspace-alloc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"alloc","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"bitflags","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.11.1","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustc-std-workspace-core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":"core","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.30","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"wit-bindgen-rust-macro","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.57.1","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"wit_bindgen","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wit-bindgen-0.57.1/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wit-bindgen-0.57.1/build.rs","edition":"2024","doc":false,"doctest":false,"test":false}],"features":{"async":[],"async-spawn":["async","dep:futures","std"],"bitflags":["dep:bitflags"],"default":["macros","realloc","async","std","bitflags","macro-string"],"futures-stream":["async","dep:futures"],"inter-task-wakeup":["async"],"macro-string":["wit-bindgen-rust-macro?/macro-string"],"macros":["dep:wit-bindgen-rust-macro"],"realloc":[],"rustc-dep-of-std":["dep:core","dep:alloc"],"std":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wit-bindgen-0.57.1/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["Alex Crichton "],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/bytecodealliance/wit-bindgen","homepage":"https://github.com/bytecodealliance/wit-bindgen","documentation":null,"edition":"2024","links":null,"default_run":null,"rust_version":"1.85.0"},{"name":"writeable","version":"0.6.3","id":"registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","license":"Unicode-3.0","license_file":null,"description":"A more efficient alternative to fmt::Display","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"either","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.9.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["small_rng"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"writeable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"writeable_message","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/examples/writeable_message.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"writeable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/tests/writeable.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"writeable","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/benches/writeable.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"alloc":[],"default":["alloc"],"either":["dep:either"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":[],"keywords":["formatting","write","string","no-std"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"xattr","version":"1.6.1","id":"registry+https://github.com/rust-lang/crates.io-index#xattr@1.6.1","license":"MIT OR Apache-2.0","license_file":null,"description":"unix extended filesystem attributes","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"tempfile","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rustix","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["fs","std"],"target":"cfg(any(target_os = \"android\", target_os = \"linux\", target_os = \"macos\", target_os = \"hurd\"))","registry":null},{"name":"libc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.150","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(any(target_os = \"freebsd\", target_os = \"netbsd\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"xattr","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/xattr-1.6.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"main","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/xattr-1.6.1/tests/main.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["unsupported"],"unsupported":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/xattr-1.6.1/Cargo.toml","metadata":null,"publish":null,"authors":["Steven Allen "],"categories":[],"keywords":["xattr","filesystem","unix"],"readme":"README.md","repository":"https://github.com/Stebalien/xattr","homepage":null,"documentation":"https://docs.rs/xattr","edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"xtask","version":"3.0.4","id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask#3.0.4","license":null,"license_file":null,"description":null,"source":null,"dependencies":[{"name":"flate2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"sha2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10.9","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"tar","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"yaml_serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["bin"],"crate_types":["bin"],"name":"xtask","src_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask/src/main.rs","edition":"2021","doc":true,"doctest":false,"test":true}],"features":{},"manifest_path":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask/Cargo.toml","metadata":null,"publish":[],"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"yaml_serde","version":"0.10.7","id":"registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7","license":"MIT OR Apache-2.0","license_file":null,"description":"serde_yaml maintained by The YAML Organization","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"indexmap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.1","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"itoa","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"libyaml-rs","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ryu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.195","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"anyhow","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.79","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"indoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde_derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.195","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"yaml_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_de","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/tests/test_de.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_error","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/tests/test_error.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/tests/test_serde.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test_value","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/tests/test_value.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"default":["std"],"std":["serde/std","indexmap/std"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yaml_serde-0.10.7/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition"]}}},"publish":null,"authors":["YAML Organization "],"categories":["encoding","parser-implementations"],"keywords":["yaml","serde","data","serialization"],"readme":"README.md","repository":"https://github.com/yaml/yaml-serde","homepage":null,"documentation":"https://docs.rs/yaml_serde/","edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"yoke","version":"0.8.3","id":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","license":"Unicode-3.0","license_file":null,"description":"Abstraction allowing borrowed data to be carried along with the backing data it borrows from","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"stable_deref_trait","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"yoke-derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"yoke","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"bincode","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.3/tests/bincode.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"miri","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.3/tests/miri.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{"alloc":["stable_deref_trait/alloc","zerofrom/alloc"],"default":["alloc","zerofrom"],"derive":["dep:yoke-derive","zerofrom/derive"],"serde":[],"zerofrom":["dep:zerofrom"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.3/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["Manish Goregaokar "],"categories":["data-structures","memory-management","caching","no-std"],"keywords":["zero-copy","serialization","memory-efficiency","self-referential"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"yoke-derive","version":"0.8.2","id":"registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.8.2","license":"Unicode-3.0","license_file":null,"description":"Custom derive for the yoke crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.44","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.21","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["fold"],"target":null,"registry":null},{"name":"synstructure","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"yoke_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-derive-0.8.2/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"yoke_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-derive-0.8.2/examples/yoke_derive.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-derive-0.8.2/Cargo.toml","metadata":{"workspaces":{"independent":true}},"publish":null,"authors":["Manish Goregaokar "],"categories":["data-structures","memory-management","caching","no-std"],"keywords":["macros","derive","zero-copy","serialization","self-referential"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"zerofrom","version":"0.1.8","id":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","license":"Unicode-3.0","license_file":null,"description":"ZeroFrom trait for constructing","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"zerofrom-derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zerofrom","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-0.1.8/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"alloc":[],"default":["alloc"],"derive":["dep:zerofrom-derive"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-0.1.8/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"playground":{"all-features":true}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["data-structures","caching","no-std"],"keywords":["zero-copy","serialization","memory-efficiency"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.1"},{"name":"zerofrom-derive","version":"0.1.7","id":"registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.7","license":"Unicode-3.0","license_file":null,"description":"Custom derive for the zerofrom crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.44","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.21","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["fold"],"target":null,"registry":null},{"name":"synstructure","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.13.0","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"zerofrom_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-derive-0.1.7/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"zf_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-derive-0.1.7/examples/zf_derive.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerofrom-derive-0.1.7/Cargo.toml","metadata":{"workspaces":{"independent":true}},"publish":null,"authors":["Manish Goregaokar "],"categories":["data-structures","memory-management","caching","no-std"],"keywords":["macros","derive","zero-copy","serialization"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.71.1"},{"name":"zerotrie","version":"0.2.4","id":"registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","license":"Unicode-3.0","license_file":null,"description":"A data structure that efficiently maps strings to integers","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"displaydoc","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.3","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"litemap","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"serde_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.6","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"icu_locale_core","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"itertools","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.14.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["alloc"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_pcg","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rmp-serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zerotrie","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"first_weekday_for_region","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/examples/first_weekday_for_region.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["test"],"crate_types":["bin"],"name":"asciitrie_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/asciitrie_test.rs","edition":"2021","required-features":["alloc","litemap"],"doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"builder_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/builder_test.rs","edition":"2021","required-features":["alloc","litemap"],"doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"dense_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/dense_test.rs","edition":"2021","required-features":["alloc","dense"],"doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"derive_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/derive_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"ignorecase_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/ignorecase_test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"locale_aux_test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/tests/locale_aux_test.rs","edition":"2021","required-features":["alloc","litemap"],"doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"overview","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/benches/overview.rs","edition":"2021","required-features":["alloc","litemap"],"doc":false,"doctest":false,"test":false}],"features":{"alloc":["zerovec?/alloc"],"databake":["dep:databake","zerovec?/databake"],"default":[],"dense":["dep:zerovec"],"litemap":["dep:litemap","alloc"],"serde":["dep:serde_core","dep:litemap","alloc","litemap/serde","zerovec?/serde"],"yoke":["dep:yoke"],"zerofrom":["dep:zerofrom"],"zerovec":["dep:zerovec"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerotrie-0.2.4/Cargo.toml","metadata":{"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["internationalization","localization","no-std","embedded"],"keywords":["zero-copy","serialization","memory-efficiency","trie"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":"https://icu4x.unicode.org","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.82"},{"name":"zerovec","version":"0.11.6","id":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","license":"Unicode-3.0","license_file":null,"description":"Zero-copy vector backed by a byte array","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"databake","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.2.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"schemars","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.4","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"twox-hash","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.0","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":["xxhash64"],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerofrom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.6","kind":null,"rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"zerovec-derive","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.11.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"getrandom","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["wasm_js"],"target":null,"registry":null},{"name":"iai","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"insta","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.43.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["json"],"target":null,"registry":null},{"name":"postcard","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["use-std"],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_distr","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand_pcg","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rmp-serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.2.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"yoke","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8.2","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.5.0","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":"cfg(not(target_arch = \"wasm32\"))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zerovec","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"zv_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/examples/zv_serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"vzv","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/vzv.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"zeromap","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/zeromap.rs","edition":"2021","required-features":["serde","hashmap","derive"],"doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"zerovec","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/zerovec.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"zerovec_iai","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/zerovec_iai.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["bench"],"crate_types":["bin"],"name":"zerovec_serde","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/benches/zerovec_serde.rs","edition":"2021","required-features":["serde"],"doc":false,"doctest":false,"test":false}],"features":{"alloc":["serde?/alloc"],"databake":["dep:databake"],"derive":["dep:zerovec-derive"],"hashmap":["dep:twox-hash","alloc"],"schemars":["dep:schemars","alloc"],"serde":["dep:serde"],"std":[],"yoke":["dep:yoke"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/Cargo.toml","metadata":{"workspaces":{"independent":true},"docs":{"rs":{"all-features":true}},"cargo-all-features":{"max_combination_size":3}},"publish":null,"authors":["The ICU4X Project Developers"],"categories":["rust-patterns","memory-management","caching","no-std","data-structures"],"keywords":["zero-copy","serialization","memory-efficiency","serde"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.83"},{"name":"zerovec-derive","version":"0.11.3","id":"registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.11.3","license":"Unicode-3.0","license_file":null,"description":"Custom derive for the zerovec crate","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"proc-macro2","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.61","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quote","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.44","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"syn","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^2.0.21","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["extra-traits"],"target":null,"registry":null},{"name":"bincode","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"serde","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.220","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":["derive"],"target":null,"registry":null},{"name":"serde_json","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.45","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null}],"targets":[{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"zerovec_derive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"derives","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/examples/derives.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"make","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/examples/make.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["example"],"crate_types":["bin"],"name":"make_var","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/examples/make_var.rs","edition":"2021","doc":false,"doctest":false,"test":true}],"features":{},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-derive-0.11.3/Cargo.toml","metadata":{"workspaces":{"independent":true}},"publish":null,"authors":["Manish Goregaokar "],"categories":["rust-patterns","memory-management","caching","no-std","data-structures"],"keywords":["macros","derive","serialization","zero-copy"],"readme":"README.md","repository":"https://github.com/unicode-org/icu4x","homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"zlib-rs","version":"0.6.8","id":"registry+https://github.com/rust-lang/crates.io-index#zlib-rs@0.6.8","license":"Zlib","license_file":null,"description":"A memory-safe zlib implementation written in rust","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"arbitrary","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":["derive"],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":null,"rename":null,"optional":true,"uses_default_features":false,"features":[],"target":null,"registry":null},{"name":"crc32fast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.3.2","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"memoffset","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.9.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"quickcheck","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.0.3","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":null,"registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zlib_rs","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zlib-rs-0.6.8/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"ZLIB_DEBUG":[],"__internal-api":[],"__internal-fuzz":["arbitrary"],"__internal-fuzz-disable-checksum":[],"__internal-test":["quickcheck"],"arbitrary":["dep:arbitrary"],"avx512":["vpclmulqdq"],"c-allocator":[],"default":["std","c-allocator"],"lsx":[],"quickcheck":["dep:quickcheck"],"rust-allocator":[],"std":["rust-allocator"],"vpclmulqdq":[]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zlib-rs-0.6.8/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":"README.md","repository":"https://github.com/trifectatechfoundation/zlib-rs","homepage":"https://github.com/trifectatechfoundation/zlib-rs","documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":"1.75"},{"name":"zmij","version":"1.0.21","id":"registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.21","license":"MIT","license_file":null,"description":"A double-to-string conversion algorithm based on Schubfach and yy","source":"registry+https://github.com/rust-lang/crates.io-index","dependencies":[{"name":"no-panic","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1.36","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num-bigint","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.4","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num-integer","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"num_cpus","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1.8","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"opt-level","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"rand","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.10","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"ryu","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^1","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null},{"name":"criterion","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.8","kind":"dev","rename":null,"optional":false,"uses_default_features":false,"features":[],"target":"cfg(not(miri))","registry":null}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"zmij","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"exhaustive","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/tests/exhaustive.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"ryu_comparison","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/tests/ryu_comparison.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["test"],"crate_types":["bin"],"name":"test","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/tests/test.rs","edition":"2021","doc":false,"doctest":false,"test":true},{"kind":["bench"],"crate_types":["bin"],"name":"bench","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/benches/bench.rs","edition":"2021","doc":false,"doctest":false,"test":false},{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/build.rs","edition":"2021","doc":false,"doctest":false,"test":false}],"features":{"no-panic":["dep:no-panic"]},"manifest_path":"/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.21/Cargo.toml","metadata":{"docs":{"rs":{"targets":["x86_64-unknown-linux-gnu"],"rustdoc-args":["--generate-link-to-definition","--generate-macro-expansion","--extern-html-root-url=core=https://doc.rust-lang.org"]}}},"publish":null,"authors":["David Tolnay "],"categories":["value-formatting","no-std","no-std::no-alloc"],"keywords":["float"],"readme":"README.md","repository":"https://github.com/dtolnay/zmij","homepage":null,"documentation":"https://docs.rs/zmij","edition":"2021","links":null,"default_run":null,"rust_version":"1.71"}],"workspace_members":["path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki#3.0.4","path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask#3.0.4"],"workspace_default_members":["path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4"],"resolve":{"nodes":[{"id":"registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2"],"deps":[{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]}],"features":["perf-literal","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#ammonia@4.1.8","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cssparser@0.37.0","registry+https://github.com/rust-lang/crates.io-index#html5ever@0.39.0","registry+https://github.com/rust-lang/crates.io-index#maplit@1.0.2","registry+https://github.com/rust-lang/crates.io-index#url@2.5.8"],"deps":[{"name":"cssparser","pkg":"registry+https://github.com/rust-lang/crates.io-index#cssparser@0.37.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"html5ever","pkg":"registry+https://github.com/rust-lang/crates.io-index#html5ever@0.39.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"maplit","pkg":"registry+https://github.com/rust-lang/crates.io-index#maplit@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"url","pkg":"registry+https://github.com/rust-lang/crates.io-index#url@2.5.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#axum@0.8.9","dependencies":["registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.6","registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.20","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#matchit@0.8.4","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.20","registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","registry+https://github.com/rust-lang/crates.io-index#tower@0.5.3","registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3"],"deps":[{"name":"axum_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.6","dep_kinds":[{"kind":null,"target":null}]},{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"hyper","pkg":"registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"hyper_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.20","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"matchit","pkg":"registry+https://github.com/rust-lang/crates.io-index#matchit@0.8.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"mime","pkg":"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"percent_encoding","pkg":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_path_to_error","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.20","dep_kinds":[{"kind":null,"target":null}]},{"name":"sync_wrapper","pkg":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower@0.5.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_layer","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_service","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["http1","json","tokio"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.6","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"mime","pkg":"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"sync_wrapper","pkg":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_layer","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_service","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dependencies":[],"deps":[],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7"],"deps":[{"name":"generic_array","pkg":"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.20.3","dependencies":[],"deps":[],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dependencies":["registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9","registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1"],"deps":[{"name":"find_msvc_tools","pkg":"registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"shlex","pkg":"registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cmake@0.1.58","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#fs2@0.4.3","registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4","registry+https://github.com/rust-lang/crates.io-index#lbug@0.19.0","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","registry+https://github.com/rust-lang/crates.io-index#lzma-rs@0.3.0","registry+https://github.com/rust-lang/crates.io-index#mimalloc@0.1.52","registry+https://github.com/rust-lang/crates.io-index#notify@8.2.0","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","registry+https://github.com/rust-lang/crates.io-index#tree-sitter@0.25.10","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-c@0.24.2","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-cpp@0.23.4","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-css@0.25.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-fortran@0.6.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-go@0.25.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-html@0.23.2","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-javascript@0.25.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-python@0.25.0","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-rust@0.24.2","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-typescript@0.23.2","registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"fs2","pkg":"registry+https://github.com/rust-lang/crates.io-index#fs2@0.4.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"getrandom","pkg":"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"lbug","pkg":"registry+https://github.com/rust-lang/crates.io-index#lbug@0.19.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]},{"name":"libmimalloc_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","dep_kinds":[{"kind":null,"target":null}]},{"name":"lzma_rs","pkg":"registry+https://github.com/rust-lang/crates.io-index#lzma-rs@0.3.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"mimalloc","pkg":"registry+https://github.com/rust-lang/crates.io-index#mimalloc@0.1.52","dep_kinds":[{"kind":null,"target":null}]},{"name":"notify","pkg":"registry+https://github.com/rust-lang/crates.io-index#notify@8.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"sha2","pkg":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter@0.25.10","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_c","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-c@0.24.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_cpp","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-cpp@0.23.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_css","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-css@0.25.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_fortran","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-fortran@0.6.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_go","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-go@0.25.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_html","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-html@0.23.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_javascript","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-javascript@0.25.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_python","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-python@0.25.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_rust","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-rust@0.24.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_typescript","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-typescript@0.23.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"walkdir","pkg":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#codespan-reporting@0.11.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1","registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.14"],"deps":[{"name":"termcolor","pkg":"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"unicode_width","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.14","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"aarch64-linux-android"},{"kind":null,"target":"cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))"},{"kind":null,"target":"cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))"},{"kind":null,"target":"cfg(all(target_arch = \"loongarch64\", target_os = \"linux\"))"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.5.0"],"deps":[{"name":"crc_catalog","pkg":"registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.5.0","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.5.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]}],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1"],"deps":[{"name":"generic_array","pkg":"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"typenum","pkg":"registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","dep_kinds":[{"kind":null,"target":null}]}],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cssparser@0.37.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#dtoa-short@0.3.5","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2"],"deps":[{"name":"dtoa_short","pkg":"registry+https://github.com/rust-lang/crates.io-index#dtoa-short@0.3.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cty@0.2.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cxx@1.0.138","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#cxxbridge-flags@1.0.138","registry+https://github.com/rust-lang/crates.io-index#cxxbridge-macro@1.0.138","registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5","registry+https://github.com/rust-lang/crates.io-index#link-cplusplus@1.0.12"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"cxxbridge_flags","pkg":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-flags@1.0.138","dep_kinds":[{"kind":"build","target":null}]},{"name":"cxxbridge_macro","pkg":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-macro@1.0.138","dep_kinds":[{"kind":null,"target":null}]},{"name":"foldhash","pkg":"registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"link_cplusplus","pkg":"registry+https://github.com/rust-lang/crates.io-index#link-cplusplus@1.0.12","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cxx-build@1.0.138","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#codespan-reporting@0.11.1","registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#scratch@1.0.9","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":null,"target":null}]},{"name":"codespan_reporting","pkg":"registry+https://github.com/rust-lang/crates.io-index#codespan-reporting@0.11.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"scratch","pkg":"registry+https://github.com/rust-lang/crates.io-index#scratch@1.0.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-flags@1.0.138","dependencies":[],"deps":[],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#cxxbridge-macro@1.0.138","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"rustversion","pkg":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11","dependencies":["registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0"],"deps":[{"name":"powerfmt","pkg":"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","powerfmt","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4","registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.7"],"deps":[{"name":"block_buffer","pkg":"registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"crypto_common","pkg":"registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.7","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","block-buffer","core-api","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#dtoa@1.0.11","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#dtoa-short@0.3.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#dtoa@1.0.11"],"deps":[{"name":"dtoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#dtoa@1.0.11","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"},{"kind":null,"target":"cfg(target_os = \"hermit\")"},{"kind":null,"target":"cfg(target_os = \"wasi\")"}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.5.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#filetime@0.2.29","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.10","dependencies":["registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0","registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.9.1","registry+https://github.com/rust-lang/crates.io-index#zlib-rs@0.6.8"],"deps":[{"name":"crc32fast","pkg":"registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"miniz_oxide","pkg":"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.9.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"zlib_rs","pkg":"registry+https://github.com/rust-lang/crates.io-index#zlib-rs@0.6.8","dep_kinds":[{"kind":null,"target":null}]}],"features":["any_impl","default","miniz_oxide","runtime_detection","rust_backend"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5","dependencies":[],"deps":[],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2"],"deps":[{"name":"percent_encoding","pkg":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#fs2@0.4.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]},{"name":"winapi","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#fsevent-sys@4.1.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.32","dependencies":["registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32"],"deps":[{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dependencies":[],"deps":[],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.32","dependencies":[],"deps":[],"features":["alloc","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","dependencies":["registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.32","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#slab@0.4.12"],"deps":[{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_task","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"slab","pkg":"registry+https://github.com/rust-lang/crates.io-index#slab@0.4.12","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","slab","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5"],"deps":[{"name":"typenum","pkg":"registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"version_check","pkg":"registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5","dep_kinds":[{"kind":"build","target":null}]}],"features":["more_lengths"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0","registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(all(any(target_os = \"linux\", target_os = \"android\"), not(any(all(target_os = \"linux\", target_env = \"\"), getrandom_backend = \"custom\", getrandom_backend = \"linux_raw\", getrandom_backend = \"rdrand\", getrandom_backend = \"rndr\"))))"},{"kind":null,"target":"cfg(any(target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"hurd\", target_os = \"illumos\", target_os = \"cygwin\", all(target_os = \"horizon\", target_arch = \"arm\")))"},{"kind":null,"target":"cfg(any(target_os = \"haiku\", target_os = \"redox\", target_os = \"nto\", target_os = \"aix\"))"},{"kind":null,"target":"cfg(any(target_os = \"ios\", target_os = \"visionos\", target_os = \"watchos\", target_os = \"tvos\"))"},{"kind":null,"target":"cfg(any(target_os = \"macos\", target_os = \"openbsd\", target_os = \"vita\", target_os = \"emscripten\"))"},{"kind":null,"target":"cfg(target_os = \"netbsd\")"},{"kind":null,"target":"cfg(target_os = \"solaris\")"},{"kind":null,"target":"cfg(target_os = \"vxworks\")"}]},{"name":"r_efi","pkg":"registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0","dep_kinds":[{"kind":null,"target":"cfg(all(target_os = \"uefi\", getrandom_backend = \"efi_rng\"))"}]},{"name":"wasip2","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"wasm32\", target_os = \"wasi\", target_env = \"p2\"))"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.5","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#html5ever@0.39.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","registry+https://github.com/rust-lang/crates.io-index#markup5ever@0.39.0"],"deps":[{"name":"log","pkg":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"markup5ever","pkg":"registry+https://github.com/rust-lang/crates.io-index#markup5ever@0.39.0","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2","registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.32","registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1","registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1"],"deps":[{"name":"atomic_waker","pkg":"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_channel","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"httparse","pkg":"registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"httpdate","pkg":"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","http1","server"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.20","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"http","pkg":"registry+https://github.com/rust-lang/crates.io-index#http@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"http_body","pkg":"registry+https://github.com/rust-lang/crates.io-index#http-body@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"hyper","pkg":"registry+https://github.com/rust-lang/crates.io-index#hyper@1.11.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_service","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","http1","server","service","tokio"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#potential_utf@0.1.5","registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"potential_utf","pkg":"registry+https://github.com/rust-lang/crates.io-index#potential_utf@0.1.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"utf8_iter","pkg":"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"yoke","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#litemap@0.8.2","registry+https://github.com/rust-lang/crates.io-index#tinystr@0.8.3","registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"litemap","pkg":"registry+https://github.com/rust-lang/crates.io-index#litemap@0.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tinystr","pkg":"registry+https://github.com/rust-lang/crates.io-index#tinystr@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"writeable","pkg":"registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["zerovec"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"icu_collections","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_normalizer_data","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_properties","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_provider","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["compiled_data"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@2.2.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"icu_collections","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_locale_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_properties_data","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_provider","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerotrie","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["compiled_data"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@2.2.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#icu_provider@2.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_locale_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_locale_core@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"writeable","pkg":"registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"yoke","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerotrie","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["baked"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.2","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4"],"deps":[{"name":"idna_adapter","pkg":"registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"utf8_iter","pkg":"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","compiled_data","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@2.2.0","registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0"],"deps":[{"name":"icu_normalizer","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@2.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"icu_properties","pkg":"registry+https://github.com/rust-lang/crates.io-index#icu_properties@2.2.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["compiled_data"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.5"],"deps":[{"name":"equivalent","pkg":"registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"hashbrown","pkg":"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.5","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#inotify@0.11.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#inotify-sys@0.1.5","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"inotify_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#inotify-sys@0.1.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#inotify-sys@0.1.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.102","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasm_bindgen","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125","dep_kinds":[{"kind":null,"target":null}]}],"features":["std"]},{"id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/k-wiki#3.0.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#ammonia@4.1.8","registry+https://github.com/rust-lang/crates.io-index#axum@0.8.9","path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark@0.13.4","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7"],"deps":[{"name":"ammonia","pkg":"registry+https://github.com/rust-lang/crates.io-index#ammonia@4.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"axum","pkg":"registry+https://github.com/rust-lang/crates.io-index#axum@0.8.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"codebase_graph","pkg":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"pulldown_cmark","pkg":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark@0.13.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"sha2","pkg":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"walkdir","pkg":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"yaml_serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#kqueue@1.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#kqueue-sys@1.1.2","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"kqueue_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#kqueue-sys@1.1.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#kqueue-sys@1.1.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#lbug@0.19.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cmake@0.1.58","registry+https://github.com/rust-lang/crates.io-index#cxx@1.0.138","registry+https://github.com/rust-lang/crates.io-index#cxx-build@1.0.138","registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.42.1","registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#time@0.3.36","registry+https://github.com/rust-lang/crates.io-index#uuid@1.18.1"],"deps":[{"name":"cmake","pkg":"registry+https://github.com/rust-lang/crates.io-index#cmake@0.1.58","dep_kinds":[{"kind":"build","target":null}]},{"name":"cxx","pkg":"registry+https://github.com/rust-lang/crates.io-index#cxx@1.0.138","dep_kinds":[{"kind":null,"target":null}]},{"name":"cxx_build","pkg":"registry+https://github.com/rust-lang/crates.io-index#cxx-build@1.0.138","dep_kinds":[{"kind":"build","target":null}]},{"name":"rust_decimal","pkg":"registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.42.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"rustversion","pkg":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","dep_kinds":[{"kind":"build","target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"time","pkg":"registry+https://github.com/rust-lang/crates.io-index#time@0.3.36","dep_kinds":[{"kind":null,"target":null}]},{"name":"uuid","pkg":"registry+https://github.com/rust-lang/crates.io-index#uuid@1.18.1","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#cty@0.2.2"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"cty","pkg":"registry+https://github.com/rust-lang/crates.io-index#cty@0.2.2","dep_kinds":[{"kind":null,"target":null}]}],"features":["cty","extended"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#libyaml-rs@0.3.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#link-cplusplus@1.0.12","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.12.1","dependencies":[],"deps":[],"features":["auxvec","elf","errno","general","ioctl","no_std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#litemap@0.8.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.14","dependencies":["registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0"],"deps":[{"name":"scopeguard","pkg":"registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["atomic_usize","default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#lzma-rs@0.3.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0","registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0"],"deps":[{"name":"byteorder","pkg":"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"crc","pkg":"registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#maplit@1.0.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#markup5ever@0.39.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","registry+https://github.com/rust-lang/crates.io-index#tendril@0.5.1","registry+https://github.com/rust-lang/crates.io-index#web_atoms@0.2.6"],"deps":[{"name":"log","pkg":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"tendril","pkg":"registry+https://github.com/rust-lang/crates.io-index#tendril@0.5.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"web_atoms","pkg":"registry+https://github.com/rust-lang/crates.io-index#web_atoms@0.2.6","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#matchit@0.8.4","dependencies":[],"deps":[],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dependencies":[],"deps":[],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#mimalloc@0.1.52","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49"],"deps":[{"name":"libmimalloc_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#libmimalloc-sys@0.1.49","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","extended"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.9.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1","registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.10"],"deps":[{"name":"adler2","pkg":"registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"simd_adler32","pkg":"registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.10","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","simd","simd-adler32","with-alloc"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.1+wasi-snapshot-preview1","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(any(unix, target_os = \"hermit\", target_os = \"wasi\"))"}]},{"name":"log","pkg":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasi","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.1+wasi-snapshot-preview1","dep_kinds":[{"kind":null,"target":"cfg(target_os = \"wasi\")"}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["default","log","net","os-ext","os-poll"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#notify@8.2.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#fsevent-sys@4.1.0","registry+https://github.com/rust-lang/crates.io-index#inotify@0.11.2","registry+https://github.com/rust-lang/crates.io-index#kqueue@1.2.0","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","registry+https://github.com/rust-lang/crates.io-index#notify-types@2.0.0","registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.60.2"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":"cfg(target_os = \"macos\")"}]},{"name":"fsevent_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#fsevent-sys@4.1.0","dep_kinds":[{"kind":null,"target":"cfg(target_os = \"macos\")"}]},{"name":"inotify","pkg":"registry+https://github.com/rust-lang/crates.io-index#inotify@0.11.2","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"linux\", target_os = \"android\"))"}]},{"name":"kqueue","pkg":"registry+https://github.com/rust-lang/crates.io-index#kqueue@1.2.0","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonflybsd\", target_os = \"ios\"))"}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]},{"name":"log","pkg":"registry+https://github.com/rust-lang/crates.io-index#log@0.4.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"mio","pkg":"registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonflybsd\", target_os = \"ios\"))"},{"kind":null,"target":"cfg(any(target_os = \"linux\", target_os = \"android\"))"}]},{"name":"notify_types","pkg":"registry+https://github.com/rust-lang/crates.io-index#notify-types@2.0.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"walkdir","pkg":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.60.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["default","fsevent-sys","macos_fsevent"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#notify-types@2.0.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#num-conv@0.1.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19","dependencies":["registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1"],"deps":[{"name":"autocfg","pkg":"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1","dep_kinds":[{"kind":"build","target":null}]}],"features":["i128"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.14","registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12"],"deps":[{"name":"lock_api","pkg":"registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.14","dep_kinds":[{"kind":null,"target":null}]},{"name":"parking_lot_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.5.18","registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]},{"name":"syscall","pkg":"registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.5.18","dep_kinds":[{"kind":null,"target":"cfg(target_os = \"redox\")"}]},{"name":"smallvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"windows_link","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","dependencies":[],"deps":[],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#phf@0.13.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228"],"deps":[{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.13.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1"],"deps":[{"name":"phf_generator","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#fastrand@2.5.0","registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1"],"deps":[{"name":"fastrand","pkg":"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.5.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#siphasher@1.0.3"],"deps":[{"name":"siphasher","pkg":"registry+https://github.com/rust-lang/crates.io-index#siphasher@1.0.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#potential_utf@0.1.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["zerovec"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#precomputed-hash@0.1.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dependencies":["registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"],"deps":[{"name":"unicode_ident","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","proc-macro","span-locations"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark@0.13.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark-escape@0.11.0","registry+https://github.com/rust-lang/crates.io-index#unicase@2.9.0"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"pulldown_cmark_escape","pkg":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark-escape@0.11.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"unicase","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicase@2.9.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["html","pulldown-cmark-escape"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#pulldown-cmark-escape@0.11.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","proc-macro"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.5.18","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","userspace"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#regex@1.12.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.14","registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11"],"deps":[{"name":"aho_corasick","pkg":"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"regex_automata","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.14","dep_kinds":[{"kind":null,"target":null}]},{"name":"regex_syntax","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","dep_kinds":[{"kind":null,"target":null}]}],"features":["perf","perf-backtrack","perf-cache","perf-dfa","perf-inline","perf-literal","perf-onepass","std","unicode","unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.14","dependencies":["registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11"],"deps":[{"name":"aho_corasick","pkg":"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"regex_syntax","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","dfa-onepass","hybrid","meta","nfa-backtrack","nfa-pikevm","nfa-thompson","perf-inline","perf-literal","perf-literal-multisubstring","perf-literal-substring","std","syntax","unicode","unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment","unicode-word-boundary"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","dependencies":[],"deps":[],"features":["std","unicode","unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.42.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6","registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19"],"deps":[{"name":"arrayvec","pkg":"registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6","dep_kinds":[{"kind":null,"target":null}]},{"name":"num_traits","pkg":"registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.12.1","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"bitflags","pkg":"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc_errno","pkg":"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","dep_kinds":[{"kind":null,"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))"},{"kind":null,"target":"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))"},{"kind":null,"target":"cfg(windows)"}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))"},{"kind":null,"target":"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))"}]},{"name":"linux_raw_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.12.1","dep_kinds":[{"kind":null,"target":"cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\"))))"},{"kind":null,"target":"cfg(all(any(target_os = \"linux\", target_os = \"android\"), any(rustix_use_libc, miri, not(all(target_os = \"linux\", any(target_endian = \"little\", any(target_arch = \"s390x\", target_arch = \"powerpc\")), any(target_arch = \"arm\", all(target_arch = \"aarch64\", target_pointer_width = \"64\"), target_arch = \"riscv64\", all(rustix_use_experimental_asm, target_arch = \"powerpc\"), all(rustix_use_experimental_asm, target_arch = \"powerpc64\"), all(rustix_use_experimental_asm, target_arch = \"s390x\"), all(rustix_use_experimental_asm, target_arch = \"mips\"), all(rustix_use_experimental_asm, target_arch = \"mips32r6\"), all(rustix_use_experimental_asm, target_arch = \"mips64\"), all(rustix_use_experimental_asm, target_arch = \"mips64r6\"), target_arch = \"x86\", all(target_arch = \"x86_64\", target_pointer_width = \"64\")))))))"}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["alloc","fs","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.23","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#same-file@1.0.6","dependencies":["registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11"],"deps":[{"name":"winapi_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#scratch@1.0.9","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dependencies":["registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228"],"deps":[{"name":"serde_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","default","derive","serde_derive","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dependencies":["registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228"],"deps":[{"name":"serde_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","dep_kinds":[{"kind":null,"target":"cfg(any())"}]}],"features":["alloc","default","result","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":["default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dependencies":["registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.21"],"deps":[{"name":"indexmap","pkg":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"memchr","pkg":"registry+https://github.com/rust-lang/crates.io-index#memchr@2.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":"cfg(any())"}]},{"name":"serde_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"zmij","pkg":"registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.21","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","indexmap","preserve_order","raw_value","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.20","dependencies":["registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228"],"deps":[{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":"cfg(any())"}]},{"name":"serde_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17","registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"cpufeatures","pkg":"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17","dep_kinds":[{"kind":null,"target":"cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))"}]},{"name":"digest","pkg":"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.8","dependencies":["registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"],"deps":[{"name":"errno","pkg":"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.10","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#siphasher@1.0.3","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#slab@0.4.12","dependencies":[],"deps":[],"features":["std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2","dependencies":[],"deps":[],"features":["const_generics","const_new"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(any(unix, target_os = \"wasi\"))"}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["all"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#streaming-iterator@0.1.9","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#string_cache@0.9.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5","registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","registry+https://github.com/rust-lang/crates.io-index#precomputed-hash@0.1.1"],"deps":[{"name":"debug_unreachable","pkg":"registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","dep_kinds":[{"kind":null,"target":null}]},{"name":"parking_lot","pkg":"registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"precomputed_hash","pkg":"registry+https://github.com/rust-lang/crates.io-index#precomputed-hash@0.1.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#string_cache_codegen@0.6.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45"],"deps":[{"name":"phf_generator","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"unicode_ident","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dep_kinds":[{"kind":null,"target":null}]}],"features":["clone-impls","default","derive","extra-traits","fold","full","parsing","printing","proc-macro","visit","visit-mut"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"unicode_ident","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dep_kinds":[{"kind":null,"target":null}]}],"features":["clone-impls","default","derive","full","parsing","printing","proc-macro"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","proc-macro"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tar@0.4.46","dependencies":["registry+https://github.com/rust-lang/crates.io-index#filetime@0.2.29","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#xattr@1.6.1"],"deps":[{"name":"filetime","pkg":"registry+https://github.com/rust-lang/crates.io-index#filetime@0.2.29","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]},{"name":"xattr","pkg":"registry+https://github.com/rust-lang/crates.io-index#xattr@1.6.1","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]}],"features":["default","xattr"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tendril@0.5.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6"],"deps":[{"name":"debug_unreachable","pkg":"registry+https://github.com/rust-lang/crates.io-index#new_debug_unreachable@1.0.6","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11"],"deps":[{"name":"winapi_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#time@0.3.36","dependencies":["registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11","registry+https://github.com/rust-lang/crates.io-index#num-conv@0.1.0","registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2"],"deps":[{"name":"deranged","pkg":"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11","dep_kinds":[{"kind":null,"target":null}]},{"name":"num_conv","pkg":"registry+https://github.com/rust-lang/crates.io-index#num-conv@0.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"powerfmt","pkg":"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"time_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2","dep_kinds":[{"kind":null,"target":null}]}],"features":["alloc","default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tinystr@0.8.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dep_kinds":[{"kind":null,"target":null}]}],"features":["zerovec"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.8","registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.5","registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.7.2","registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"bytes","pkg":"registry+https://github.com/rust-lang/crates.io-index#bytes@1.12.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(all(tokio_unstable, target_os = \"linux\"))"},{"kind":null,"target":"cfg(unix)"},{"kind":null,"target":"cfg(target_os = \"wasi\")"}]},{"name":"mio","pkg":"registry+https://github.com/rust-lang/crates.io-index#mio@1.2.1","dep_kinds":[{"kind":null,"target":null},{"kind":null,"target":"cfg(all(tokio_unstable, target_os = \"linux\"))"}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"signal_hook_registry","pkg":"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.8","dep_kinds":[{"kind":null,"target":"cfg(unix)"}]},{"name":"socket2","pkg":"registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.5","dep_kinds":[{"kind":null,"target":"cfg(any(not(target_family = \"wasm\"), all(target_os = \"wasi\", not(target_env = \"p1\"))))"}]},{"name":"tokio_macros","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.7.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":["bytes","default","io-util","libc","macros","mio","net","rt","rt-multi-thread","signal","signal-hook-registry","socket2","sync","time","tokio-macros","windows-sys"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.7.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@3.0.3","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tower@0.5.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3"],"deps":[{"name":"futures_core","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"futures_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.32","dep_kinds":[{"kind":null,"target":null}]},{"name":"pin_project_lite","pkg":"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.17","dep_kinds":[{"kind":null,"target":null}]},{"name":"sync_wrapper","pkg":"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"tokio","pkg":"registry+https://github.com/rust-lang/crates.io-index#tokio@1.53.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_layer","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"tower_service","pkg":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["futures-core","futures-util","make","pin-project-lite","sync_wrapper","tokio","util"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter@0.25.10","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#regex@1.12.4","registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#streaming-iterator@0.1.9","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"regex","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex@1.12.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"regex_syntax","pkg":"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.11","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":"build","target":null}]},{"name":"streaming_iterator","pkg":"registry+https://github.com/rust-lang/crates.io-index#streaming-iterator@0.1.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-c@0.24.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-cpp@0.23.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-css@0.25.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-fortran@0.6.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-go@0.25.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-html@0.23.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-javascript@0.25.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-python@0.25.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-rust@0.24.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-typescript@0.23.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8"],"deps":[{"name":"cc","pkg":"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64","dep_kinds":[{"kind":"build","target":null}]},{"name":"tree_sitter_language","pkg":"registry+https://github.com/rust-lang/crates.io-index#tree-sitter-language@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#typenum@1.20.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#unicase@2.9.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.14","dependencies":[],"deps":[],"features":["cjk","default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#url@2.5.8","dependencies":["registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2","registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0","registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228"],"deps":[{"name":"form_urlencoded","pkg":"registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"idna","pkg":"registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"percent_encoding","pkg":"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#uuid@1.18.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.102","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125"],"deps":[{"name":"js_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.102","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"), target_feature = \"atomics\"))"}]},{"name":"wasm_bindgen","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))"}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#walkdir@2.5.0","dependencies":["registry+https://github.com/rust-lang/crates.io-index#same-file@1.0.6","registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11"],"deps":[{"name":"same_file","pkg":"registry+https://github.com/rust-lang/crates.io-index#same-file@1.0.6","dep_kinds":[{"kind":null,"target":null}]},{"name":"winapi_util","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.1+wasi-snapshot-preview1","dependencies":[],"deps":[],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12","dependencies":["registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1"],"deps":[{"name":"wit_bindgen","pkg":"registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.125","dependencies":["registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4","registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.125","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125"],"deps":[{"name":"cfg_if","pkg":"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"once_cell","pkg":"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4","dep_kinds":[{"kind":null,"target":null}]},{"name":"rustversion_compat","pkg":"registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22","dep_kinds":[{"kind":"build","target":null}]},{"name":"wasm_bindgen_macro","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.125","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasm_bindgen_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125","dep_kinds":[{"kind":null,"target":null}]}],"features":["msrv","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.125","dependencies":["registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.125"],"deps":[{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasm_bindgen_macro_support","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.125","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.125","dependencies":["registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.20.3","registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125"],"deps":[{"name":"bumpalo","pkg":"registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.20.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]},{"name":"wasm_bindgen_shared","pkg":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.125","dependencies":["registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"],"deps":[{"name":"unicode_ident","pkg":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#web_atoms@0.2.6","dependencies":["registry+https://github.com/rust-lang/crates.io-index#phf@0.13.1","registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.13.1","registry+https://github.com/rust-lang/crates.io-index#string_cache@0.9.0","registry+https://github.com/rust-lang/crates.io-index#string_cache_codegen@0.6.1"],"deps":[{"name":"phf","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf@0.13.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"phf_codegen","pkg":"registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.13.1","dep_kinds":[{"kind":"build","target":null}]},{"name":"string_cache","pkg":"registry+https://github.com/rust-lang/crates.io-index#string_cache@0.9.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"string_cache_codegen","pkg":"registry+https://github.com/rust-lang/crates.io-index#string_cache_codegen@0.6.1","dep_kinds":[{"kind":"build","target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9","dependencies":["registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0","registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0"],"deps":[{"name":"winapi_i686_pc_windows_gnu","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0","dep_kinds":[{"kind":null,"target":"i686-pc-windows-gnu"}]},{"name":"winapi_x86_64_pc_windows_gnu","pkg":"registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0","dep_kinds":[{"kind":null,"target":"x86_64-pc-windows-gnu"}]}],"features":["fileapi","handleapi","processthreadsapi","std","winbase","winerror"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.11","dependencies":["registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2"],"deps":[{"name":"windows_sys","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dep_kinds":[{"kind":null,"target":"cfg(windows)"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.60.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.53.5"],"deps":[{"name":"windows_targets","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.53.5","dep_kinds":[{"kind":null,"target":null}]}],"features":["Win32","Win32_Foundation","Win32_Security","Win32_Storage","Win32_Storage_FileSystem","Win32_System","Win32_System_IO","Win32_System_Threading","Win32_System_WindowsProgramming","default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.61.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1"],"deps":[{"name":"windows_link","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","dep_kinds":[{"kind":null,"target":null}]}],"features":["Wdk","Wdk_Foundation","Wdk_Storage","Wdk_Storage_FileSystem","Wdk_System","Wdk_System_IO","Win32","Win32_Foundation","Win32_Networking","Win32_Networking_WinSock","Win32_Security","Win32_Security_Authorization","Win32_Storage","Win32_Storage_FileSystem","Win32_System","Win32_System_Console","Win32_System_Diagnostics","Win32_System_Diagnostics_Debug","Win32_System_Diagnostics_ToolHelp","Win32_System_IO","Win32_System_JobObjects","Win32_System_Memory","Win32_System_Pipes","Win32_System_ProcessStatus","Win32_System_SystemInformation","Win32_System_SystemServices","Win32_System_Threading","Win32_System_WindowsProgramming","default"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.53.5","dependencies":["registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnullvm@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.53.1","registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.53.1"],"deps":[{"name":"windows_link","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows-link@0.2.1","dep_kinds":[{"kind":null,"target":"cfg(windows_raw_dylib)"}]},{"name":"windows_aarch64_gnullvm","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.53.1","dep_kinds":[{"kind":null,"target":"aarch64-pc-windows-gnullvm"}]},{"name":"windows_aarch64_msvc","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))"}]},{"name":"windows_i686_gnu","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"x86\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))"}]},{"name":"windows_i686_gnullvm","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnullvm@0.53.1","dep_kinds":[{"kind":null,"target":"i686-pc-windows-gnullvm"}]},{"name":"windows_i686_msvc","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"x86\", target_env = \"msvc\", not(windows_raw_dylib)))"}]},{"name":"windows_x86_64_gnu","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(target_arch = \"x86_64\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))"}]},{"name":"windows_x86_64_gnullvm","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.53.1","dep_kinds":[{"kind":null,"target":"x86_64-pc-windows-gnullvm"}]},{"name":"windows_x86_64_msvc","pkg":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.53.1","dep_kinds":[{"kind":null,"target":"cfg(all(any(target_arch = \"x86_64\", target_arch = \"arm64ec\"), target_env = \"msvc\", not(windows_raw_dylib)))"}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnullvm@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.53.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#writeable@0.6.3","dependencies":[],"deps":[],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#xattr@1.6.1","dependencies":["registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.4"],"deps":[{"name":"libc","pkg":"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"freebsd\", target_os = \"netbsd\"))"}]},{"name":"rustix","pkg":"registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.4","dep_kinds":[{"kind":null,"target":"cfg(any(target_os = \"android\", target_os = \"linux\", target_os = \"macos\", target_os = \"hurd\"))"}]}],"features":["default","unsupported"]},{"id":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask#3.0.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.10","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","registry+https://github.com/rust-lang/crates.io-index#tar@0.4.46","registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7"],"deps":[{"name":"flate2","pkg":"registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.10","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde_json","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.150","dep_kinds":[{"kind":null,"target":null}]},{"name":"sha2","pkg":"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9","dep_kinds":[{"kind":null,"target":null}]},{"name":"tar","pkg":"registry+https://github.com/rust-lang/crates.io-index#tar@0.4.46","dep_kinds":[{"kind":null,"target":null}]},{"name":"yaml_serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#yaml_serde@0.10.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","registry+https://github.com/rust-lang/crates.io-index#libyaml-rs@0.3.0","registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.23","registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228"],"deps":[{"name":"indexmap","pkg":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"itoa","pkg":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18","dep_kinds":[{"kind":null,"target":null}]},{"name":"libyaml_rs","pkg":"registry+https://github.com/rust-lang/crates.io-index#libyaml-rs@0.3.0","dep_kinds":[{"kind":null,"target":null}]},{"name":"ryu","pkg":"registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.23","dep_kinds":[{"kind":null,"target":null}]},{"name":"serde","pkg":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1","registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.8.2","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8"],"deps":[{"name":"stable_deref_trait","pkg":"registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1","dep_kinds":[{"kind":null,"target":null}]},{"name":"yoke_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.8.2","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":["derive","zerofrom"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.8.2","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]},{"name":"synstructure","pkg":"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dependencies":["registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.7"],"deps":[{"name":"zerofrom_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.7","dep_kinds":[{"kind":null,"target":null}]}],"features":["derive"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.7","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]},{"name":"synstructure","pkg":"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.2","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerotrie@0.2.4","dependencies":["registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8"],"deps":[{"name":"displaydoc","pkg":"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.7","dep_kinds":[{"kind":null,"target":null}]},{"name":"yoke","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]}],"features":["yoke","zerofrom"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerovec@0.11.6","dependencies":["registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.11.3"],"deps":[{"name":"yoke","pkg":"registry+https://github.com/rust-lang/crates.io-index#yoke@0.8.3","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerofrom","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.8","dep_kinds":[{"kind":null,"target":null}]},{"name":"zerovec_derive","pkg":"registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.11.3","dep_kinds":[{"kind":null,"target":null}]}],"features":["derive","yoke"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.11.3","dependencies":["registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"],"deps":[{"name":"proc_macro2","pkg":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106","dep_kinds":[{"kind":null,"target":null}]},{"name":"quote","pkg":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45","dep_kinds":[{"kind":null,"target":null}]},{"name":"syn","pkg":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zlib-rs@0.6.8","dependencies":[],"deps":[],"features":["rust-allocator","std"]},{"id":"registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.21","dependencies":[],"deps":[],"features":[]}],"root":"path+file:///private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo#codebase-graph@3.0.4"},"target_directory":"/Users/rabii/Projects/Repositories/codebaseGraph/target","build_directory":"/Users/rabii/Projects/Repositories/codebaseGraph/target","version":1,"workspace_root":"/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo","metadata":null} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr new file mode 100644 index 00000000..a6684489 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr @@ -0,0 +1,7 @@ +──────────── + Nextest run ID 5f3b8e10-9d6f-414b-a4ec-6a910655d899 with nextest profile: cleanup-s5 + Starting 2 tests across 1 binary + PASS [ 0.024s] (1/2) codebase-graph::storage_maintenance health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown + PASS [ 0.018s] (2/2) codebase-graph::storage_maintenance nonboolean_quiescence_confirmation_is_rejected_before_repository_effects +──────────── + Summary [ 0.042s] 2 tests run: 2 passed, 0 skipped diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stdout b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stdout new file mode 100644 index 00000000..e69de29b diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json new file mode 100644 index 00000000..bfbd0ce6 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json @@ -0,0 +1,147 @@ +{ + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "c2ea75969a673712e4206c3be5576929608e9820cd19a39b2b3f202add34a05e", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "51230173cd6ea2767f1ec95306f3d3d2c42a8b07b20e56ec3c7661cbb4b96775", + "src/api/graph_read.rs": "0f0f6da64105186d884dab60dfb5e34e3b948a41ad6c8ca494b4904b4b9a1ab4", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "77b89a73eebd5dc24f75291e5615db0028f3ad15f7abfe80ac162d194f43520f", + "src/api/mod.rs": "3e2b72f33f55a3670764ecde9d90c844b6cb8d5d59adee131aaef2dd79dd769b", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52", + "src/api/refresh.rs": "1cebe4ab394d446e4c2b99c34447182b8b2ac4135612040ffda200eefb3ac595", + "src/artifact_store.rs": "5b3b69365285d073a13937d88391a848cd742754bbc759aa9db966f3d7136de2", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator/tests.rs": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252", + "src/coordinator.rs": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "c783ba112f44ce247be532c475b5e39a1bdc8b19db0f1e7d27f627fcf93d3c6b", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "32510a0dccc98be71174eb0dcd5a4e72c1164d42f1a25d77d85c8bfcc534f5fd", + "src/db_writer/phase.rs": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "10fcfc158e6fa8d86ab07cbb2ccec13464488b7ddced929f6b011c0e614f6ec7", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "df6b6f259b1c542c340f7c0850befcb615d7b89c944b9404dbc0ce5f10e95ae5", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e", + "src/materialization_worker.rs": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663", + "src/process_lifecycle/unix.rs": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d", + "src/process_lifecycle/windows.rs": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "0fe724e549ca6276d205be724f8dd41ab746397c81e4043aae64d1e41baf6202", + "src/scan.rs": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da", + "src/staging_writer/files.rs": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "src/storage/direct.rs": "b751c06e1383c2574ad24a72e1919ff82a5c3fc79b28879727a2a79339f5fcbe", + "src/storage/inventory.rs": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "c6296037d04a0120cccdc405052046355d721fc2255929c7bace0e4d1a2def2b", + "src/storage/maintenance.rs": "b237227071c56e84b4f7a281c1c6fbcb6003b3954a2cd4d4f9c0ecda114b5fa8", + "src/storage/maintenance_ownership.rs": "568f69872470e91efaafb91af83b323493dd5b70e136d1ee4fa2ed2e098f7362", + "src/storage/maintenance_scheduler.rs": "fcec504f2f55a227b441d5e103ce18e4caa69c4fd9610070a9f0eaec45e9a90f", + "src/storage/managed.rs": "83364bf78763af639f1866eef61efddc7fbbe58b671296045f6744cc19c3f295", + "src/storage/mod.rs": "68db9239927733a14f0853d5b7330ba19c9114fe8c3cac3d56d67ed2cf3ff981", + "src/storage/run_workspace.rs": "03be7a368aa28a2a14d079fc7401c4d87862d10843d4fe41795f6ccba186ca40", + "src/storage/test_harness.rs": "5fcf6cc2e6f9b79dc87b1f8affe70981f870d8df12c92500bff407723710feb6", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "Cargo.toml": "f267cf8583c7eb20a70d381b461ad0e20ecf52d7e1432ba204ae3664add86a1f", + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "build.rs": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + ".cargo/config.toml": "388ad5407d7e78cae8dacd1c3c556f8c97f43d998c4e09273dcf8fca00eb0f82", + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json new file mode 100644 index 00000000..bfbd0ce6 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json @@ -0,0 +1,147 @@ +{ + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "c2ea75969a673712e4206c3be5576929608e9820cd19a39b2b3f202add34a05e", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "51230173cd6ea2767f1ec95306f3d3d2c42a8b07b20e56ec3c7661cbb4b96775", + "src/api/graph_read.rs": "0f0f6da64105186d884dab60dfb5e34e3b948a41ad6c8ca494b4904b4b9a1ab4", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "77b89a73eebd5dc24f75291e5615db0028f3ad15f7abfe80ac162d194f43520f", + "src/api/mod.rs": "3e2b72f33f55a3670764ecde9d90c844b6cb8d5d59adee131aaef2dd79dd769b", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52", + "src/api/refresh.rs": "1cebe4ab394d446e4c2b99c34447182b8b2ac4135612040ffda200eefb3ac595", + "src/artifact_store.rs": "5b3b69365285d073a13937d88391a848cd742754bbc759aa9db966f3d7136de2", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator/tests.rs": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252", + "src/coordinator.rs": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "c783ba112f44ce247be532c475b5e39a1bdc8b19db0f1e7d27f627fcf93d3c6b", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "32510a0dccc98be71174eb0dcd5a4e72c1164d42f1a25d77d85c8bfcc534f5fd", + "src/db_writer/phase.rs": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "10fcfc158e6fa8d86ab07cbb2ccec13464488b7ddced929f6b011c0e614f6ec7", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "df6b6f259b1c542c340f7c0850befcb615d7b89c944b9404dbc0ce5f10e95ae5", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e", + "src/materialization_worker.rs": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663", + "src/process_lifecycle/unix.rs": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d", + "src/process_lifecycle/windows.rs": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "0fe724e549ca6276d205be724f8dd41ab746397c81e4043aae64d1e41baf6202", + "src/scan.rs": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da", + "src/staging_writer/files.rs": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "src/storage/direct.rs": "b751c06e1383c2574ad24a72e1919ff82a5c3fc79b28879727a2a79339f5fcbe", + "src/storage/inventory.rs": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "c6296037d04a0120cccdc405052046355d721fc2255929c7bace0e4d1a2def2b", + "src/storage/maintenance.rs": "b237227071c56e84b4f7a281c1c6fbcb6003b3954a2cd4d4f9c0ecda114b5fa8", + "src/storage/maintenance_ownership.rs": "568f69872470e91efaafb91af83b323493dd5b70e136d1ee4fa2ed2e098f7362", + "src/storage/maintenance_scheduler.rs": "fcec504f2f55a227b441d5e103ce18e4caa69c4fd9610070a9f0eaec45e9a90f", + "src/storage/managed.rs": "83364bf78763af639f1866eef61efddc7fbbe58b671296045f6744cc19c3f295", + "src/storage/mod.rs": "68db9239927733a14f0853d5b7330ba19c9114fe8c3cac3d56d67ed2cf3ff981", + "src/storage/run_workspace.rs": "03be7a368aa28a2a14d079fc7401c4d87862d10843d4fe41795f6ccba186ca40", + "src/storage/test_harness.rs": "5fcf6cc2e6f9b79dc87b1f8affe70981f870d8df12c92500bff407723710feb6", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "Cargo.toml": "f267cf8583c7eb20a70d381b461ad0e20ecf52d7e1432ba204ae3664add86a1f", + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "build.rs": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + ".cargo/config.toml": "388ad5407d7e78cae8dacd1c3c556f8c97f43d998c4e09273dcf8fca00eb0f82", + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json new file mode 100644 index 00000000..bfbd0ce6 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json @@ -0,0 +1,147 @@ +{ + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "c2ea75969a673712e4206c3be5576929608e9820cd19a39b2b3f202add34a05e", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "51230173cd6ea2767f1ec95306f3d3d2c42a8b07b20e56ec3c7661cbb4b96775", + "src/api/graph_read.rs": "0f0f6da64105186d884dab60dfb5e34e3b948a41ad6c8ca494b4904b4b9a1ab4", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "77b89a73eebd5dc24f75291e5615db0028f3ad15f7abfe80ac162d194f43520f", + "src/api/mod.rs": "3e2b72f33f55a3670764ecde9d90c844b6cb8d5d59adee131aaef2dd79dd769b", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52", + "src/api/refresh.rs": "1cebe4ab394d446e4c2b99c34447182b8b2ac4135612040ffda200eefb3ac595", + "src/artifact_store.rs": "5b3b69365285d073a13937d88391a848cd742754bbc759aa9db966f3d7136de2", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator/tests.rs": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252", + "src/coordinator.rs": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "c783ba112f44ce247be532c475b5e39a1bdc8b19db0f1e7d27f627fcf93d3c6b", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "32510a0dccc98be71174eb0dcd5a4e72c1164d42f1a25d77d85c8bfcc534f5fd", + "src/db_writer/phase.rs": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "10fcfc158e6fa8d86ab07cbb2ccec13464488b7ddced929f6b011c0e614f6ec7", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "df6b6f259b1c542c340f7c0850befcb615d7b89c944b9404dbc0ce5f10e95ae5", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e", + "src/materialization_worker.rs": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663", + "src/process_lifecycle/unix.rs": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d", + "src/process_lifecycle/windows.rs": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "0fe724e549ca6276d205be724f8dd41ab746397c81e4043aae64d1e41baf6202", + "src/scan.rs": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da", + "src/staging_writer/files.rs": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "src/storage/direct.rs": "b751c06e1383c2574ad24a72e1919ff82a5c3fc79b28879727a2a79339f5fcbe", + "src/storage/inventory.rs": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "c6296037d04a0120cccdc405052046355d721fc2255929c7bace0e4d1a2def2b", + "src/storage/maintenance.rs": "b237227071c56e84b4f7a281c1c6fbcb6003b3954a2cd4d4f9c0ecda114b5fa8", + "src/storage/maintenance_ownership.rs": "568f69872470e91efaafb91af83b323493dd5b70e136d1ee4fa2ed2e098f7362", + "src/storage/maintenance_scheduler.rs": "fcec504f2f55a227b441d5e103ce18e4caa69c4fd9610070a9f0eaec45e9a90f", + "src/storage/managed.rs": "83364bf78763af639f1866eef61efddc7fbbe58b671296045f6744cc19c3f295", + "src/storage/mod.rs": "68db9239927733a14f0853d5b7330ba19c9114fe8c3cac3d56d67ed2cf3ff981", + "src/storage/run_workspace.rs": "03be7a368aa28a2a14d079fc7401c4d87862d10843d4fe41795f6ccba186ca40", + "src/storage/test_harness.rs": "5fcf6cc2e6f9b79dc87b1f8affe70981f870d8df12c92500bff407723710feb6", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "Cargo.toml": "f267cf8583c7eb20a70d381b461ad0e20ecf52d7e1432ba204ae3664add86a1f", + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "build.rs": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + ".cargo/config.toml": "388ad5407d7e78cae8dacd1c3c556f8c97f43d998c4e09273dcf8fca00eb0f82", + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09" +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr b/docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr new file mode 100644 index 00000000..dcc12149 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr @@ -0,0 +1,6 @@ +error: failed to select a version for the requirement `zlib-rs = "^0.6.0"` (locked to 0.6.8) +candidate versions found which didn't match: 0.6.7, 0.6.6, 0.6.5, ... +location searched: crates.io index +required by package `flate2 v1.1.10` + ... which satisfies dependency `flate2 = "^1"` (locked to 1.1.10) of package `xtask v3.0.4 (/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo/crates/xtask)` +As a reminder, you're using offline mode (--offline) which can sometimes cause surprising resolution failures, if this error is too confusing you may wish to retry without the offline flag. diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr b/docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr new file mode 100644 index 00000000..079c65f4 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr @@ -0,0 +1,14 @@ +error: failed to download `clap v4.6.1` + +Caused by: + unable to get packages from source + +Caused by: + failed to parse manifest at `/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/s5-cargo-home/registry/src/index.crates.io-6f17d22bba15001f/clap-4.6.1/Cargo.toml` + +Caused by: + feature `edition2024` is required + + The package requires the Cargo feature called `edition2024`, but that feature is not stabilized in this version of Cargo (1.82.0 (8f40fc59f 2024-08-21)). + Consider trying a newer version of Cargo (this may require the nightly release). + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#edition-2024 for more information about the status of this feature. diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-narrow-audit-fix-checkpoint.json b/docs/testing/graph-disk-cleanup-evidence/s5-narrow-audit-fix-checkpoint.json new file mode 100644 index 00000000..62a1c275 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-narrow-audit-fix-checkpoint.json @@ -0,0 +1,66 @@ +{ + "scope": "S5 narrowreadinessfixverification; fullS5pending", + "prior_owned_snapshot_sha256": "df2fbc22f67bcb3c6d2c72105e66ef69c84f5f0300e980e697007f750ac25fa6", + "owned_files_sha256": { + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09", + "docs/testing/graph-disk-cleanup-evidence.json": "d7c62b8db056212eb65184d3ae9b379867af8065788c0382cff53fe10105b7a7", + "docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json": "55763bb75fe573cf97fbe8878687d8e12c30836fa6ee991ee7b7a71451779611", + "docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr": "e4ed00080d9030eba59f6c9af6ac3d6b784e69ad1e9dcb2ce9107c19fb07be0d", + "docs/testing/graph-disk-cleanup-evidence/audit-regressions.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/audit.py": "03cb5168970c16f3691018510a1ad0c4b61f0bd2800ed4267d1869cb8fc57efe", + "docs/testing/graph-disk-cleanup-evidence/foundation-reports.json": "88eff4652ee0d4914980912c2a9600481bb891bc539706fea1677062133b3bb8", + "docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json": "0dc7bd221b6d210f260677b1c2027b707a3bd0f8a0a314d35eefb64d0402e151", + "docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627", + "docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627", + "docs/testing/graph-disk-cleanup-evidence/review-outcomes.json": "0538febc432243201e622d0c734ac82bceaab3abdf7f441c9bd6d08f07e008c9", + "docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml": "43789b2717a390d201e486c0c1def0e0f7aa8cfe3548a61fe2ee4fc8546e0603", + "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876", + "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml": "ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92", + "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json": "65d9f8a5c1124a215f66e63f5819f60aef52382559cb627d807ce7a7d5bab718", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt": "ffd58ad5367a31890d30ae14515523e74e76c810742d011f028c46a4729ae6ed", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json": "95f89f93b01ad3c9b02cbab6b97278cdbb14d604080963a4a2b8a08deee39c5c", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt": "c84d342763d48ca6fee1d2d644bb05cf49b4cd4b2f9f9cc26c21a9e4e2f2fc6c", + "docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml": "8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414", + "docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml": "c4d0cbb89af4996d859c7da5b1bed3b04a1a9b90dd44135bc7c9d2d2c9ecfb3f", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml": "3b8c4e663c2c93d3c52f93da6af4b17096b529deff117ce4f9b191ad766e44b7", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json": "c2dd357024899ff4a64e9d4de15f181a4f9b827e460e509d2ed7e8b1a09ba3f3", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml": "8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json": "ea74ad1208875efa0e56d2f168bcec49c409cfe9219387e8e0b3f85a15645f86", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr": "38b1121bacbb008a468e084a6453f3b81fb7972fc81e6670176e212381b8aa3e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json": "a0dbc8b2f618b3e0c2157cdbbdba9e6a58676e8b02d5210e75263d33cb287651", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.json": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stdout": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr": "df966e27aff804782c1143e8aa9325367cfb66ead53b60fdb86348e6175bea1a", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr": "3789ba8ed22ae4a82b1fc7adaea6cd8d86d3c94beb1f30f698bd4afb3f7e6ff0", + "docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr": "d75191f480933414ac1b27956d159e407be16eecac57c0d782ec127207ea333e", + "docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json": "ff7043693f6cd3bf978921262ae7c9e83ad4772bf760d1db777f169f93694600", + "docs/testing/graph-disk-cleanup-evidence/test_audit.py": "a18988b8ed73c090b3b05337cf6f2150b619b403be2e3cd9cc7387971a02e9c9" + }, + "owned_snapshot_sha256": "244a4fb83602255cf5f432fcbf5e7c6ea0e9dc1936cd4dd3090fb0f7f89199a3", + "confirmed_findings_addressed": { + "P2": "Namedfoundationcases individuallymustactuallypass; externalexpectednames/runUUID/all3countschecked;8isolatedregressions", + "P3": "Freshcompiled2-testexecutionwith145fullsrc/relevantsourcehashesandbinaryidentitybefore-after; historicalprovenancegapretainedexplicitly" + }, + "tested_source_identity_manifest": "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "actual_execution": "5f3b8e10-9d6f-414b-a4ec-6a910655d899:2passed0failed0skipped", + "audit_regressions": "8actualunittestspassed; logs/sourcehashesin audit-regression-execution.json", + "limitations": [ + "Unfinishedproductionintegrationwarnings remain; no warning-freeclosure", + "MSRVworkspaceblockedbyunchangedbaselinedependencies", + "No collector/churn/fault/fullsuite/nativeplatformqualification", + "Samefreshreviewerfixverificationand finalintegrateddifferentfreshMaxreviewpending" + ] +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-narrow-checkpoint.json b/docs/testing/graph-disk-cleanup-evidence/s5-narrow-checkpoint.json new file mode 100644 index 00000000..4763954b --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-narrow-checkpoint.json @@ -0,0 +1,71 @@ +{ + "scope": "S5narrowlossless-evidence-archivefollowup; no runtime/sourcechanges", + "prior_owned_snapshot_sha256": "244a4fb83602255cf5f432fcbf5e7c6ea0e9dc1936cd4dd3090fb0f7f89199a3", + "prior_commit": "9eaa8dedf4f626ebf0974f327f81d0a3c0315e57", + "owned_files_sha256": { + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09", + "docs/testing/graph-disk-cleanup-evidence.json": "8cb9670ace63fc10d02447c4f612f306242a060e7d1882e4992cae7ba6347d40", + "docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json": "c750c93245f79df2379200a4a43fb8b5b94c48a7d351b22da1eefb8e05bcc91d", + "docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr": "37deacba5b2b624d0962949aa5cb60ce368a873c072cfff764cf836bb098a66b", + "docs/testing/graph-disk-cleanup-evidence/audit-regressions.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/audit.py": "316630c38e1638370b8c1873174d4311cc32ae4d0d9bcbbf0045e1083ccfb518", + "docs/testing/graph-disk-cleanup-evidence/foundation-reports.json": "88eff4652ee0d4914980912c2a9600481bb891bc539706fea1677062133b3bb8", + "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regression-execution.json": "55763bb75fe573cf97fbe8878687d8e12c30836fa6ee991ee7b7a71451779611", + "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stderr": "e4ed00080d9030eba59f6c9af6ac3d6b784e69ad1e9dcb2ce9107c19fb07be0d", + "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json": "0dc7bd221b6d210f260677b1c2027b707a3bd0f8a0a314d35eefb64d0402e151", + "docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627", + "docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627", + "docs/testing/graph-disk-cleanup-evidence/review-outcomes.json": "bac2d4ad1d32aa54f709c6ccfd34b4a3b5917f0822d3524e1b8f70378cc5ccbe", + "docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml": "43789b2717a390d201e486c0c1def0e0f7aa8cfe3548a61fe2ee4fc8546e0603", + "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876", + "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml": "ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92", + "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json": "65d9f8a5c1124a215f66e63f5819f60aef52382559cb627d807ce7a7d5bab718", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt": "ffd58ad5367a31890d30ae14515523e74e76c810742d011f028c46a4729ae6ed", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json": "95f89f93b01ad3c9b02cbab6b97278cdbb14d604080963a4a2b8a08deee39c5c", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt.gz": "9deb649f914ef65bc0ad3dc7632409c2b4d4615091c8ee74781b4e390abf806a", + "docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml": "8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414", + "docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml": "c4d0cbb89af4996d859c7da5b1bed3b04a1a9b90dd44135bc7c9d2d2c9ecfb3f", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml": "3b8c4e663c2c93d3c52f93da6af4b17096b529deff117ce4f9b191ad766e44b7", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json": "c2dd357024899ff4a64e9d4de15f181a4f9b827e460e509d2ed7e8b1a09ba3f3", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml": "8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json": "ea74ad1208875efa0e56d2f168bcec49c409cfe9219387e8e0b3f85a15645f86", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr": "38b1121bacbb008a468e084a6453f3b81fb7972fc81e6670176e212381b8aa3e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json": "a0dbc8b2f618b3e0c2157cdbbdba9e6a58676e8b02d5210e75263d33cb287651", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.json": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stdout": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr": "df966e27aff804782c1143e8aa9325367cfb66ead53b60fdb86348e6175bea1a", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr": "3789ba8ed22ae4a82b1fc7adaea6cd8d86d3c94beb1f30f698bd4afb3f7e6ff0", + "docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr": "d75191f480933414ac1b27956d159e407be16eecac57c0d782ec127207ea333e", + "docs/testing/graph-disk-cleanup-evidence/s5-narrow-audit-fix-checkpoint.json": "330a275563716dcd528c145b9ff785ac7ab686088a86572d1379f11aa9d278be", + "docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json": "ff7043693f6cd3bf978921262ae7c9e83ad4772bf760d1db777f169f93694600", + "docs/testing/graph-disk-cleanup-evidence/test_audit.py": "b317ac2a5f4ab7ec3c52e98169e37090d65c9a7a92d397bd898962516f6f545c" + }, + "owned_snapshot_sha256": "b9f5ef787e5f70289200d744abfaab03079b7aefdfcf991f4f23c2bf94765c1d", + "lossless_archive": { + "path": "docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt.gz", + "decoded_sha256": "c84d342763d48ca6fee1d2d644bb05cf49b4cd4b2f9f9cc26c21a9e4e2f2fc6c", + "method": "gzipfilenameempty,mtime0; originalbytespreservedincludingblankEOF", + "historical_commit": "9eaa8dedf4f626ebf0974f327f81d0a3c0315e57" + }, + "audit_regressions": "10actualunittestcasespassed; logs/sourcehashesin audit-regression-execution.json", + "tested_source_identity_manifest": "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "production_test_execution_unchanged": "5f3b8e10-9d6f-414b-a4ec-6a910655d899:2passed0failed0skipped; no rerunnecessaryforencodingonlyfix", + "limitations": [ + "FullS5collector/churn/fault/nativeplatform/MSRV/warningfreequalificationpending", + "FocusedlosslessarchiveverificationbyexistingfreshMaxreviewerpending" + ] +} diff --git a/docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json b/docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json new file mode 100644 index 00000000..adfb581d --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json @@ -0,0 +1,42 @@ +{ + "scope": "S5 readiness/evidence and2externalproductionhealth/requesttests; fullS5pending", + "owned_files_sha256": { + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09", + "docs/testing/graph-disk-cleanup-evidence.json": "f2d9ba24ef6f4b9a602d5530e30d19c37e0cb0f9d5ca7e1e311fb09cf6b18795", + "docs/testing/graph-disk-cleanup-evidence/audit.py": "43f748026768955879cb8ff8d0e0d2f90044c16f58535bc4e7e551286650a42a", + "docs/testing/graph-disk-cleanup-evidence/foundation-reports.json": "88eff4652ee0d4914980912c2a9600481bb891bc539706fea1677062133b3bb8", + "docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json": "0dc7bd221b6d210f260677b1c2027b707a3bd0f8a0a314d35eefb64d0402e151", + "docs/testing/graph-disk-cleanup-evidence/review-outcomes.json": "0538febc432243201e622d0c734ac82bceaab3abdf7f441c9bd6d08f07e008c9", + "docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml": "43789b2717a390d201e486c0c1def0e0f7aa8cfe3548a61fe2ee4fc8546e0603", + "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876", + "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml": "ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92", + "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json": "65d9f8a5c1124a215f66e63f5819f60aef52382559cb627d807ce7a7d5bab718", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt": "ffd58ad5367a31890d30ae14515523e74e76c810742d011f028c46a4729ae6ed", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json": "95f89f93b01ad3c9b02cbab6b97278cdbb14d604080963a4a2b8a08deee39c5c", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt": "c84d342763d48ca6fee1d2d644bb05cf49b4cd4b2f9f9cc26c21a9e4e2f2fc6c", + "docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml": "8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414", + "docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml": "c4d0cbb89af4996d859c7da5b1bed3b04a1a9b90dd44135bc7c9d2d2c9ecfb3f", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml": "8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json": "51f3263d819bceae59b1eb471dab1005fc5bc27450bb089f4aa79727d115a600", + "docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr": "3789ba8ed22ae4a82b1fc7adaea6cd8d86d3c94beb1f30f698bd4afb3f7e6ff0", + "docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr": "d75191f480933414ac1b27956d159e407be16eecac57c0d782ec127207ea333e" + }, + "owned_snapshot_sha256": "df2fbc22f67bcb3c6d2c72105e66ef69c84f5f0300e980e697007f750ac25fa6", + "tested_source_identity_manifest": "docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json", + "executed_checks": [ + "2/2 external health/request tests passed; no skipped", + "stable rustfmt --check passed on both owned Rustfiles", + "standalone rustc1.82 fixture support library compilation passed", + "evidence integrity audit passed" + ], + "limitations": [ + "136 library dead-code warnings from unfinished productionintegration remain", + "MSRV workspace metadata blocked by unchanged baseline dependencies", + "No fullcollector/churn/fault/nativeplatformqualification", + "Final integrated different fresh Max review not performed" + ] +} diff --git a/docs/testing/graph-disk-cleanup-evidence/test_audit.py b/docs/testing/graph-disk-cleanup-evidence/test_audit.py new file mode 100644 index 00000000..f934f32d --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/test_audit.py @@ -0,0 +1,133 @@ +"""Mutation regressions use isolated copies; original execution reports stay intact.""" + +import contextlib +import hashlib +import gzip +import importlib.util +import io +import json +from pathlib import Path +import shutil +import tempfile +import unittest +import xml.etree.ElementTree as ET + +ROOT = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("evidence_audit", ROOT / "audit.py") +AUDIT = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(AUDIT) + + +class EvidenceMutationTests(unittest.TestCase): + def setUp(self): + self.temporary = tempfile.TemporaryDirectory(prefix="cgraph-s5-audit-regression-") + self.addCleanup(self.temporary.cleanup) + base = Path(self.temporary.name) + self.root = base / ROOT.name + shutil.copytree(ROOT, self.root, ignore=shutil.ignore_patterns("__pycache__")) + shutil.copy2(ROOT.parent / "graph-disk-cleanup-evidence.json", base) + + def read(self, name): + return json.loads((self.root / name).read_text()) + + def write(self, name, data): + (self.root / name).write_text(json.dumps(data)) + + def rewrite_report(self, record, mutate, path_key="junit", hash_key="junit_sha256"): + path = self.root / record[path_key] + tree = ET.parse(path) + mutate(tree.getroot()) + tree.write(path, encoding="utf-8", xml_declaration=True) + record[hash_key] = hashlib.sha256(path.read_bytes()).hexdigest() + + def external(self): + index = json.loads((self.root.parent / "graph-disk-cleanup-evidence.json").read_text()) + return Path(index["s5_external_execution"]).name + + def rejected(self): + with contextlib.redirect_stdout(io.StringIO()), self.assertRaises(AssertionError): + AUDIT.audit(self.root) + + def test_original_actual_reports_pass(self): + with contextlib.redirect_stdout(io.StringIO()): + AUDIT.audit(self.root) + + def test_compressed_artifact_matches_historical_decoded_bytes(self): + data = self.read("review-outcomes.json") + artifact = next(a for a in data["s3_expanded_partial"]["executed_failure_artifacts"] if a.get("encoding") == "gzip") + decoded = gzip.decompress((self.root / artifact["path"]).read_bytes()) + historical = self.read("s5-narrow-initial-checkpoint.json") + historical_hash = historical["owned_files_sha256"][artifact["historical_repository_path"]] + self.assertEqual(hashlib.sha256(decoded).hexdigest(), historical_hash) + self.assertEqual(artifact["decoded_sha256"], historical_hash) + self.assertEqual(artifact["historical_commit"], "9eaa8dedf4f626ebf0974f327f81d0a3c0315e57") + + def test_compressed_payload_tamper_rejected_even_with_matching_encoded_hash(self): + data = self.read("review-outcomes.json") + artifact = next(a for a in data["s3_expanded_partial"]["executed_failure_artifacts"] if a.get("encoding") == "gzip") + encoded = gzip.compress(b"changed observation output\n", mtime=0) + (self.root / artifact["path"]).write_bytes(encoded) + artifact["sha256"] = hashlib.sha256(encoded).hexdigest() + self.write("review-outcomes.json", data) + self.rejected() + + def test_named_foundation_skip_rejected_even_with_matching_hash_and_counts(self): + data = self.read("foundation-reports.json") + record = next(r for r in data["reports"] if r["id"] == "s1-post-main") + def skip(xml): + case = next(c for c in xml.findall(".//testcase") if "dc_s1_a1_" in c.get("name", "")) + ET.SubElement(case, "skipped") + self.rewrite_report(record, skip, "report", "sha256") + record["execution_counts"]["skipped"] += 1 + self.write("foundation-reports.json", data) + self.rejected() + + def test_external_skip_rejected_with_original_claimed_counts(self): + name = self.external() + data = self.read(name) + self.rewrite_report(data, lambda xml: ET.SubElement(xml.findall(".//testcase")[0], "skipped")) + self.write(name, data) + self.rejected() + + def test_external_skip_rejected_even_with_matching_manifest_counts(self): + name = self.external() + data = self.read(name) + self.rewrite_report(data, lambda xml: ET.SubElement(xml.findall(".//testcase")[0], "skipped")) + data["actual_result"] = {"passed": 1, "failed": 0, "skipped": 1} + self.write(name, data) + self.rejected() + + def test_external_claimed_failure_count_mismatch_rejected(self): + name = self.external() + data = self.read(name) + data["actual_result"]["failed"] = 1 + self.write(name, data) + self.rejected() + + def test_external_case_name_mismatch_rejected_despite_valid_hash(self): + name = self.external() + data = self.read(name) + self.rewrite_report(data, lambda xml: xml.findall(".//testcase")[0].set("name", "different_case")) + self.write(name, data) + self.rejected() + + def test_external_run_identity_mismatch_rejected_despite_valid_hash(self): + name = self.external() + data = self.read(name) + self.rewrite_report(data, lambda xml: xml.set("uuid", "different-run")) + self.write(name, data) + self.rejected() + + def test_external_missing_case_rejected_despite_valid_hash(self): + name = self.external() + data = self.read(name) + def remove(xml): + suite = xml.find("testsuite") + suite.remove(suite.find("testcase")) + self.rewrite_report(data, remove) + self.write(name, data) + self.rejected() + + +if __name__ == "__main__": + unittest.main() diff --git a/docs/testing/graph-disk-cleanup-evidence/test_qualification.py b/docs/testing/graph-disk-cleanup-evidence/test_qualification.py new file mode 100644 index 00000000..4e206288 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-evidence/test_qualification.py @@ -0,0 +1,405 @@ +"""Protocol regressions. Synthetic archives here never qualify production behavior.""" + +import importlib.util +import json +import os +import subprocess +from pathlib import Path +import sys +import tempfile +import unittest +from unittest.mock import patch + +ROOT = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("qualification", ROOT / "qualification.py") +Q = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(Q) + + +class QualificationProtocolTests(unittest.TestCase): + def setUp(self): + self.temporary = tempfile.TemporaryDirectory(prefix="cgraph-s5-qualification-protocol-") + self.addCleanup(self.temporary.cleanup) + self.output = Path(self.temporary.name) + source = {name: "a" * 64 for name in ["Cargo.toml", "Cargo.lock", "build.rs", "src/lib.rs", "src/main.rs", "tests/storage_maintenance.rs"]} + for name in ["source-before.json", "source-after-build.json", "source-after-execution.json"]: + Q.write_json(self.output / name, source) + binaries = {"/synthetic/target/debug/deps/storage_maintenance-test": "b" * 64, + "/synthetic/target/debug/codebase-graph": "c" * 64} + for name in ["binary-before.json", "binary-after.json"]: + Q.write_json(self.output / name, binaries) + metadata = {"rust-build-meta": { + "target-directory": "/synthetic/target", "base-output-directories": ["debug"], + "non-test-binaries": {"synthetic-root": [{"path": "debug/codebase-graph"}]}, + }, "rust-binaries": {Q.BINARY: {"package-id": "synthetic-root", "binary-path": "/synthetic/target/debug/deps/storage_maintenance-test"}}} + Q.write_json(self.output / "binary-list.stdout", metadata) + self.xml = '' + ''.join( + '' % (Q.BINARY, name) for name in sorted(Q.EXPECTED) + ) + '' + (self.output / "junit.xml").write_text(self.xml) + for name in ["binary-list.stderr", "cargo-metadata.stdout", "cargo-metadata.stderr", + "execution.stdout", "execution.stderr", "nextest.toml", "capture-driver.py", "rustc.stdout", + "rustc.stderr", "cargo.stdout", "cargo.stderr"]: + (self.output / name).write_text("synthetic protocol fixture\n") + repository = Path("/synthetic/repo") + package = {"id": "synthetic-root", "name": "codebase-graph", "manifest_path": str(repository / "Cargo.toml")} + Q.write_json(self.output / "cargo-metadata.stdout", {"workspace_root": str(repository), "workspace_members": [package["id"]], "packages": [package]}) + messages, events = [], [] + for name, kind, source, filename in [ + ("codebase_graph", "lib", "src/lib.rs", "/synthetic/target/debug/deps/libcodebase_graph.rlib"), + ("codebase-graph", "bin", "src/main.rs", "/synthetic/target/debug/codebase-graph"), + ("storage_maintenance", "test", "tests/storage_maintenance.rs", "/synthetic/target/debug/deps/storage_maintenance-test"), + ]: + messages.append({"reason": "compiler-artifact", "package_id": package["id"], "fresh": False, + "profile": {"debug_assertions": True}, "target": {"name": name, "kind": [kind], "src_path": str(repository / source)}, "filenames": [filename]}) + events.append({"compiler": "/synthetic/rustc", "manifest": str(repository), "cwd": str(repository), "exit": 0, + "args": ["--crate-name", name.replace("-", "_"), source, "--out-dir", str(Path(filename).parent), "-C", "debug-assertions=on"]}) + (self.output / "root-build.stdout").write_text("".join(json.dumps(m) + "\n" for m in messages)) + for name in ["root-build.stderr", "wrapper-build.stdout", "wrapper-build.stderr", "root-wrapper.exe", "root-rustc.stdout", "root-rustc.stderr"]: + (self.output / name).write_text("synthetic protocol fixture\n") + (self.output / "root-wrapper.rs").write_text(Q.WRAPPER_SOURCE) + outputs = {m["filenames"][0]: {"compiler_output": m["filenames"][0], "sha256": binaries.get(m["filenames"][0], "d" * 64)} for m in messages} + Q.write_json(self.output / "compile-proof.json", {"events": events, "cargo_messages": messages, "outputs": outputs}) + Q.write_json(self.output / "build-environment.json", { + "RUSTC_WORKSPACE_WRAPPER": str(self.output / "root-wrapper.exe"), "CGRAPH_QUALIFICATION_EVENTS": str(self.output / "rustc-events"), + "CARGO_PROFILE_TEST_DEBUG_ASSERTIONS": "true", "CARGO_PROFILE_DEV_DEBUG_ASSERTIONS": "true"}) + Q.write_json(self.output / "inventory.stdout", { + "rust-build-meta": metadata["rust-build-meta"], "test-count": len(Q.EXPECTED), + "rust-suites": {Q.BINARY: {"binary-path": "/synthetic/target/debug/deps/storage_maintenance-test", "package-id": "synthetic-root", + "testcases": {name: {"ignored": False, "filter-match": {"status": "matches"}} for name in Q.EXPECTED}}}}) + (self.output / "inventory.stderr").write_text("synthetic protocol inventory\n") + self.record = { + "schema": Q.SCHEMA, "scope": "storage_maintenance_suite", "output": str(self.output), "repository": str(repository), + "compile_proof": {"events": events, "cargo_messages": messages, "outputs": outputs}, + "expected_case_names": sorted(Q.EXPECTED), + "qualification": {"aggregate": "pending", "windows": "pending", "linux": "pending"}, + "limitations": Q.LIMITATIONS, + "commands": {label: {"argv": argv, "exit_code": 0} for label, argv in [ + ("rustc", ["rustc", "-vV"]), ("cargo", ["cargo", "-V"]), + ("cargo-metadata", ["cargo", "metadata", "--offline", "--locked", "--format-version", "1"]), + ("binary-list", ["cargo", "nextest", "list", "--offline", "--locked", "--test", "storage_maintenance", "--list-type", "binaries-only", "--message-format", "json"]), + ("wrapper-build", ["rustc", "--edition=2021", str(self.output / "root-wrapper.rs"), "-o", str(self.output / "root-wrapper.exe")]), + ("root-build", Q.root_build_command()), + ("root-rustc", ["/synthetic/rustc", "-vV"]), + ("inventory", Q.inventory_command(self.output)), + ("execution", Q.execution_command(self.output)), + ]}, + "result": Q.junit_result(self.output / "junit.xml"), + } + self.seal() + + def seal(self): + self.record["artifacts"] = {p.name: Q.sha256(p) for p in self.output.iterdir() if p.name != "capture.json"} + Q.write_json(self.output / "capture.json", self.record) + + def rejected(self, reason): + with self.assertRaisesRegex(ValueError, reason): + Q.verify(self.output) + + def test_protocol_fixture_is_valid_without_production_qualification(self): + self.assertEqual(Q.verify(self.output)["qualification"]["aggregate"], "pending") + + def test_missing_and_changed_artifacts_rejected(self): + (self.output / "execution.stderr").write_text("changed") + self.rejected("artifact changed") + + def test_path_traversal_rejected(self): + self.record["artifacts"]["../outside"] = "a" * 64 + Q.write_json(self.output / "capture.json", self.record) + self.rejected("escapes archive") + + def test_source_change_even_with_rehashed_artifact_rejected(self): + Q.write_json(self.output / "source-after-build.json", {"Cargo.toml": "d" * 64}) + self.seal() + self.rejected("source changed") + + def test_source_addition_and_deletion_invalidate_snapshot(self): + for name in ["Cargo.toml", "Cargo.lock", "build.rs"]: + (self.output / name).write_text("fixture") + with patch.object(Q.subprocess, "check_output", return_value=b"Cargo.toml\0Cargo.lock\0build.rs\0"): + original = Q.source_manifest(self.output) + (self.output / "new.rs").write_text("new") + with patch.object(Q.subprocess, "check_output", return_value=b"Cargo.toml\0Cargo.lock\0build.rs\0src/new.rs\0"): + (self.output / "src").mkdir() + (self.output / "src/new.rs").write_text("new") + self.assertNotEqual(Q.source_manifest(self.output), original) + (self.output / "Cargo.lock").unlink() + with patch.object(Q.subprocess, "check_output", return_value=b"Cargo.toml\0Cargo.lock\0build.rs\0"): + with self.assertRaises(FileNotFoundError): + Q.source_manifest(self.output) + + def test_changed_binary_rejected_even_with_rehashed_artifact(self): + Q.write_json(self.output / "binary-after.json", {"/synthetic/other": "d" * 64}) + self.seal() + self.rejected("binary identity") + + def test_unrecorded_helper_binary_rejected(self): + for name in ["binary-before.json", "binary-after.json"]: + data = Q.read_json(self.output / name) + del data["/synthetic/target/debug/codebase-graph"] + Q.write_json(self.output / name, data) + self.seal() + self.rejected("binary identity") + + def test_release_build_cannot_qualify_debug_capture(self): + metadata = Q.read_json(self.output / "binary-list.stdout") + metadata["rust-build-meta"]["base-output-directories"] = ["release"] + Q.write_json(self.output / "binary-list.stdout", metadata) + self.seal() + self.rejected("debug build") + + def test_failed_build_or_execution_rejected(self): + for label in ["binary-list", "execution"]: + self.record["commands"][label]["exit_code"] = 101 + self.seal() + self.rejected("command failed") + self.record["commands"][label]["exit_code"] = 0 + + def test_filtered_or_rebuilt_execution_rejected(self): + self.record["commands"]["execution"]["argv"] += ["-E", "test(nothing)"] + self.seal() + self.rejected("execution command") + + def test_skip_failure_error_and_retry_are_not_passes(self): + for tag in ["skipped", "failure", "error", "rerunFailure", "rerunError"]: + (self.output / "junit.xml").write_text(self.xml.replace('/>', '><%s/>' % tag, 1)) + self.seal() + self.rejected("first execution") + + def test_missing_extra_and_duplicate_cases_rejected(self): + for change in [self.xml.replace('name="health_preserves', 'name="different_health_preserves'), + self.xml.replace('', ''), + self.xml.replace('', '' % (Q.BINARY, sorted(Q.EXPECTED)[0]))]: + (self.output / "junit.xml").write_text(change) + self.seal() + self.rejected("testcase") + + def test_result_and_run_identity_mismatch_rejected(self): + self.record["result"]["run_id"] = "another-run" + self.seal() + self.rejected("identity mismatch") + + def test_platform_or_aggregate_promotion_rejected(self): + for gate in ["aggregate", "windows", "linux"]: + self.record["qualification"][gate] = "passed" + self.seal() + self.rejected("qualification gates") + self.record["qualification"][gate] = "pending" + + def test_capture_refuses_existing_output_before_any_build(self): + with self.assertRaises(FileExistsError): + Q.capture(ROOT, self.output) + + def mutate_proof(self, mutate): + proof = Q.read_json(self.output / "compile-proof.json") + mutate(proof) + Q.write_json(self.output / "compile-proof.json", proof) + self.record["compile_proof"] = proof + (self.output / "root-build.stdout").write_text("".join(json.dumps(m) + "\n" for m in proof["cargo_messages"])) + self.seal() + + def test_cached_root_artifact_is_rejected(self): + self.mutate_proof(lambda proof: proof["cargo_messages"][0].update(fresh=True)) + self.rejected("reused from cache") + + def test_assertion_disabled_root_profile_is_rejected(self): + self.mutate_proof(lambda proof: proof["cargo_messages"][0]["profile"].update(debug_assertions=False)) + self.rejected("profile disables") + + def test_missing_actual_root_compiler_invocation_is_rejected(self): + self.mutate_proof(lambda proof: proof.update(events=[])) + self.rejected("actual root compiler identity|actual rustc invocation") + + def test_assertion_disabled_actual_compiler_is_rejected(self): + self.mutate_proof(lambda proof: proof["events"][0]["args"].__setitem__(-1, "debug-assertions=off")) + self.rejected("compiler assertions") + + def test_wrong_workspace_and_package_are_rejected(self): + data = Q.read_json(self.output / "cargo-metadata.stdout") + data["workspace_root"] = "/another/repository" + Q.write_json(self.output / "cargo-metadata.stdout", data) + self.seal() + self.rejected("wrong Cargo workspace") + + def test_ambient_rustflags_are_rejected_before_compilation(self): + with patch.dict(os.environ, {"RUSTFLAGS": "-C debug-assertions=off"}): + with self.assertRaisesRegex(ValueError, "unsupported ambient compiler override"): + Q.build_environment(self.output) + + def test_current_suite_inventory_cannot_hide_filtered_or_ignored_cases(self): + original = Q.read_json(self.output / "inventory.stdout") + for field in ["ignored", "filter-match"]: + inventory = json.loads(json.dumps(original)) + case = next(iter(inventory["rust-suites"][Q.BINARY]["testcases"].values())) + case[field] = True if field == "ignored" else {"status": "mismatch"} + Q.write_json(self.output / "inventory.stdout", inventory) + self.seal() + self.rejected("ignored or filtered") + + def test_added_registered_case_must_execute_in_junit(self): + inventory = Q.read_json(self.output / "inventory.stdout") + inventory["test-count"] += 1 + inventory["rust-suites"][Q.BINARY]["testcases"]["new_real_case"] = {"ignored": False, "filter-match": {"status": "matches"}} + Q.write_json(self.output / "inventory.stdout", inventory) + self.record["expected_case_names"].append("new_real_case") + self.record["expected_case_names"].sort() + self.seal() + self.rejected("testcase") + + def test_source_workload_cannot_pass_only_the_three_historical_cases(self): + for name in ["source-before.json", "source-after-build.json", "source-after-execution.json"]: + source = Q.read_json(self.output / name) + source["tests/support/storage_maintenance/workload.rs"] = "e" * 64 + Q.write_json(self.output / name, source) + self.seal() + self.rejected("required fixture test cases") + + def test_inventory_binary_package_and_named_identity_must_match(self): + inventory = Q.read_json(self.output / "inventory.stdout") + inventory["rust-suites"][Q.BINARY]["package-id"] = "foreign" + Q.write_json(self.output / "inventory.stdout", inventory) + self.seal() + self.rejected("binary/package identity") + + def test_source_bound_eight_and_nine_case_inventories_are_accepted_exactly(self): + names = set(Q.EXPECTED) | Q.WORKLOAD_CASES + for include_process in [False, True]: + if include_process: + names.add(Q.PROCESS_CASE) + for name in ["source-before.json", "source-after-build.json", "source-after-execution.json"]: + source = Q.read_json(self.output / name) + source["tests/support/storage_maintenance/workload.rs"] = "e" * 64 + if include_process: + source["tests/support/storage_maintenance/process.rs"] = "f" * 64 + Q.write_json(self.output / name, source) + inventory = Q.read_json(self.output / "inventory.stdout") + inventory["test-count"] = len(names) + inventory["rust-suites"][Q.BINARY]["testcases"] = {name: {"ignored": False, "filter-match": {"status": "matches"}} for name in names} + Q.write_json(self.output / "inventory.stdout", inventory) + xml = '' + ''.join('' % (Q.BINARY, name) for name in sorted(names)) + '' + (self.output / "junit.xml").write_text(xml) + self.record["expected_case_names"] = sorted(names) + self.record["result"] = Q.junit_result(self.output / "junit.xml", names) + self.seal() + self.assertEqual(Q.verify(self.output)["result"]["passed"], 9 if include_process else 8) + + def test_historical_schema_two_keeps_its_exact_three_case_scope(self): + self.record["schema"] = 2 + self.record["scope"] = "narrow_external_contract" + self.record["limitations"] = Q.LEGACY_LIMITATIONS + del self.record["commands"]["inventory"] + for name in ["inventory.stdout", "inventory.stderr"]: + (self.output / name).unlink() + self.seal() + historical = Q.verify(self.output) + self.assertEqual(historical["result"]["passed"], 3) + self.assertEqual(historical["schema"], 2) + self.assertEqual(historical["scope"], "narrow_external_contract") + + def test_real_subprocess_exit_and_output_are_captured(self): + result = Q.run([sys.executable, "-c", "import sys; print('actual output'); sys.stderr.write('actual error'); sys.exit(7)"], self.output, self.output, "probe") + self.assertEqual(result["exit_code"], 7) + self.assertEqual((self.output / "probe.stdout").read_text(), "actual output\n") + self.assertEqual((self.output / "probe.stderr").read_text(), "actual error") + + +class QualificationRuntimeTests(unittest.TestCase): + """Real Cargo/nextest counterexamples, using only a tiny fixture-owned target. + + These run normally with this module. --protocol-only is available while the + coordinator's Cargo lease belongs to another slice; it is not qualification. + """ + def setUp(self): + evidence_root = os.environ.get("CGRAPH_QUALIFICATION_RUNTIME_OUTPUT") + if evidence_root: + self.base = Path(tempfile.mkdtemp(prefix=self._testMethodName + "-", dir=evidence_root)).resolve() + print("Real provenance regression archive: " + str(self.base), file=sys.stderr) + else: + self.temporary = tempfile.TemporaryDirectory(prefix="cgraph-s5-real-provenance-") + self.addCleanup(self.temporary.cleanup) + self.base = Path(self.temporary.name).resolve() + self.repository = self.base / "repo" + self.repository.mkdir() + (self.repository / "src").mkdir() + (self.repository / "tests").mkdir() + (self.repository / "Cargo.toml").write_text('[package]\nname="codebase-graph"\nversion="0.0.0"\nedition="2021"\n') + (self.repository / "build.rs").write_text("fn main() {}\n") + (self.repository / "src/lib.rs").write_text("pub fn assertions_enabled() -> bool { cfg!(debug_assertions) }\n") + (self.repository / "src/main.rs").write_text("fn main() { assert!(cfg!(debug_assertions)); }\n") + self.test_source = self.repository / "tests/storage_maintenance.rs" + self.write_assertions("assert!(true)") + environment = os.environ.copy() + environment["CARGO_TARGET_DIR"] = str(self.base / "target") + for name in ["RUSTFLAGS", "CARGO_ENCODED_RUSTFLAGS", "RUSTC", "RUSTC_WRAPPER", "RUSTC_WORKSPACE_WRAPPER"]: + environment.pop(name, None) + self.environment = environment + for command in [ + ["git", "init", "--quiet"], ["cargo", "generate-lockfile", "--offline"], + ["git", "add", "."], ["git", "-c", "user.name=Qualification fixture", "-c", "user.email=fixture@example.invalid", "commit", "--quiet", "-m", "test: isolated fixture"], + ]: + subprocess.run(command, cwd=self.repository, env=environment, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + def write_assertions(self, assertion): + self.test_source.write_text("".join( + '#[test]\nfn %s() { %s; assert!(codebase_graph::assertions_enabled()); assert!(std::process::Command::new(env!("CARGO_BIN_EXE_codebase-graph")).status().unwrap().success()); }\n' % (name, assertion) + for name in sorted(Q.EXPECTED))) + + def capture(self, name): + with patch.dict(os.environ, self.environment, clear=True): + return Q.capture(self.repository, self.base / name) + + def test_mtime_preserved_failing_source_is_recompiled_and_fails(self): + original = self.capture("before") + old_stat = self.test_source.stat() + self.write_assertions("assert!(false)") + os.utime(self.test_source, ns=(old_stat.st_atime_ns, old_stat.st_mtime_ns)) + failing_hash = Q.sha256(self.test_source) + with self.assertRaisesRegex(ValueError, "execution failed"): + self.capture("after") + self.assertEqual(Q.sha256(self.test_source), failing_hash) + self.assertEqual(self.test_source.stat().st_mtime_ns, old_stat.st_mtime_ns) + proof = Q.read_json(self.base / "after/compile-proof.json") + root = [m for m in proof["cargo_messages"] if m.get("reason") == "compiler-artifact" and "custom-build" not in m["target"]["kind"]] + self.assertTrue(root and all(m["fresh"] is False for m in root)) + self.assertNotEqual(Q.read_json(self.base / "before/source-before.json"), Q.read_json(self.base / "after/source-before.json")) + self.assertEqual(original["result"]["passed"], 3) + Q.write_json(self.base / "runtime-result.json", { + "scenario": "mtime-preserved changed assertion", "baseline_result": original["result"], + "failing_source_sha256": failing_hash, "mtime_before_ns": old_stat.st_mtime_ns, + "mtime_after_ns": self.test_source.stat().st_mtime_ns, + "changed_source_was_recompiled": all(m["fresh"] is False for m in root), + "changed_source_capture_rejected": True, + "actual_failed_junit_sha256": Q.sha256(self.base / "after/junit.xml"), + }) + + def test_assertion_disabled_environment_and_config_cannot_pass_disabled_tests(self): + self.environment["CARGO_PROFILE_TEST_DEBUG_ASSERTIONS"] = "false" + self.environment["CARGO_PROFILE_DEV_DEBUG_ASSERTIONS"] = "false" + config = self.repository / ".cargo" + config.mkdir() + (config / "config.toml").write_text('[build]\nrustflags = ["-C", "debug-assertions=off"]\n') + # Enabled-sensitive library, helper and all tests pass despite ambient off. + enabled = self.capture("enabled") + self.assertEqual(enabled["result"]["passed"], 3) + self.write_assertions("assert!(!cfg!(debug_assertions))") + with self.assertRaisesRegex(ValueError, "execution failed"): + self.capture("disabled-counterexample") + proof = Q.read_json(self.base / "enabled/compile-proof.json") + self.assertTrue(any("debug-assertions=off" in e["args"] for e in proof["events"])) + root_events = [e for e in proof["events"] if "--crate-name" in e["args"]] + self.assertTrue(root_events and all(e["args"][-2:] == ["-C", "debug-assertions=on"] for e in root_events)) + Q.write_json(self.base / "runtime-result.json", { + "scenario": "assertion-disabled environment and Cargo config", "enabled_result": enabled["result"], + "ambient_test_debug_assertions": "false", "ambient_dev_debug_assertions": "false", + "config_rustflags": ["-C", "debug-assertions=off"], "actual_root_compilers_end_assertions_on": True, + "assertion_disabled_test_capture_rejected": True, + "actual_failed_junit_sha256": Q.sha256(self.base / "disabled-counterexample/junit.xml"), + }) + + +if __name__ == "__main__": + if "--protocol-only" in sys.argv: + sys.argv.remove("--protocol-only") + suite = unittest.defaultTestLoader.loadTestsFromTestCase(QualificationProtocolTests) + sys.exit(not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()) + unittest.main() diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-admission-reproduction.tar.gz b/docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-admission-reproduction.tar.gz new file mode 100644 index 00000000..7a1d3c19 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-admission-reproduction.tar.gz differ diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-registration-fix-verification.tar.gz b/docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-registration-fix-verification.tar.gz new file mode 100644 index 00000000..99ee7556 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-registration-fix-verification.tar.gz differ diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-junit.xml b/docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-junit.xml new file mode 100644 index 00000000..06cd4272 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-junit.xml @@ -0,0 +1,607 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-verification.tar.gz b/docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-verification.tar.gz new file mode 100644 index 00000000..42957998 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-verification.tar.gz differ diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-fix-verification.json b/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-fix-verification.json new file mode 100644 index 00000000..e95a2ed8 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-fix-verification.json @@ -0,0 +1,167 @@ +{ + "kind": "independent_focused_fix_verification", + "reviewer": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max", + "fresh_original_integrated_reviewer": true + }, + "finding": "P2 advisory Direct registration failures block committed reads", + "disposition": "resolved", + "new_confirmed_code_findings": [], + "snapshot": { + "path": "/private/tmp/codebasegraph-cleanup-fix-review-e968", + "git_base": "e96893e025eda512b13f2a9ba754adbac45f4a49", + "coordinator_documentation_head_at_freeze": "16081af63122eea06af3acafedf67f168695c366", + "owned_overlay": { + "src/storage/direct.rs": "c40e99468c2ab0a465b7a5b5ee2a8e755c872d450da5e88cd212d91365691e25", + "src/storage/test_harness.rs": "3d94ecbe47000f8dcc77f0f2dff3a7ec3c0bdc5087b0ab3e01b3d78d1b5cdf58" + }, + "patch_sha256": "c24809e5c8dde1749b717b83347ca81afc6feac1cd6ae487d9324929f62561b6", + "all_rust_source_count": 137, + "canonical_relative_string_order_identity": "b987bc054651cb0cb4fbfd56cff0e33f5c27d92bc27cb4680d79dbcdccb1d2f4", + "equivalent_path_component_order_identity": "5a16444a8d3bee62a40508f8b9a5d118fbbe2c28c8b55c89df2900005b4405a0", + "all_per_file_hashes_match_worker_before_and_after": true, + "reviewer_source_before_after_equal": true + }, + "assessment": { + "ordinary_read": "begin_read no longer creates/decodes/repairs registration. It retains existing writer and both shared resource/physical-parent-family guards through returned lease lifetime.", + "strict_mutation": "begin_write still requires registration. recover_with_lock requires it after proving a journal exists and before ReferenceMutation, journal decode or replay effects.", + "retention": "Missing/corrupt registration survives ordinary read unchanged. Four shipping regressions assert bytes/inodes, use exclusion and no manufactured destination cohort; existing overlap validation remains unchanged.", + "source_anchors": [ + { + "line": 114, + "text": "super::maintenance_ownership::register_direct_pair(&self.layout)?;" + }, + { + "line": 155, + "text": "pub(crate) fn begin_read(&self) -> Result {" + }, + { + "line": 221, + "text": "fn recover_with_lock(&self) -> Result<(), NativeError> {" + }, + { + "line": 230, + "text": "super::maintenance_ownership::register_direct_pair(&self.layout)?;" + } + ] + }, + "reviewer_executions": { + "fixed-focused": { + "snapshot": "/private/tmp/codebasegraph-cleanup-fix-review-e968", + "command": [ + "cargo", + "test", + "--offline", + "--locked", + "--test", + "direct_read_metadata", + "integrated_probe_direct_", + "--", + "--nocapture", + "--test-threads=1" + ], + "environment": { + "REVIEW_SNAPSHOT": "/private/tmp/codebasegraph-cleanup-fix-review-e968", + "CARGO_TARGET_DIR": "/Users/rabii/Projects/Repositories/codebaseGraph/target" + }, + "returncode": 0, + "source_hashes_unchanged": true, + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/direct_read_metadata-4bce9bdfddc3e313", + "binary_sha256": "c24dcd1fe0b516731e5ecff8f617515be03829026bedc9ac8ff9d1b3df4a017d" + }, + "fixed-shipping-regressions": { + "snapshot": "/private/tmp/codebasegraph-cleanup-fix-review-e968", + "command": [ + "cargo", + "test", + "--offline", + "--locked", + "--test", + "direct_read_metadata", + "direct_committed_", + "--", + "--nocapture", + "--test-threads=1" + ], + "environment": { + "REVIEW_SNAPSHOT": "/private/tmp/codebasegraph-cleanup-fix-review-e968", + "CARGO_TARGET_DIR": "/Users/rabii/Projects/Repositories/codebaseGraph/target" + }, + "returncode": 0, + "source_hashes_unchanged": true, + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/direct_read_metadata-4bce9bdfddc3e313", + "binary_sha256": "c24dcd1fe0b516731e5ecff8f617515be03829026bedc9ac8ff9d1b3df4a017d" + }, + "fixed-valid-replay-negative": { + "snapshot": "/private/tmp/codebasegraph-cleanup-fix-review-e968", + "command": [ + "cargo", + "test", + "--offline", + "--locked", + "--test", + "strict_replay", + "integrated_probe_strict_", + "--", + "--nocapture", + "--test-threads=1" + ], + "environment": { + "REVIEW_SNAPSHOT": "/private/tmp/codebasegraph-cleanup-fix-review-e968", + "CARGO_TARGET_DIR": "/Users/rabii/Projects/Repositories/codebaseGraph/target" + }, + "returncode": 0, + "source_hashes_unchanged": true, + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/strict_replay-5b1f384ce48045da", + "binary_sha256": "1a6cea0e4b4e328e172585b1045f279d316f0b705142eca27b403f7ef89db769" + } + }, + "focused_case_counts": { + "original_reproduction_cases": { + "passed": 3, + "failed": 0 + }, + "shipping_regression_cases": { + "passed": 4, + "failed": 0 + }, + "independent_valid_prepared_journal_case": { + "passed": 1, + "failed": 0 + }, + "note": "Separate focused execution groups; do not add to overlapping coordinator172pass or earlier foundation295pass as independent acceptance coverage." + }, + "strict_valid_journal_probe": { + "source": "/private/tmp/codebasegraph-integrated-availability-probe/fix-strict/strict_replay.rs", + "source_sha256": "eea13f75119a319a75615df3eded0d676bf65492fffe62e739337ec47cb24d39", + "method": "Actually publishes a synthetic Direct storage pair; constructs and successfully decodes a valid Prepared journal and matching candidate checksums. Corrupt registration rejects producer/read replay before all six watched file byte/inode changes. Fixture-only registration restoration permits the same untouched valid journal to replay and commit successfully.", + "result": "passed" + }, + "metadata_correction": { + "classification": "documentation-only algorithm clarification; not a source or executed-result replacement", + "detail": "Original5a164 identity uses pathlib path-component ordering. Canonical relative-string ordering producesb987. Same137per-file hashes in both cases. Handoff/manifest correction preserves raw source maps, logs, JUnit and results.", + "artifacts": { + "HANDOFF.md": "4cd1c340759f5be662ad7999d6ce18e275de18bc09d5e0c6fe29b7a64198d2d0", + "manifest.json": "d53b88460899c5309b19e8a3212e8bd9275bff772106d2b27209ef268ccab683", + "identity-correction.json": "45cb5c6b00ab306ceedf3a6873805787d095bf904cfe95e9b5e3ce54e0506fc2" + } + }, + "additional_check": { + "git_diff_check": "passed" + }, + "environment": { + "platform": "macOS-26.6.2-arm64-arm-64bit", + "rustc": "rustc 1.96.0 (ac68faa20 2026-05-25)", + "cargo": "cargo 1.96.0 (30a34c682 2026-05-25)" + }, + "limitations": [ + "Storage fixtures use synthetic database bytes; no new native Ladybug query semantics or Linux/Windows qualification is claimed.", + "Independent probes use isolated storage-only Cargo packages whose direct dependencies match baseline versions; no root package rebuild/check was performed by this reviewer. Coordinator reported172/172 root check results remain separately attributed.", + "Configured code graph indexes the older original checkout. Current source identity is established by complete hash matching and the exact frozen patch, not by graph freshness.", + "Original public raw-native-handle decision, latency amendment, future collector/scheduler, migration and aggregate qualification remain pending.75existing library warnings remain the coordinator-reported full-scope limitation." + ], + "historical_finding_artifact": "/private/tmp/codebasegraph-integrated-availability-probe/integrated-review.json", + "memory_checkpoint": "Reusable lesson verified: committed reads require lifetime exclusion, while producer overlap registration remains strict for mutation/replay and cannot substitute for quiescence. Read-only reviewer leaves memory/wiki recording to coordinator.", + "shared_cargo": "released" +} diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-foundation-review.json b/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-foundation-review.json new file mode 100644 index 00000000..98212d85 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-foundation-review.json @@ -0,0 +1,103 @@ +{ + "review_kind": "fresh_read_only_integrated_foundation_review", + "reviewer_profile": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max" + }, + "baseline": "018df35e72447fc3e014e950d1d3c6648bbd4820", + "reviewed_commit": "e96893e025eda512b13f2a9ba754adbac45f4a49", + "snapshot_path": "/private/tmp/codebasegraph-cleanup-review-e968-integrated", + "snapshot_head": "e96893e025eda512b13f2a9ba754adbac45f4a49", + "source_tree_clean": true, + "scope": "Committed foundations only; automatic collector/scheduler, public native handle choice, canonical fast seal, legacy migration and full qualification remain pending.", + "findings": [ + { + "priority": "P2", + "title": "Advisory Direct registration failures block committed reads", + "location": { + "file": "src/storage/direct.rs", + "line": 157 + }, + "secondary_location": { + "file": "src/storage/maintenance_ownership.rs", + "lines": [ + 1597, + 1599, + 1604 + ] + }, + "trigger": "No journal/candidate; readable committed DB/manifest, writable preexisting writer lock and readable preexisting family/resource guards; registration is either corrupt or absent while its parent is chmod555.", + "mechanism": "begin_read propagates register_direct_pair parse/create errors before admission. resolve_direct_read propagates this into runtime resolution.", + "consequence": "Existing graph queries/health lose access because optional future-cleanup overlap metadata is unavailable.", + "baseline": "All three isolated probes pass; registration metadata is not a committed-read prerequisite.", + "reviewed": "Writable first read passes; corrupt registration fails JSON parse; readonly missing-registration fails EACCES. DB/manifest bytes preserved.", + "remedy": "Retain real writer/resource guards and mandatory ambiguous publication replay; preserve/report failed overlap registration as unknown maintenance instead of failing a committed read. Ensure collection never treats failed registration as proven idle or overwrite corrupt evidence.", + "verification": "Promote the three isolated admission probes into regressions; verify runtime resolution still reads a real graph and maintenance stays unknown/pending for invalid or unwritable registration." + } + ], + "executed_focused_evidence": { + "reviewed": { + "snapshot": "/private/tmp/codebasegraph-cleanup-review-e968-integrated", + "command": [ + "cargo", + "test", + "--offline", + "--locked", + "--test", + "direct_read_metadata", + "integrated_probe_direct_", + "--", + "--nocapture", + "--test-threads=1" + ], + "environment": { + "REVIEW_SNAPSHOT": "/private/tmp/codebasegraph-cleanup-review-e968-integrated", + "CARGO_TARGET_DIR": "/Users/rabii/Projects/Repositories/codebaseGraph/target" + }, + "returncode": 101, + "source_hashes_unchanged": true, + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/direct_read_metadata-4bce9bdfddc3e313", + "binary_sha256": "f6790e8d6b07dcdd20437bc0baf216d5989e60c87f8fc6514871797cb3c50d48" + }, + "baseline": { + "snapshot": "/private/tmp/codebasegraph-integrated-availability-probe/baseline", + "command": [ + "cargo", + "test", + "--offline", + "--locked", + "--test", + "direct_read_metadata", + "integrated_probe_direct_", + "--", + "--nocapture", + "--test-threads=1" + ], + "environment": { + "REVIEW_SNAPSHOT": "/private/tmp/codebasegraph-integrated-availability-probe/baseline", + "CARGO_TARGET_DIR": "/Users/rabii/Projects/Repositories/codebaseGraph/target" + }, + "returncode": 0, + "source_hashes_unchanged": true, + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/direct_read_metadata-4bce9bdfddc3e313", + "binary_sha256": "38e6947b1ea4d8e2319459bc95daa5553cacd479d9e0440c293959ce70f77273" + }, + "distinct_probe_count": 3, + "counts_must_not_be_summed_with_prior_or_coordinator_runs": true + }, + "probe_files": { + "probe.rs": "ace66a175f8774b89ff3f960929aa69ed6d9c4804b1f193fda8c48a41420a10c", + "Cargo.toml": "acf85ab74d690566d7f0f340e503607e18d15fe851df657e72bb78d5628ecb5b", + "Cargo.lock": "2eb043ceac36eeb2aa62090ca1558175f046d76b21478c7df1527be3fc500c28", + "run_probe.py": "49be1301056ee0ddfa8295d51f04e4e4b04d964b3b9f7cc80ec05ba0adcf7329", + "baseline-dependency-versions.json": "a46d4361b463c6e599a288a1e93b92ce070bcaa7a46ea6c246bcaa308bec5592" + }, + "limitations": [ + "Only deterministic local storage admission probes were executed by this reviewer; native DB bytes were synthetic because failure occurs before opening/inspecting native contents. Reachability into runtime resolution established statically.", + "Coordinator-reported295pass combined run and checks were not rerun or counted as this reviewer execution.", + "Configured graph indexes older original checkout; existing files located through it, new foundation files/test fixtures mapped using frozen commit and Scryer.", + "No live cleanup, daemon operation, source/test/wiki/Scryer mutation, native Linux/Windows qualification or full five-step closure.", + "No second confirmed defect found; pending runtime and qualification work remains a gate." + ], + "memory_recording_decision": "Do not create memory during this read-only review; the confirmed unfixed defect and probe are concrete fix evidence. Coordinator should decide durable memory after verified resolution." +} diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/integration-binaries.json.gz b/docs/testing/graph-disk-cleanup-foundation-evidence/integration-binaries.json.gz new file mode 100644 index 00000000..4dc2d1c2 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-foundation-evidence/integration-binaries.json.gz differ diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/integration-check.log.gz b/docs/testing/graph-disk-cleanup-foundation-evidence/integration-check.log.gz new file mode 100644 index 00000000..29801608 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-foundation-evidence/integration-check.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/integration-junit.xml b/docs/testing/graph-disk-cleanup-foundation-evidence/integration-junit.xml new file mode 100644 index 00000000..62dd165b --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/integration-junit.xml @@ -0,0 +1,599 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/integration-tests.log.gz b/docs/testing/graph-disk-cleanup-foundation-evidence/integration-tests.log.gz new file mode 100644 index 00000000..de306875 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-foundation-evidence/integration-tests.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-independent-junit.xml b/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-independent-junit.xml new file mode 100644 index 00000000..b8050062 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-independent-junit.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-rebuild-provenance.json b/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-rebuild-provenance.json new file mode 100644 index 00000000..534bda2f --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-rebuild-provenance.json @@ -0,0 +1,28 @@ +{ + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-ffa80bf96221ee79", + "binary_sha256": "9c17fa8b7204c785f0eab75195233a1b4121349472d1b1da1b57402098b2e4ba", + "listed_unit_tests": 561, + "selected_cases": [ + "api::context::tests::dc_epoch_optional_direct_cleanup_keeps_both_runtime_selectors_readable_and_pending: test", + "api::context::tests::dc_epoch_optional_managed_cleanup_keeps_runtime_readable_and_pending: test", + "storage::maintenance_ownership::tests::dc_epoch_optional_direct_cleanup_preserves_committed_read_but_not_journal_replay: test", + "storage::maintenance_ownership::tests::dc_epoch_optional_managed_cleanup_preserves_committed_read_and_residue: test", + "storage::maintenance_ownership::tests::review_epoch_corrupt_control_blocks_direct_read_with_unpublished_candidate: test", + "storage::maintenance_ownership::tests::review_epoch_corrupt_control_blocks_managed_runtime_with_retained_generation: test" + ], + "review_probe_tests_added": 2, + "original_frozen_unit_count": 559, + "junit": "/private/tmp/cleanup-s2-read-fix-review-4i1igj5d/junit.xml", + "junit_sha256": "640d1dc448e77ea726b0468255a488c9badce39a955176e7fa63158263fcdc76", + "junit_summary": { + "name": "nextest-run", + "tests": "6", + "failures": "0", + "errors": "0", + "uuid": "80fc793e-7da4-425f-a25e-23a7aeec1197", + "timestamp": "2026-10-21T04:01:06.534+09:30", + "time": "3.167" + }, + "root_rebuilt_from": "/private/tmp/cleanup-s2-read-fix-review-4i1igj5d/repo", + "profile_request": "profile.test.package.codebase-graph.codegen-units=129" +} \ No newline at end of file diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-independent-junit.xml b/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-independent-junit.xml new file mode 100644 index 00000000..21b4fdd5 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-independent-junit.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-review-evidence.json b/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-review-evidence.json new file mode 100644 index 00000000..1817eb10 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-review-evidence.json @@ -0,0 +1,31 @@ +{ + "scope": "Independent narrow postpublication S4 review; full DC-S4 not qualified", + "reviewer": "GPT-6.1 Sol Max, independent initial reviewer continued read-only", + "manifest": "/private/tmp/codebasegraph-s4-postpublication-review-et3thm39/reconstructed-manifest.json", + "manifest_sha256": "d070b7c622cf49812d78fdf1d2249c2567fb63d1d51b1f5eaa8ff0d8134c704f", + "junit": "/private/tmp/codebasegraph-s4-postpublication-review-et3thm39/nextest/s4-postpublication-review/junit.xml", + "junit_sha256": "3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216", + "run": { + "name": "nextest-run", + "tests": "17", + "failures": "0", + "errors": "0", + "uuid": "ce7bf792-cc80-48fa-ac01-54ce87aba79e", + "timestamp": "2026-10-21T03:57:50.551+09:30", + "time": "21.397" + }, + "command": "CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo nextest run --offline --locked --lib --config 'profile.test.package.codebase-graph.codegen-units=128' --config-file /private/tmp/codebasegraph-s4-postpublication-review-et3thm39/nextest.toml --profile s4-postpublication-review -E 'test(api::materialization::tests) | test(protocol::maintenance_summary_tests)' --test-threads 1", + "forced_root_package_rebuild": true, + "excluded_first_run": { + "junit": "/private/tmp/codebasegraph-s4-postpublication-review-et3thm39/first-reused-target-junit.xml", + "sha256": "ed70ff0327920ea194f4df6fd76ceac547cd67db2ccdc03b96c06f8c9c4d408c", + "reason": "No root rebuild; 559 total tests differed from frozen source. Forced rebuild restored expected 555 tests." + }, + "confirmed_findings": [], + "limits": [ + "Managed recover_and_gc error classification unchanged and pending typed handoff", + "No owned collector scheduling or registered-key convergence qualification", + "Existing SystemMaintenanceClock unused warning", + "No native Windows or MSRV qualification" + ] +} diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/s4-reused-target-excluded-junit.xml b/docs/testing/graph-disk-cleanup-foundation-evidence/s4-reused-target-excluded-junit.xml new file mode 100644 index 00000000..2cc539d2 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-evidence/s4-reused-target-excluded-junit.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-foundation-evidence/unfinished-work-and-scryer-source.tar.gz b/docs/testing/graph-disk-cleanup-foundation-evidence/unfinished-work-and-scryer-source.tar.gz new file mode 100644 index 00000000..b6b519cd Binary files /dev/null and b/docs/testing/graph-disk-cleanup-foundation-evidence/unfinished-work-and-scryer-source.tar.gz differ diff --git a/docs/testing/graph-disk-cleanup-foundation-integration.json b/docs/testing/graph-disk-cleanup-foundation-integration.json new file mode 100644 index 00000000..f5e9d646 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-foundation-integration.json @@ -0,0 +1,409 @@ +{ + "path": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/integration-candidate-p3vxv89j", + "base_head": "a313b4d713f534831d99e8eb5daacec1f983540c", + "s2_frozen_owned_identity": "235548b4195215d9781ed885b424a34884694fa2b2e3e4d940afdcc7ca0d8b0f", + "overlays": { + "src/api/materialization.rs": "446e276c15cab8f5a35ad48e2df77ba52475812c904ef04caa545ef216622651", + "src/protocol.rs": "c56b04d18060e0ae2ce43e4c9d5b59535078d7186bfcac819005d2605ae01b3d", + "src/artifact_store.rs": "8a248d0f2a9e33b6b22b75548441de661fc8413a346fe0bb5512d85b1cbfa598" + }, + "source_sha256": { + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "b038192e4cc8258c94c7c524f9ed565017e45d7e28092b09e5490d4836db0eed", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "51230173cd6ea2767f1ec95306f3d3d2c42a8b07b20e56ec3c7661cbb4b96775", + "src/api/graph_read.rs": "0f0f6da64105186d884dab60dfb5e34e3b948a41ad6c8ca494b4904b4b9a1ab4", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "446e276c15cab8f5a35ad48e2df77ba52475812c904ef04caa545ef216622651", + "src/api/mod.rs": "3e2b72f33f55a3670764ecde9d90c844b6cb8d5d59adee131aaef2dd79dd769b", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52", + "src/api/refresh.rs": "1cebe4ab394d446e4c2b99c34447182b8b2ac4135612040ffda200eefb3ac595", + "src/artifact_store.rs": "8a248d0f2a9e33b6b22b75548441de661fc8413a346fe0bb5512d85b1cbfa598", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator/tests.rs": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252", + "src/coordinator.rs": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "c783ba112f44ce247be532c475b5e39a1bdc8b19db0f1e7d27f627fcf93d3c6b", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "32510a0dccc98be71174eb0dcd5a4e72c1164d42f1a25d77d85c8bfcc534f5fd", + "src/db_writer/phase.rs": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "10fcfc158e6fa8d86ab07cbb2ccec13464488b7ddced929f6b011c0e614f6ec7", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "f2b7ef995dd0abdb4a927a3b2a20b9ab39d9f74c238711d1ab7d806c3ad4ed44", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e", + "src/materialization_worker.rs": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663", + "src/process_lifecycle/unix.rs": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d", + "src/process_lifecycle/windows.rs": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "c56b04d18060e0ae2ce43e4c9d5b59535078d7186bfcac819005d2605ae01b3d", + "src/scan.rs": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da", + "src/staging_writer/files.rs": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "src/storage/direct.rs": "1099ec57efbaf3e0b7f31f4227c78082e74a01b6dfc1347e247960b0fe8da20e", + "src/storage/inventory.rs": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "c6296037d04a0120cccdc405052046355d721fc2255929c7bace0e4d1a2def2b", + "src/storage/maintenance_ownership.rs": "6566a1cd2f86084f2b4549e499d93854bb8e8c48a15794e9085b7d407ea21d06", + "src/storage/managed.rs": "aee08a9c6f09640bde3d2d0a3b50690391e294d308561544e94079c9abe2041d", + "src/storage/mod.rs": "1549d70674632db6b7233f23f0789055e162c224cf1625f8f15cd8b208bb3eb6", + "src/storage/run_workspace.rs": "120d4c61691a97aa4c9bb4c29c2487a3a2b53e45a63ce5288e1f5a0e587eb877", + "src/storage/test_harness.rs": "5fcf6cc2e6f9b79dc87b1f8affe70981f870d8df12c92500bff407723710feb6", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d" + }, + "excluded": "Unqualified S3 validator/cursor/job and scheduler prototypes are excluded from shipping source; exact recovery patch is in the nonshipping source archive.", + "status": "Historical295test checkpoint retained; current source superseded by final_verified_snapshot with299passing affected executions. Full specifications and warnings remain pending.", + "checks": { + "workspace_all_targets_exit": 0, + "format_exit": 0, + "diff_check_exit": 0, + "tests_exit": 0, + "passed": 295, + "filtered_or_not_run": 407, + "junit": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/integration-candidate-p3vxv89j/target/nextest/cleanup-integrated/junit.xml", + "junit_sha256": "3562f1cb18f27b76ecd94f3b71770d2659b91836e24c37f9bebc7c1c2adb8e76", + "run_id": "7f5466e4-cad5-4aeb-96cb-2390256fb386", + "package_profile_override": "profile.test.package.codebase-graph.codegen-units=137", + "source_before_after_identical": true, + "known_remaining_lib_warnings": 75, + "visible_root_rebuild": "Compiling codebase-graph v3.0.4 (/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/integration-candidate-p3vxv89j)" + }, + "binary_identities_after_execution": { + "codebase-graph": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-aa19afd4d3cb417f", + "sha256_after_execution": "817e72ef165f9c93703af0f949baa39a1cf97ec8b3cd25391e11107234cf75df" + }, + "codebase-graph::agent_hooks_process": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/agent_hooks_process-531202a66093eb1f", + "sha256_after_execution": "06a51fb76d941f6bced46567318b76a0e9485b95aef3e4a73116f936509e6e51" + }, + "codebase-graph::auto_repo_root": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/auto_repo_root-5880c5d3a6540212", + "sha256_after_execution": "13e37b1efa7a027996c93a1876c136bed31e400aeaf2da91fc088ac81a8bf46f" + }, + "codebase-graph::coordinator_process": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/coordinator_process-c61c04316ef3882f", + "sha256_after_execution": "0b12829ffc7dd0e0f870de95aa07123f2ced7ac87e4d204a17d0e6d8404cc778" + }, + "codebase-graph::generation_storage": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/generation_storage-548b33930d07cb68", + "sha256_after_execution": "01769cdbfcbc85c8345259852f8f28cb9eb74837f165a04d2cd9ca8b04f89df4" + }, + "codebase-graph::http_admission_process": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/http_admission_process-22149a2730f51bc4", + "sha256_after_execution": "8431900516304b2204c2b85783ac6e4e45f18a8c8a10a96cd3dd0d64c8a2100f" + }, + "codebase-graph::http_daemon_process": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/http_daemon_process-9741d209db775aa4", + "sha256_after_execution": "85e61cf2c179e8e452225fb95c4d3d677a1b23baf99669bb2691256276701621" + }, + "codebase-graph::native_request_identity": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/native_request_identity-c051745a2dfc0c72", + "sha256_after_execution": "640413541de1cdd326d6b47bfad63f268317151892b244cbcf3c1dbbcb7b9f16" + }, + "codebase-graph::refresh_recovery_process": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/refresh_recovery_process-cc2a509b2ae12afd", + "sha256_after_execution": "0e67938911da003ab57d354dff22c9f72572c8f7450577d092013ce504d8a830" + }, + "codebase-graph::storage_foundation": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_foundation-5371f69e25e12540", + "sha256_after_execution": "eb58a62a879eeb8ef7136ec51ebccc61c3ecc25fd95c5eaf9a8321d3fa53bf69" + }, + "codebase-graph::storage_maintenance": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_maintenance-4001876baabee9e7", + "sha256_after_execution": "b8ed8adaf1ca3e2a083858efd3af859ba42ca875df963236842b0bc6d5c746e4" + }, + "codebase-graph::bin/codebase-graph": { + "binary_path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-6f57ee7a2c156b1e", + "sha256_after_execution": "2b8a5101de286ffbfbe93e8d6eed9f1e3bc8690460eea3472628e82c1f174821" + } + }, + "integration_commit": "e96893e025eda512b13f2a9ba754adbac45f4a49", + "schema_version": 1, + "scope": "Reviewed storage cleanup foundations only; all five specifications remain incomplete.", + "artifacts_directory": "graph-disk-cleanup-foundation-evidence", + "artifacts": { + "integration-junit.xml": { + "sha256": "3562f1cb18f27b76ecd94f3b71770d2659b91836e24c37f9bebc7c1c2adb8e76", + "original_sha256": "3562f1cb18f27b76ecd94f3b71770d2659b91836e24c37f9bebc7c1c2adb8e76", + "encoding": "identity", + "source": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/integration-candidate-p3vxv89j/target/nextest/cleanup-integrated/junit.xml" + }, + "integration-check.log.gz": { + "sha256": "d8568eedc674aabe90e1a136722e71275602269e4132c7a90654ccb28c563911", + "original_sha256": "7832a09a65dad0b0fb6b67f44d6ead7427e83825f430493fb8cf4e6e09d72e64", + "encoding": "gzip", + "source": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/integration-check.log" + }, + "integration-tests.log.gz": { + "sha256": "aeb4eca36c07170d46718e713a6b6a568b2786295a3ede992dd802a41d3aa221", + "original_sha256": "77f078bcb526e173cab578a47b91093e42923f8850128c5ba8d6787e7044eae6", + "encoding": "gzip", + "source": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/integration-tests.log" + }, + "integration-binaries.json.gz": { + "sha256": "2a9cf8f1e9abbcb72dd0b84efa3dcc3fc7e670d780dff63603c2b5fcfd0046b0", + "original_sha256": "599fd0962dc71ef5dd0be7354ac2d4de5ee9797d5290c0c7e9b735b6bda42d4f", + "encoding": "gzip", + "source": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/integration-binaries.json" + }, + "s4-postpublication-independent-junit.xml": { + "sha256": "3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216", + "original_sha256": "3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216", + "encoding": "identity", + "source": "/private/tmp/codebasegraph-s4-postpublication-review-et3thm39/nextest/s4-postpublication-review/junit.xml" + }, + "s4-reused-target-excluded-junit.xml": { + "sha256": "ed70ff0327920ea194f4df6fd76ceac547cd67db2ccdc03b96c06f8c9c4d408c", + "original_sha256": "ed70ff0327920ea194f4df6fd76ceac547cd67db2ccdc03b96c06f8c9c4d408c", + "encoding": "identity", + "source": "/private/tmp/codebasegraph-s4-postpublication-review-et3thm39/first-reused-target-junit.xml" + }, + "s4-postpublication-review-evidence.json": { + "sha256": "6101c50b30b24228d473ff86ab98c4d6d16128fd8e98195705df595ea5d22729", + "original_sha256": "6101c50b30b24228d473ff86ab98c4d6d16128fd8e98195705df595ea5d22729", + "encoding": "identity", + "source": "/private/tmp/codebasegraph-s4-postpublication-review-et3thm39/review-evidence.json" + }, + "s2-epoch-read-fix-independent-junit.xml": { + "sha256": "640d1dc448e77ea726b0468255a488c9badce39a955176e7fa63158263fcdc76", + "original_sha256": "640d1dc448e77ea726b0468255a488c9badce39a955176e7fa63158263fcdc76", + "encoding": "identity", + "source": "/private/tmp/cleanup-s2-read-fix-review-4i1igj5d/junit.xml" + }, + "s2-epoch-read-fix-rebuild-provenance.json": { + "sha256": "acef411c4c6d5a68565001d23d69da28bd483350e1ea74dfc64a15e98fbbfdc0", + "original_sha256": "acef411c4c6d5a68565001d23d69da28bd483350e1ea74dfc64a15e98fbbfdc0", + "encoding": "identity", + "source": "/private/tmp/cleanup-s2-read-fix-review-4i1igj5d/rebuild-provenance.json" + }, + "unfinished-work-and-scryer-source.tar.gz": { + "sha256": "b938db71e07857f73dabfc852c94e6006321caf0dc360c3cfbe0250423066294", + "encoding": "gzip/tar", + "status": "nonshipping recovery archive; no runtime files", + "member_count": 7 + }, + "integrated-foundation-review.json": { + "sha256": "f3286fa16726d54717d3fa90d79ed817e86878af3a6bab13f416462d9ffe9549", + "original_sha256": "f3286fa16726d54717d3fa90d79ed817e86878af3a6bab13f416462d9ffe9549", + "encoding": "identity", + "source": "/private/tmp/codebasegraph-integrated-availability-probe/integrated-review.json" + }, + "direct-read-admission-reproduction.tar.gz": { + "sha256": "0c8f21fb29f1137b5f509aa0a904d2ae441729bb935a1e02aaed8d5c8d80a5b9", + "encoding": "gzip/tar", + "status": "historical confirmed-defect reproduction; excludes binaries", + "member_count": 15 + }, + "integrated-fix-verification.json": { + "sha256": "5fd30a710e42520ee3732b73470870687f8511830cca98ce2e218062abd3279e", + "original_sha256": "5fd30a710e42520ee3732b73470870687f8511830cca98ce2e218062abd3279e", + "encoding": "identity", + "source": "/private/tmp/codebasegraph-integrated-availability-probe/integrated-fix-verification.json" + }, + "direct-read-registration-fix-verification.tar.gz": { + "sha256": "9141fd2c55d0a8b074f6e0b324c4984c5ba0e429c6dda15a7160af01aef1b722", + "encoding": "gzip/tar", + "status": "reviewed frozen fix worker and independent verification; excludes binaries", + "member_count": 33 + }, + "final-foundation-verification.tar.gz": { + "sha256": "3703829e77e3f4d2f47b64b40e187bbda7b3e66b7628b1e73f5f7ab0757b2a74", + "encoding": "gzip/tar", + "status": "final source/checks/binary identities, passing JUnit and preserved sandbox failure; no binaries", + "member_count": 14 + }, + "final-foundation-junit.xml": { + "sha256": "698d41772c871f1398e5cf926c415a93af41ef65bd73b298220323266a7e761f", + "original_sha256": "698d41772c871f1398e5cf926c415a93af41ef65bd73b298220323266a7e761f", + "encoding": "identity", + "source": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/final-foundation-verification/junit.xml" + } + }, + "toolchain": { + "rustc": "rustc 1.96.0 (ac68faa20 2026-05-25)", + "cargo": "cargo 1.96.0 (30a34c682 2026-05-25)" + }, + "pending_owner_decisions": [ + "public native database-handle compatibility", + "proposed DC-S3/DC-S4 latency amendment" + ], + "remaining_gates": [ + "bounded admission and collector seals", + "entry-origin and complete-reference proof", + "actual collection, backup recovery and controlled migration", + "automatic lifecycle scheduling/reporting", + "mixed Managed recovery/cleanup classification", + "whole-state churn/control, graph equivalence and cache reuse", + "warning-free clippy/checks and native platforms/MSRV", + "fresh full-runtime integrated review and aggregate closure" + ], + "aggregate_closed": false, + "scryer_local_status": { + "change": "chg-6vx5tj", + "aggregate_closed": false, + "producer_node": "node-yp5307", + "responsibility": "resp-ejzb4s", + "attached_executed_tests": 20, + "responsibilities_with_actual_verdicts": 41, + "unrelated_missing_verdicts_retained": true, + "latest_ingested_run": "bda126c4-1f9e-4980-9545-acf34ec6de22" + }, + "integrated_review": { + "status": "P2 Direct read-registration defect resolved; no new confirmedP0-P3 on exact final source. Full collector/scheduler and aggregate acceptance pending.", + "model": "gpt-6.1-sol", + "reasoning_effort": "max", + "reviewed_commit": "e96893e025eda512b13f2a9ba754adbac45f4a49", + "baseline": "018df35e72447fc3e014e950d1d3c6648bbd4820", + "report": "graph-disk-cleanup-foundation-evidence/integrated-foundation-review.json", + "focused_verification": "graph-disk-cleanup-foundation-evidence/integrated-fix-verification.json", + "final_code_commit": "077a8d8e1e75dbca553a4d9b0f65412ca9c5def1" + }, + "prototype_recovery_check": { + "base_commit": "e96893e025eda512b13f2a9ba754adbac45f4a49", + "exact_reconstruction_verified": true, + "path_sha256": { + "src/artifact_store.rs": "c210fdfad30a1a22c08756069d10e28bf7ee58dd1d2cd15ac5bb86bacf2662a2", + "src/storage/mod.rs": "68db9239927733a14f0853d5b7330ba19c9114fe8c3cac3d56d67ed2cf3ff981", + "src/storage/maintenance.rs": "b237227071c56e84b4f7a281c1c6fbcb6003b3954a2cd4d4f9c0ecda114b5fa8", + "src/storage/maintenance_scheduler.rs": "fcec504f2f55a227b441d5e103ce18e4caa69c4fd9610070a9f0eaec45e9a90f" + }, + "shipping_working_tree_restored": true + }, + "wiki_validation": { + "configured_bundle_root": "/Users/rabii/Projects/Repositories/codebaseGraph/knowledge", + "recommended_accepted": true, + "diagnostics": 0, + "links_diagnostics": 0, + "build_routes": 302, + "authored_pages_copied_without_edits": true, + "isolated_checkout_validation": "unavailable: cloned bundle root is not configured with the registered MCP server", + "mechanical_relative_path_checks": 15, + "mechanical_check_is_not_wiki_qualification": true + }, + "final_verified_snapshot": { + "code_commit": "077a8d8e1e75dbca553a4d9b0f65412ca9c5def1", + "base_code_commit": "e96893e025eda512b13f2a9ba754adbac45f4a49", + "source_delta": { + "src/storage/direct.rs": "c40e99468c2ab0a465b7a5b5ee2a8e755c872d450da5e88cd212d91365691e25", + "src/storage/test_harness.rs": "3d94ecbe47000f8dcc77f0f2dff3a7ec3c0bdc5087b0ab3e01b3d78d1b5cdf58" + }, + "src_rust_count": 137, + "canonical_source_identity": "b987bc054651cb0cb4fbfd56cff0e33f5c27d92bc27cb4680d79dbcdccb1d2f4", + "tracked_rust_and_manifests": 196, + "source_before_after_identical": true, + "binary_identities_before_after_identical": 12, + "passing_affected_testcases": 299, + "filtered_or_not_run": 407, + "run_id": "bda126c4-1f9e-4980-9545-acf34ec6de22", + "junit_sha256": "698d41772c871f1398e5cf926c415a93af41ef65bd73b298220323266a7e761f", + "workspace_all_targets_exit": 0, + "format_exit": 0, + "warnings": [ + "warning: `codebase-graph` (lib) generated 75 warnings" + ], + "scope": "Reviewed foundations only, no automatic collector/scheduler/migration closure.", + "sandbox_failure_preserved": true + }, + "pull_request": { + "url": "https://github.com/rabii-chaarani/codebaseGraph/pull/153", + "draft": true, + "code_commit": "077a8d8e1e75dbca553a4d9b0f65412ca9c5def1", + "full_scope_closed": false + } +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/baseline-macos.log b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/baseline-macos.log new file mode 100644 index 00000000..818287d3 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/baseline-macos.log @@ -0,0 +1,15 @@ + +running 1 test +test storage::managed::transaction_janitor_tests::managed_read_recovery_closes_registered_run_without_replaying ... FAILED + +failures: + +failures: + storage::managed::transaction_janitor_tests::managed_read_recovery_closes_registered_run_without_replaying + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 827 filtered out; finished in 0.18s + + +thread 'storage::managed::transaction_janitor_tests::managed_read_recovery_closes_registered_run_without_replaying' (31344599) panicked at src/storage/managed.rs:2150:13: +fixture must have genuine producer registration +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/binaries.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/binaries.json new file mode 100644 index 00000000..282604f7 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/binaries.json @@ -0,0 +1,5 @@ +{ + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-ffa80bf96221ee79": "ba9ed3337b68e6c89de34533ec062ca1ab98503b81f2d2b7629f37a370edf145", + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_foundation-19729f82bc7a7900": "a13193d3898989ee5fe4df1f6e46e9c04789b630fd11c53b02a31b802b7a3b6f", + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/codebase-graph": "2a43962065ed051975613c9ae0a3c059899f4264370b14d8d2c281f2f757e71d" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/canonical-temp-probe.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/canonical-temp-probe.log.gz new file mode 100644 index 00000000..fd1eb1f6 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/canonical-temp-probe.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/clippy.log b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/clippy.log new file mode 100644 index 00000000..621e6b70 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/clippy.log @@ -0,0 +1,2 @@ + Checking codebase-graph v3.0.4 (/Users/rabii/.codex/worktrees/pr153-native-ci-fixes/codebaseGraph) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.35s diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/initial-focused.junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/initial-focused.junit.xml new file mode 100644 index 00000000..455f2667 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/initial-focused.junit.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-binaries.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-binaries.json new file mode 100644 index 00000000..54cc9cd2 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-binaries.json @@ -0,0 +1,5 @@ +{ + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-ffa80bf96221ee79": "5a6cf2bc35e651aa537c2478d74a4bcee0c06db89db0edfff88a6934fa9f357b", + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/storage_foundation-19729f82bc7a7900": "a13193d3898989ee5fe4df1f6e46e9c04789b630fd11c53b02a31b802b7a3b6f", + "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/codebase-graph": "2a43962065ed051975613c9ae0a3c059899f4264370b14d8d2c281f2f757e71d" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-failure.junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-failure.junit.xml new file mode 100644 index 00000000..4cfb953c --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-failure.junit.xml @@ -0,0 +1,698 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + thread 'storage::maintenance_collector_tests::dc_s5_real_mcp_and_writer_progress_during_preparation_and_changed_epoch_blocks_stale_effect' (31362833) panicked at src/storage/../../tests/support/storage_maintenance/collector.rs:2394:9: +actual advisory preparation never reached its root-scoped hook: {"active_generation":"71574-1792834909635347000-1","active_generation_published_at_unix_ms":1792834918048,"cleanup_pending":true,"coordinator":{"endpoint":"127.0.0.1:53343","pid":71574,"role":"owner"},"database_exists":true,"database_path":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository/.codebaseGraph/storage/generations/gen-71574-1792834909635347000-1/graph.ldb","error":null,"freshness":"overdue","graph_readable":true,"logical_database_bytes":6746926,"maintenance":{"bytes":{"by_category":{"active_cache":{"allocated_bytes":110592,"logical_bytes":103109},"active_graph":{"allocated_bytes":6975488,"logical_bytes":6749847},"artifact_temporary":{"allocated_bytes":null,"logical_bytes":null},"coordination":{"allocated_bytes":720896,"logical_bytes":24977},"leased_generation":{"allocated_bytes":null,"logical_bytes":null},"legacy_staging":{"allocated_bytes":null,"logical_bytes":null},"live_run":{"allocated_bytes":3022848,"logical_bytes":930658},"recovery_evidence":{"allocated_bytes":null,"logical_bytes":null},"retired_direct":{"allocated_bytes":null,"logical_bytes":null},"unknown":{"allocated_bytes":null,"logical_bytes":null}},"deferred":{"allocated_bytes":3022848,"logical_bytes":930658},"potential_reclamation":{"allocated_bytes":null,"logical_bytes":null},"retained":{"allocated_bytes":7806976,"logical_bytes":6877933},"total":{"allocated_bytes":null,"logical_bytes":null},"unknown":{"allocated_bytes":null,"logical_bytes":null}},"errors":[],"inventory_status":null,"last_attempt":{"attempted_entries":null,"continuation_phase":null,"deleted_entries":0,"finished_at_unix_ms":1792834979401,"reclaimed_allocated_bytes":null,"started_at_unix_ms":1792834979338,"status":"deferred"},"measurement_complete":false,"observation_age_ms":15440,"observation_complete":false,"observed_at_unix_ms":1792834979392,"pending":true,"reasons":["maintenance_measurement_partial_lower_bounds","unknown_entries_retained","maintenance_admission_busy","maintenance_candidate_deferred","maintenance_observation_incomplete"],"requested":true,"running":false},"manifest_exists":true,"manifest_path":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository/.codebaseGraph/storage/generations/gen-71574-1792834909635347000-1/manifest.json","ok":true,"pending_runs":0,"physical_database_bytes":6963200,"refresh":{"backend":"poll","coalesced_event_count":0,"deduplicated_refresh_count":99,"dirty_epoch":99,"effective_root":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository","enabled":true,"filtered_event_count":0,"last_changed_paths":0,"last_database_written":false,"last_deleted":0,"last_error":null,"last_error_count":0,"last_event_count":0,"last_noop_reason":"active_generation_current","last_rebuilt":0,"last_refresh_unix_ms":1792834994263,"last_retry_unix_ms":null,"last_successful_reconciliation_unix_ms":1792834994263,"leader_pid":71574,"memory_limits":{"max_parallelism":1,"rust_memory_mib":384,"spill_chunk_mib":32,"worker_memory_mib":768},"next_retry_unix_ms":null,"oldest_pending_unix_ms":null,"overflow_count":0,"pending":false,"phase_high_water_marks":{},"reconcile_interval_ms":250,"reconciled_epoch":99,"refreshing":false,"role":"leader","spill_bytes":0,"state":"running","task_alive":true,"worker_pid":null},"refresh_health":{"dirty_epoch":99,"effective_root":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository","freshness":"overdue","last_error":null,"last_successful_reconciliation_unix_ms":1792834994263,"next_retry_unix_ms":null,"oldest_pending_unix_ms":null,"pending":false,"readiness":"ready","reconciled_epoch":99,"state":"running","task_alive":true},"refresh_readiness":"ready","remediation":null,"repo_root":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository","storage_format":"managed_v2","storage_root":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository/.codebaseGraph/storage","total_nodes":138,"writable":true} +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +retained failed storage qualification fixture: /private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository + +running 1 test +test storage::maintenance_collector_tests::dc_s5_real_mcp_and_writer_progress_during_preparation_and_changed_epoch_blocks_stale_effect ... FAILED + +failures: + +failures: + storage::maintenance_collector_tests::dc_s5_real_mcp_and_writer_progress_during_preparation_and_changed_epoch_blocks_stale_effect + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 828 filtered out; finished in 86.49s + + + canonical-only fixture preparation: 1 known completed run(s) removed by established compatibility cleanup; this does not qualify terminal GC + +thread 'storage::maintenance_collector_tests::dc_s5_real_mcp_and_writer_progress_during_preparation_and_changed_epoch_blocks_stale_effect' (31362833) panicked at src/storage/../../tests/support/storage_maintenance/collector.rs:2394:9: +actual advisory preparation never reached its root-scoped hook: {"active_generation":"71574-1792834909635347000-1","active_generation_published_at_unix_ms":1792834918048,"cleanup_pending":true,"coordinator":{"endpoint":"127.0.0.1:53343","pid":71574,"role":"owner"},"database_exists":true,"database_path":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository/.codebaseGraph/storage/generations/gen-71574-1792834909635347000-1/graph.ldb","error":null,"freshness":"overdue","graph_readable":true,"logical_database_bytes":6746926,"maintenance":{"bytes":{"by_category":{"active_cache":{"allocated_bytes":110592,"logical_bytes":103109},"active_graph":{"allocated_bytes":6975488,"logical_bytes":6749847},"artifact_temporary":{"allocated_bytes":null,"logical_bytes":null},"coordination":{"allocated_bytes":720896,"logical_bytes":24977},"leased_generation":{"allocated_bytes":null,"logical_bytes":null},"legacy_staging":{"allocated_bytes":null,"logical_bytes":null},"live_run":{"allocated_bytes":3022848,"logical_bytes":930658},"recovery_evidence":{"allocated_bytes":null,"logical_bytes":null},"retired_direct":{"allocated_bytes":null,"logical_bytes":null},"unknown":{"allocated_bytes":null,"logical_bytes":null}},"deferred":{"allocated_bytes":3022848,"logical_bytes":930658},"potential_reclamation":{"allocated_bytes":null,"logical_bytes":null},"retained":{"allocated_bytes":7806976,"logical_bytes":6877933},"total":{"allocated_bytes":null,"logical_bytes":null},"unknown":{"allocated_bytes":null,"logical_bytes":null}},"errors":[],"inventory_status":null,"last_attempt":{"attempted_entries":null,"continuation_phase":null,"deleted_entries":0,"finished_at_unix_ms":1792834979401,"reclaimed_allocated_bytes":null,"started_at_unix_ms":1792834979338,"status":"deferred"},"measurement_complete":false,"observation_age_ms":15440,"observation_complete":false,"observed_at_unix_ms":1792834979392,"pending":true,"reasons":["maintenance_measurement_partial_lower_bounds","unknown_entries_retained","maintenance_admission_busy","maintenance_candidate_deferred","maintenance_observation_incomplete"],"requested":true,"running":false},"manifest_exists":true,"manifest_path":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository/.codebaseGraph/storage/generations/gen-71574-1792834909635347000-1/manifest.json","ok":true,"pending_runs":0,"physical_database_bytes":6963200,"refresh":{"backend":"poll","coalesced_event_count":0,"deduplicated_refresh_count":99,"dirty_epoch":99,"effective_root":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository","enabled":true,"filtered_event_count":0,"last_changed_paths":0,"last_database_written":false,"last_deleted":0,"last_error":null,"last_error_count":0,"last_event_count":0,"last_noop_reason":"active_generation_current","last_rebuilt":0,"last_refresh_unix_ms":1792834994263,"last_retry_unix_ms":null,"last_successful_reconciliation_unix_ms":1792834994263,"leader_pid":71574,"memory_limits":{"max_parallelism":1,"rust_memory_mib":384,"spill_chunk_mib":32,"worker_memory_mib":768},"next_retry_unix_ms":null,"oldest_pending_unix_ms":null,"overflow_count":0,"pending":false,"phase_high_water_marks":{},"reconcile_interval_ms":250,"reconciled_epoch":99,"refreshing":false,"role":"leader","spill_bytes":0,"state":"running","task_alive":true,"worker_pid":null},"refresh_health":{"dirty_epoch":99,"effective_root":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository","freshness":"overdue","last_error":null,"last_successful_reconciliation_unix_ms":1792834994263,"next_retry_unix_ms":null,"oldest_pending_unix_ms":null,"pending":false,"readiness":"ready","reconciled_epoch":99,"state":"running","task_alive":true},"refresh_readiness":"ready","remediation":null,"repo_root":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository","storage_format":"managed_v2","storage_root":"/private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository/.codebaseGraph/storage","total_nodes":138,"writable":true} +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +retained failed storage qualification fixture: /private/var/folders/3y/n_wglyqn3bv8xy8s0b9p32d00000gn/T/cgraph-s5-collector-71574-0000000000000000/repository + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-failure.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-failure.log.gz new file mode 100644 index 00000000..ccd67c59 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-failure.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-source.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-source.json new file mode 100644 index 00000000..30fc2fa9 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/intermediate-source.json @@ -0,0 +1,214 @@ +{ + "base_sha": "073f0fb91e8a866d31e5f01a07a20898c3eb2370", + "platform": "macOS-26.6.2-arm64-arm-64bit", + "rustc": "rustc 1.96.0 (ac68faa20 2026-05-25)\nbinary: rustc\ncommit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96\ncommit-date: 2026-05-25\nhost: aarch64-apple-darwin\nrelease: 1.96.0\nLLVM version: 22.1.2", + "cargo": "cargo 1.96.0 (30a34c682 2026-05-25)", + "source_sha256": { + ".cargo/config.toml": "388ad5407d7e78cae8dacd1c3c556f8c97f43d998c4e09273dcf8fca00eb0f82", + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "Cargo.toml": "0616e830566bae5a09ea67ac01126d4ab2920b084f8d60041e1d91938dbfdf52", + "assets/architecture_queries.json": "f14a2f43e0f1d1748f39aa0f7a92791e832955cf25a2435357d25bfa1d33a41c", + "assets/graph_schema.json": "244fbce950f51ed5db6b52fa8aadb418fdd96d73bc475b4a1a0d1fb66e34d3bd", + "assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz": "3bd371decbfbf2054c146f7723f44843bc88c95114b1bbc787497ad1d3d6cb2c", + "assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz": "87805c35df6a990470b29b2376fef386d463cdb9e568acab3af325e2f04cc407", + "assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz": "36a30b551e7293c4045a1e892e0475a67e057de4a8c641da0ed3e88073c517e0", + "assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz": "a0595f1509a3333f29e7a6eadb5a8089b4ac2f241538b64757fb2c24e1858d66", + "assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz": "a9d60bfae1e6121eeb8ea0f610a9c8177e3f439956eeab39127822dfd2fa4016", + "assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz": "28b2df80cfa9a3631de8f7c2f8b6a524f0dda3e81ad01cf5e15c43990eb61437", + "assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz": "066f43565dfca6c1cbe79f40f9102aeb52c98b68b7fb74861a6be81d4318df4a", + "assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz": "7d39d0ba2fe3eb41364febc09fb45aca02b37bb64a06e3a3666bfb70f49792cd", + "assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz": "ef05f061c330115537be9051ca9d274e4502d92fdb05e628fd985c166b3dcced", + "assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz": "e374f49a261ba8818cfff1386e7a1b01694b268f77f5cd308668b62aeca1c503", + "assets/mcp_tool_specs.json": "b7fd145e4885b4009e6d8ccecb0300eb6286ca7d117a915934478c64c72ab060", + "assets/query_helpers.json": "c91ab54b35de0c26a14cffbb531e0a4374763f4ccb723e6d8d3bac8f2a91b689", + "build.rs": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "00340ebf27a5738c4dee1f0db3203f05ac80055333115ef6e9be3aeae42f129a", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "e4e96687ae2452bd14c222f9b270f017adca30980e014281606a2c37f998c3d1", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "6f5a802b7cb5da3da0bdc1452ec3092101a0cd46d147a54d773aa4e2184dc4c1", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "addc21ef57da7daeb08033529a76f246121fa8ac48ba3ea21ed0af90c77193dc", + "src/api/graph_read.rs": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "d5391ee4e4bc50dcd0fa77d9fc2049aa91ab28d6796f840ded9134bbaab8ca24", + "src/api/mod.rs": "2ef0485007ca6758f9d30fbe94a371425db70e1882a346818937c106938b22ae", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "4486d7f007e20df0b057b6cf505b05fee96af835d6ffa4cd15b90d8a399945a2", + "src/api/refresh.rs": "eb03a82af7ef1d989d1d05e3d8952000fa62004666446584df490c6953dedcdd", + "src/artifact_store.rs": "5faaf02516944f75b6b3392dd3759eb6b16c0eb4ac4fc764987d55f2215f2b78", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator.rs": "a11b7ceb26cc38e7e331ec23e07c752fd00197fea2abede8f9dac6dfbd8d5879", + "src/coordinator/tests.rs": "cf08afb329a78a30dacd79e298564521bf1a531f0aa93ee9ec0603d2f36292fb", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "2de8241f421b3d0e9a4e5896f632698713bd3c00c703fc01a4268013f1d8a9c5", + "src/db_writer/phase.rs": "a0afc7c8be36c10ae7dcc839ccb9abd821dd3a2966b93564434e0d6473902b97", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "fb00bc867d9aaeeb903c62bae36c6d08bf20726c61efd41b726c053e0f3ff1f2", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "46851842698e2dfc87f7410bccd1976bed5d7ebdaa47b40faa0016ddb2283357", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "899acb0f8bf2cdeb55a1f232694ae6d5d4ef4aee61db33235eebfcd1d5dc3470", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "432fdf59a49379c72f45dc10306f20a180b53968000ad51960ee358a6696ab96", + "src/materialization_worker.rs": "e90314596351dbdfc4dd7219cab3757a89f888fd5533bf1337e7e436cd5881f0", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "b7b7bf8188609029abf769b4c44591f715fdbca908c0a2255f0d0cc9b9ea0326", + "src/process_lifecycle/unix.rs": "bbe2c2cc599f9472047faaad821eacb813653082bb16f713118636d7717093a5", + "src/process_lifecycle/windows.rs": "5dc75bc172428ac7c282c25b7aea04032c32a9614f00f6917d1c358669271682", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "7d18d8b1c202c0443717d632c2c5847aad4993d7b223792de85287dee8d81198", + "src/scan.rs": "1be1606d97106236a6ae3635e92d910a135f9e8555455f7e10bfb976fd74c50f", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "359385201e8af617957ee854be2069092b1cdebfc96c5b738962c874ce445481", + "src/staging_writer/files.rs": "2c65ec06747cca9a551a9c0f0ec9a5fab2fa70adebbe3fff0289d1bde6e4bbaf", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "4e0e8a8baadeb8e7d1904945ab11bdffec40dc68319451d0b3898d67baa7cac1", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "18a34283f11dad63417b527c2fd7483b265d98634e74b39abbc01b5fab1483ab", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "a8bb246054fb39d559bd7d89e9c8ba1370c4fe805d97dc73cdc35fd9cbf49d84", + "src/storage/direct.rs": "faad998af4467a00fd0cd946ecab5dbb3628c7bcc3aa1f24ace166472f1149d2", + "src/storage/inventory.rs": "f7c4c9d83f79f68ea2ca3cf16bca08a6a841536b3769e5f6ef6f18740ac628b7", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "3cf865ded0431c8d1822c65fbb7a9274bf81a39ab20ba62a55a953e82e4dbe4e", + "src/storage/maintenance.rs": "d934271636dbff9c98f8186e5895adb9b26cd740238462e8262b3589b8d62741", + "src/storage/maintenance_admission.rs": "ba60a06b0a04d3dc5bed4c161a82e0063e265f59699004a26f8b1119d67eed11", + "src/storage/maintenance_automatic_direct.rs": "7516ebce83df47d3067f2538f54231c13fb435411599174a304aa04a22a1965e", + "src/storage/maintenance_backup.rs": "ef38f7d1c621f1f3b6d9deafdae1d2a72da63b74f8f5c024fddaaf2a9c983bd7", + "src/storage/maintenance_budget.rs": "2d15689d5811386c87505cf8b89a0c90b066f781020f18e9fe5a69aa4ba5d8aa", + "src/storage/maintenance_candidate.rs": "e86cd87ab2f1acabeb291afd8406105f3350d43ff7d1e6c06120558181699760", + "src/storage/maintenance_controls.rs": "abe5c9ac8267174fcd016d6e9ea58ea8001a1929cf49a50170f497aa519b3bd8", + "src/storage/maintenance_generation_engine.rs": "639a9857928a57504fb059c5650f4a24ee7cd4cb7e4b6b8e31ab1d4d6614692b", + "src/storage/maintenance_generation_gate.rs": "e109a6e00394341d22abd7a3beb7ec478ac776e667fd592694722d22974001bc", + "src/storage/maintenance_generations.rs": "1936c84203b0a659bba927fcd9facb709c9fd3f401852fe66dc6d602b7d6775b", + "src/storage/maintenance_journal.rs": "d9f903b20bd05614af5246a70364e4c17b53f250b24dca242d673f83f52bbd48", + "src/storage/maintenance_json.rs": "7cf5baff196bbbab8a77fb646cb338b1a95f89c6ef657acd0bf2139a5b7fd3ac", + "src/storage/maintenance_legacy_association.rs": "49df4ef23a396866e48b2e0006e418521b34510885511ba5b25142e45c2ca151", + "src/storage/maintenance_legacy_bootstrap.rs": "11f9536f8a7cf80994355caf0d8e8e4ea9755f39b082b62c5708b9557d217853", + "src/storage/maintenance_legacy_coverage.rs": "32e7eb2b5137bc4a5672da7e39db9e738d5c8376b542cce333e4227588adafd6", + "src/storage/maintenance_legacy_engine.rs": "806a916a2823dd395c9a2551276761ea0a46632e1f42d5ec7588ee9005c80aad", + "src/storage/maintenance_legacy_generations_engine.rs": "b00614a6da6417384648ef1a5e32f2a4e7b32c290644178886eed749e8ed2b3c", + "src/storage/maintenance_legacy_guard.rs": "842e07a1cd42daba36daa1b2379f581047fb0483b9749f1084757c4452618f75", + "src/storage/maintenance_legacy_source_recovery_tests.rs": "b7da7834cb331774a2368048a15dddef414f446bd6a449585bb8cd0dba2c12ad", + "src/storage/maintenance_legacy_staging.rs": "9251f63d4924186d764d31da369b980424230a5634ad6e1a96fbae493325e9b2", + "src/storage/maintenance_legacy_stream.rs": "ecbe24522e7615b879c36b2c7fc80b79f8c54779ff192907902a4f836dd2708c", + "src/storage/maintenance_native_projection.rs": "a54f389b354944c3a5ca62230acbf67a28ce460c8ea59aa9bdd74449143df90e", + "src/storage/maintenance_origin.rs": "655de59e5269fdb55b5ce26ae3a022b60303dfef7f6119a6a5a89a57a14a4fb1", + "src/storage/maintenance_ownership.rs": "13e3afecf427797c663a81354d6d68021fca90f2eb88ef368b4279bab12c2976", + "src/storage/maintenance_projection.rs": "a2dfb99976a2aa82e73e633537b3452d3f7a7d9f7682d731606c1cfc233f7923", + "src/storage/maintenance_references.rs": "78094003a14879441255272657b4044080a9592b7ca59f9794926934813e762b", + "src/storage/maintenance_retirement.rs": "f0d0db19b03321bfd1890b94eefbb809866c58345759df8264beb06e39a22161", + "src/storage/maintenance_scheduler.rs": "a65509524dd191c22471da3fee6067e4272e4bad33bee818b1ab0367fba5949e", + "src/storage/maintenance_validation.rs": "403e3edf264b762edd852e32ce6c5dd628bdd29f9a0728ac80cf17cdd8e509c2", + "src/storage/maintenance_workspace_gate.rs": "5dcef864f6077eccaff80b9019e2a06d9b64839fc91cfb21c6977d992d09a6fc", + "src/storage/managed.rs": "8090dfa8926c954b569f84eb9db507f09fee04f16c6e5659abe352b5b239937c", + "src/storage/mod.rs": "c8d46bd2fcf6ada768a91b2fa0480b9c35f49c77158e5e57dd4a847bcbc1121a", + "src/storage/run_workspace.rs": "2b3284a6ff9b49b2249d95d52dc1ae2d96de8267c591d342a266dd910ca2f273", + "src/storage/test_harness.rs": "dd391c4db26b4314345fd6425df56bdeed8538bb02f54b20aa0f054f39a68500", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "tests/agent_hooks_process.rs": "364d029cd0d4daf328ba46a1254e1d9a6f5df4e7fc9b0a86bcbf7740b6030478", + "tests/auto_repo_root.rs": "09d5d09635409c18b2d9b1da8a2272766b4fa5be63d93efd3aa4fdaf0f93fc76", + "tests/coordinator_process.rs": "a1ec773c1ee3aeb9875a65c0de66b4d7f757a6a70ebd20d1d8791a02b2c2f980", + "tests/generation_storage.rs": "778c9c91dcc1729cf45e59de49b46c4f0d3a3908c9107165d2d56b052452c5d6", + "tests/http_admission_process.rs": "3dd5533bfd85467027710b37d77be2ebfc84832d23942f3a5398c335b151dbb9", + "tests/http_daemon_process.rs": "fc651df00823b1d3f854d6627dc80c0d10227d78597c1adab8e417d8e0a55d46", + "tests/native_request_identity.rs": "f624bd73ba6b282f415a6251edca1c8d0e85a7d9bcacad1fe2d020e732c74eb9", + "tests/refresh_recovery_process.rs": "158e6795af9b9dd0b812f9ae7df579694c15238b4a0de5c36cca6a603fc63b5c", + "tests/storage_maintenance.rs": "16ccd5ba363ce2b4774163c70036a3a7971e9dcccff36d942ee70e84dadf06d1", + "tests/support/lifecycle_faults.rs": "0e8e052b39dd704aad218ffe87f1c497ce55b925faa849e6776c25fb730dbd9b", + "tests/support/storage_maintenance/collector.rs": "9548b8076e0783785269016366180fba18d543638439e0dc85818513d9fd88e8", + "tests/support/storage_maintenance/mod.rs": "7594eacd903f327c882f26ef1d5c49847e20afd3d7fc37f2334efc62702f0fd5", + "tests/support/storage_maintenance/process.rs": "212a9d91e62f9648ed9e911cbd2b0daa20bce9286bf0d76c4636575054ea4669", + "tests/support/storage_maintenance/workload.rs": "570e5c0d70bdfccda3e655f2285f97269dea4573e67d524841df2ad15fed9085", + "vendor/tree-sitter-wat/LICENSE": "28a1c2b92a241a705632869e5c3a5e83a53bb182eb273c851565ae381f12982c", + "vendor/tree-sitter-wat/UPSTREAM.md": "edb5f2e65b8131e5ac0f6081e4fa7b8e271034cfe1f8f6577f8f70a7a75ce225", + "vendor/tree-sitter-wat/src/node-types.json": "b8ee14131666f1553d8948fa661526b2aa5d4bc5f64aa5f28f662fb33ebf453a", + "vendor/tree-sitter-wat/src/parser.c": "ba3659413ddfa7acd66d1739ca96ace762e6d08f2c60a43d22670503fac87ec6", + "vendor/tree-sitter-wat/src/scanner.c": "e6197b66843756e44e67ff6e9f61790b7df44c930ed1ba5b5fc1169874fc6d60", + "vendor/tree-sitter-wat/src/tree_sitter/alloc.h": "b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea", + "vendor/tree-sitter-wat/src/tree_sitter/array.h": "5bdf6ed1a78e3409fd443e085ca967a64c188a5d082aaf7f819bccd53a471c94", + "vendor/tree-sitter-wat/src/tree_sitter/parser.h": "180b893c8734778fd32f372dfbc27bd6ad1cd2221f26150b31256ff6716320d2" + } +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/report.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/report.json new file mode 100644 index 00000000..4cb1bd57 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/report.json @@ -0,0 +1,170 @@ +{ + "work": { + "pull_request": "https://github.com/rabii-chaarani/codebaseGraph/pull/153", + "failed_run": 40074045438, + "baseline": "073f0fb91e8a866d31e5f01a07a20898c3eb2370", + "source_patch_sha256": "55cc9bc3fcffac942ee10827eeea436bad4758637700253dc08fb1ebe5de04aa", + "spec": "knowledge/architecture/graph-disk-cleanup-contract.md", + "spec_revisions": { + "DC-S1": "0.1", + "DC-S2": "0.2", + "DC-S3": "0.2", + "DC-S4": "0.2", + "DC-S5": "0.1" + }, + "approval": "Existing all-step approval and amendments recorded in the canonical wiki contract; current user request to fix CI run 40074045438. No scope, public contract or architecture-claim changes.", + "scryer_change": "chg-30es7n", + "linear": "Existing RAB-6 through RAB-10. Synchronization pending: the CodebaseGraph connection cannot resolve configured P-RAB-1; no unrelated workspace writes.", + "closure_owner": "repository maintainer/user" + }, + "repairs": [ + { + "platform": "Windows", + "failure": "Native-tests process exited 0xc00000fd in first materializing CLI case. FixedControlScratch carried a 64 KiB UTF-16 array inline through preparation aggregates.", + "change": "Keep the native path buffer in an aligned MemoryBox initialized directly in place using the caller’s existing MemoryReservation. Retain the complete charge through disposal; enforce allocation failure normally.", + "contracts": [ + "DC-S4-R3", + "DC-S4-R4", + "DC-S5-R6", + "DC-S5-R8" + ], + "claim": "resp-hakfnc", + "anchors": [ + "src/storage/locks.rs::FixedControlScratch", + "src/storage/maintenance_budget.rs::MemoryBox::new_uninit_reserved" + ], + "checks": [ + "dc_control_scratch_uses_small_worker_frame_and_retains_reserved_path_storage", + "dc_driver_real_requested_allocations_fit_tight_ceiling_and_aligned_factory", + "git_diff_plan_scopes_to_changed_paths", + "windows-path-probe.log.gz" + ] + }, + { + "platform": "Darwin ARM64", + "failure": "Six CI tests expected genuine workspace registration beneath an uncanonicalized temporary path containing the macOS /var symlink. Strict real_path validation correctly declined optional origin registration.", + "change": "Canonicalize only the existing temporary parent in four test fixture constructors; preserve actual fresh directory creation and every production ownership check.", + "contracts": [ + "DC-S2-R3", + "DC-S2-R6", + "DC-S5-R5", + "DC-S5-R6" + ], + "claim": "resp-232", + "checks": [ + "baseline-macos.log", + "canonical-temp-probe.log.gz", + "initial-focused.junit.xml", + "targeted.junit.xml" + ] + }, + { + "platform": "Linux fixture, reproduced on Darwin", + "failure": "A 10-second hook wait was shorter than the 60-second cleanup cooldown. Forced initial producer activity also showed the 250 ms full-reconciliation loop could repeatedly defer actual epoch preparation.", + "change": "Force and observe one genuine deferred startup attempt; allow the existing 60-second retry plus preparation time; keep periodic full rescans outside the fixture window while ordinary Poll source-change detection performs the real write; include owner health on timeout.", + "contracts": [ + "DC-S2-R5", + "DC-S3-R1", + "DC-S3-R7", + "DC-S3-A5", + "DC-S5-R5" + ], + "claim": "resp-109", + "checks": [ + "dc_s5_real_mcp_and_writer_progress_during_preparation_and_changed_epoch_blocks_stale_effect", + "dc_s5_real_owner_cancel_joins_paused_preparation_before_fixture_reuse" + ] + } + ], + "executed": { + "native_target": "aarch64-apple-darwin", + "profile": "default dev/test, debug assertions enabled", + "targeted_tests": { + "passed": 334, + "failed": 0, + "ignored_in_selection": 0, + "unselected": 757, + "junit": "targeted.junit.xml", + "command": "cargo nextest run --offline -p codebase-graph --lib --test storage_foundation --profile ci-pr153 --config-file /private/tmp/pr153-native-ci-final-nextest.toml -E ", + "selection": "fixed_control_tests; maintenance_budget::tests; six original macOS failures and Managed janitor tests; git_diff_plan_scopes_to_changed_paths; both real preparation/cancellation tests; actual driver allocation boundary; Scryer radius install, execution::run, semantic_enrichment, staging_writer::tests and storage_foundation" + }, + "clippy": { + "command": "cargo clippy --offline --workspace --all-targets --all-features -- -D warnings", + "result": "pass", + "log": "clippy.log" + }, + "format": { + "command": "cargo fmt --all -- --check", + "result": "pass" + }, + "diff_check": { + "command": "git diff --check", + "result": "pass" + }, + "windows_layout_probe": { + "result": "10/10 standalone copied-module host tests pass with the exact Windows 64 KiB path layout; the same probe cross-compiles to x86_64-pc-windows-msvc LLVM IR with -D warnings", + "native_command": "rustc --edition=2021 --test -D warnings windows-path-probe.rs -o windows-path-probe; ./windows-path-probe", + "cross_command": "rustc --edition=2021 --test --target x86_64-pc-windows-msvc --emit=llvm-ir -C opt-level=3 -D warnings windows-path-probe.rs -o windows-path-probe.ll", + "limitations": "This validates the constructor, layout, alignment and accounting; it is not native Windows OS or whole-program execution." + }, + "input_stability": { + "source_and_native_files": 206, + "executed_binaries": 3, + "source_hashes_unchanged": true, + "binary_hashes_unchanged": true + }, + "scryer": { + "junit_ingested": 334, + "claims_with_verdicts_recorded": 32, + "claims_changed": 0, + "drift_scopes_after_reconciliation": 0, + "remaining_preexisting_attachment_radius": [ + "src/adapters/cli/tests/install.rs (2 claims)", + "src/execution/run.rs (2 claims)", + "src/semantic_enrichment/mod.rs (2 claims)", + "src/storage/test_harness.rs (1 claim)" + ], + "note": "Every existing enabled test in the requested radius files was selected and passed. Seven pre-existing missing attachment verdicts remain; there are no failing or stale verdicts. No replacement test names or claims were invented. Empty proportional ledger closed." + }, + "wiki": { + "profile": "recommended", + "accepted": true, + "validation_diagnostics": 0, + "link_diagnostics": 0, + "site_routes": 322, + "site_assets": 2, + "active_memory": "memory/procedural/canonical-temp-parents-for-owned-workspace-fixtures-2026-10-24" + } + }, + "review": { + "method": "Primary-agent review of the actual diff, allocation layout/RAII, confinement and capability preservation, fixture-only canonicalization, and real retry/read/write/cancellation assertions. The repository exempts bounded CI fixes from its large-implementation multi-agent workflow.", + "outcome": "No remaining confirmed defect in the reviewed change." + }, + "history": { + "initial_focused": "31/31 passing; preserves the baseline macOS failure and same-binary canonical-temp comparison.", + "intermediate": "333/334 passed; forced producer activity exposed repeated full-rescan contention. The failed snapshot, JUnit and source/binary identities are retained separately.", + "final": "334/334 selected checks passed on the final correction; all 206 source/native input and three binary hashes remained unchanged." + }, + "limits": [ + "Native Linux/Windows and full hosted CI qualification of the new commit remain pending. Local evidence does not close aggregate, milestone, rollout or release gates.", + "The repository graph registration indexes the older primary checkout, so PR branch discovery used its verified GitHub changed-file list after graph searches and the existing same-repository Scryer model.", + "A new-spec copy, live graph migration, installed helper upgrade and architecture topology changes were outside this CI repair." + ], + "files_sha256": { + "baseline-macos.log": "d2c41a7bcfb3e96e50c11f37a437a106811169127b656009695a257e4a455954", + "binaries.json": "66fe588b69149198077197cd9dcef65a493d69ccb342aef5bb2936295abb292e", + "canonical-temp-probe.log.gz": "1245e61208c7d86652f505233bd5350637cf33bd826a9450019d01f03e9d271c", + "clippy.log": "d1c3dd556a510606b7cf300a4743ce8ec28827d60433d68dcfbf695c8a53ffbc", + "initial-focused.junit.xml": "637fc9f29282bc598c24793ff402d88722b480debac405e49eb6b0512662e14a", + "intermediate-binaries.json": "428796a23638bb6b0bd609df13b6f25ffe15a1b9ea723610430405fe94a58180", + "intermediate-failure.junit.xml": "e28fadcb1e5b07550b8421baee606d10013d1dcdd96367b4d92e3147d3ec36c5", + "intermediate-failure.log.gz": "ca762dd677bf88c7cbacd669d3d165469264d124abc0c4c81f299b5594ca6ce0", + "intermediate-source.json": "841b9ba299bfd002e51d81fe3634c11535779ba587ba7d758832dbb7bbb2e23c", + "source-manifest.json": "03eaefca92ce313a4da2e28bc512b244b7b218e63001eeb6f769f1dfd49c50e7", + "targeted.junit.xml": "8857ee16bd2bc20e206310ae63a6eae935f9b828139c0081b04ecf3d70a63a7d", + "targeted.log.gz": "0fb62b112631339ad642cefbd22677bd7ea58be7afd3562f3becc6db290aa7a2", + "windows-path-probe.json": "068eae61cf83cd0ac0bef7ebc70da92bf005627f6b4c2a44a00b843e0a1ed726", + "windows-path-probe.log.gz": "1374d492df6cb4dce76816faf67941273d7592c042ed82c488249afa3b00f1e4", + "windows-path-probe.rs.gz": "5781379df6a230e4c6559eb4005280a7d1cd7030c26ccbc6b4f85596c80eca90" + } +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/source-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/source-manifest.json new file mode 100644 index 00000000..08477152 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/source-manifest.json @@ -0,0 +1,214 @@ +{ + "base_sha": "073f0fb91e8a866d31e5f01a07a20898c3eb2370", + "platform": "macOS-26.6.2-arm64-arm-64bit", + "rustc": "rustc 1.96.0 (ac68faa20 2026-05-25)\nbinary: rustc\ncommit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96\ncommit-date: 2026-05-25\nhost: aarch64-apple-darwin\nrelease: 1.96.0\nLLVM version: 22.1.2", + "cargo": "cargo 1.96.0 (30a34c682 2026-05-25)", + "source_sha256": { + ".cargo/config.toml": "388ad5407d7e78cae8dacd1c3c556f8c97f43d998c4e09273dcf8fca00eb0f82", + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "Cargo.toml": "0616e830566bae5a09ea67ac01126d4ab2920b084f8d60041e1d91938dbfdf52", + "assets/architecture_queries.json": "f14a2f43e0f1d1748f39aa0f7a92791e832955cf25a2435357d25bfa1d33a41c", + "assets/graph_schema.json": "244fbce950f51ed5db6b52fa8aadb418fdd96d73bc475b4a1a0d1fb66e34d3bd", + "assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz": "3bd371decbfbf2054c146f7723f44843bc88c95114b1bbc787497ad1d3d6cb2c", + "assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz": "87805c35df6a990470b29b2376fef386d463cdb9e568acab3af325e2f04cc407", + "assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz": "36a30b551e7293c4045a1e892e0475a67e057de4a8c641da0ed3e88073c517e0", + "assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz": "a0595f1509a3333f29e7a6eadb5a8089b4ac2f241538b64757fb2c24e1858d66", + "assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz": "a9d60bfae1e6121eeb8ea0f610a9c8177e3f439956eeab39127822dfd2fa4016", + "assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz": "28b2df80cfa9a3631de8f7c2f8b6a524f0dda3e81ad01cf5e15c43990eb61437", + "assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz": "066f43565dfca6c1cbe79f40f9102aeb52c98b68b7fb74861a6be81d4318df4a", + "assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz": "7d39d0ba2fe3eb41364febc09fb45aca02b37bb64a06e3a3666bfb70f49792cd", + "assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz": "ef05f061c330115537be9051ca9d274e4502d92fdb05e628fd985c166b3dcced", + "assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz": "e374f49a261ba8818cfff1386e7a1b01694b268f77f5cd308668b62aeca1c503", + "assets/mcp_tool_specs.json": "b7fd145e4885b4009e6d8ccecb0300eb6286ca7d117a915934478c64c72ab060", + "assets/query_helpers.json": "c91ab54b35de0c26a14cffbb531e0a4374763f4ccb723e6d8d3bac8f2a91b689", + "build.rs": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "00340ebf27a5738c4dee1f0db3203f05ac80055333115ef6e9be3aeae42f129a", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "e4e96687ae2452bd14c222f9b270f017adca30980e014281606a2c37f998c3d1", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "6f5a802b7cb5da3da0bdc1452ec3092101a0cd46d147a54d773aa4e2184dc4c1", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "addc21ef57da7daeb08033529a76f246121fa8ac48ba3ea21ed0af90c77193dc", + "src/api/graph_read.rs": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "d5391ee4e4bc50dcd0fa77d9fc2049aa91ab28d6796f840ded9134bbaab8ca24", + "src/api/mod.rs": "2ef0485007ca6758f9d30fbe94a371425db70e1882a346818937c106938b22ae", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "4486d7f007e20df0b057b6cf505b05fee96af835d6ffa4cd15b90d8a399945a2", + "src/api/refresh.rs": "eb03a82af7ef1d989d1d05e3d8952000fa62004666446584df490c6953dedcdd", + "src/artifact_store.rs": "5faaf02516944f75b6b3392dd3759eb6b16c0eb4ac4fc764987d55f2215f2b78", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator.rs": "a11b7ceb26cc38e7e331ec23e07c752fd00197fea2abede8f9dac6dfbd8d5879", + "src/coordinator/tests.rs": "cf08afb329a78a30dacd79e298564521bf1a531f0aa93ee9ec0603d2f36292fb", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "2de8241f421b3d0e9a4e5896f632698713bd3c00c703fc01a4268013f1d8a9c5", + "src/db_writer/phase.rs": "a0afc7c8be36c10ae7dcc839ccb9abd821dd3a2966b93564434e0d6473902b97", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "fb00bc867d9aaeeb903c62bae36c6d08bf20726c61efd41b726c053e0f3ff1f2", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "46851842698e2dfc87f7410bccd1976bed5d7ebdaa47b40faa0016ddb2283357", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "899acb0f8bf2cdeb55a1f232694ae6d5d4ef4aee61db33235eebfcd1d5dc3470", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "432fdf59a49379c72f45dc10306f20a180b53968000ad51960ee358a6696ab96", + "src/materialization_worker.rs": "e90314596351dbdfc4dd7219cab3757a89f888fd5533bf1337e7e436cd5881f0", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "b7b7bf8188609029abf769b4c44591f715fdbca908c0a2255f0d0cc9b9ea0326", + "src/process_lifecycle/unix.rs": "bbe2c2cc599f9472047faaad821eacb813653082bb16f713118636d7717093a5", + "src/process_lifecycle/windows.rs": "5dc75bc172428ac7c282c25b7aea04032c32a9614f00f6917d1c358669271682", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "7d18d8b1c202c0443717d632c2c5847aad4993d7b223792de85287dee8d81198", + "src/scan.rs": "1be1606d97106236a6ae3635e92d910a135f9e8555455f7e10bfb976fd74c50f", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "359385201e8af617957ee854be2069092b1cdebfc96c5b738962c874ce445481", + "src/staging_writer/files.rs": "2c65ec06747cca9a551a9c0f0ec9a5fab2fa70adebbe3fff0289d1bde6e4bbaf", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "4e0e8a8baadeb8e7d1904945ab11bdffec40dc68319451d0b3898d67baa7cac1", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "18a34283f11dad63417b527c2fd7483b265d98634e74b39abbc01b5fab1483ab", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "a8bb246054fb39d559bd7d89e9c8ba1370c4fe805d97dc73cdc35fd9cbf49d84", + "src/storage/direct.rs": "faad998af4467a00fd0cd946ecab5dbb3628c7bcc3aa1f24ace166472f1149d2", + "src/storage/inventory.rs": "f7c4c9d83f79f68ea2ca3cf16bca08a6a841536b3769e5f6ef6f18740ac628b7", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "3cf865ded0431c8d1822c65fbb7a9274bf81a39ab20ba62a55a953e82e4dbe4e", + "src/storage/maintenance.rs": "d934271636dbff9c98f8186e5895adb9b26cd740238462e8262b3589b8d62741", + "src/storage/maintenance_admission.rs": "ba60a06b0a04d3dc5bed4c161a82e0063e265f59699004a26f8b1119d67eed11", + "src/storage/maintenance_automatic_direct.rs": "7516ebce83df47d3067f2538f54231c13fb435411599174a304aa04a22a1965e", + "src/storage/maintenance_backup.rs": "ef38f7d1c621f1f3b6d9deafdae1d2a72da63b74f8f5c024fddaaf2a9c983bd7", + "src/storage/maintenance_budget.rs": "2d15689d5811386c87505cf8b89a0c90b066f781020f18e9fe5a69aa4ba5d8aa", + "src/storage/maintenance_candidate.rs": "e86cd87ab2f1acabeb291afd8406105f3350d43ff7d1e6c06120558181699760", + "src/storage/maintenance_controls.rs": "abe5c9ac8267174fcd016d6e9ea58ea8001a1929cf49a50170f497aa519b3bd8", + "src/storage/maintenance_generation_engine.rs": "639a9857928a57504fb059c5650f4a24ee7cd4cb7e4b6b8e31ab1d4d6614692b", + "src/storage/maintenance_generation_gate.rs": "e109a6e00394341d22abd7a3beb7ec478ac776e667fd592694722d22974001bc", + "src/storage/maintenance_generations.rs": "1936c84203b0a659bba927fcd9facb709c9fd3f401852fe66dc6d602b7d6775b", + "src/storage/maintenance_journal.rs": "d9f903b20bd05614af5246a70364e4c17b53f250b24dca242d673f83f52bbd48", + "src/storage/maintenance_json.rs": "7cf5baff196bbbab8a77fb646cb338b1a95f89c6ef657acd0bf2139a5b7fd3ac", + "src/storage/maintenance_legacy_association.rs": "49df4ef23a396866e48b2e0006e418521b34510885511ba5b25142e45c2ca151", + "src/storage/maintenance_legacy_bootstrap.rs": "11f9536f8a7cf80994355caf0d8e8e4ea9755f39b082b62c5708b9557d217853", + "src/storage/maintenance_legacy_coverage.rs": "32e7eb2b5137bc4a5672da7e39db9e738d5c8376b542cce333e4227588adafd6", + "src/storage/maintenance_legacy_engine.rs": "806a916a2823dd395c9a2551276761ea0a46632e1f42d5ec7588ee9005c80aad", + "src/storage/maintenance_legacy_generations_engine.rs": "b00614a6da6417384648ef1a5e32f2a4e7b32c290644178886eed749e8ed2b3c", + "src/storage/maintenance_legacy_guard.rs": "842e07a1cd42daba36daa1b2379f581047fb0483b9749f1084757c4452618f75", + "src/storage/maintenance_legacy_source_recovery_tests.rs": "b7da7834cb331774a2368048a15dddef414f446bd6a449585bb8cd0dba2c12ad", + "src/storage/maintenance_legacy_staging.rs": "9251f63d4924186d764d31da369b980424230a5634ad6e1a96fbae493325e9b2", + "src/storage/maintenance_legacy_stream.rs": "ecbe24522e7615b879c36b2c7fc80b79f8c54779ff192907902a4f836dd2708c", + "src/storage/maintenance_native_projection.rs": "a54f389b354944c3a5ca62230acbf67a28ce460c8ea59aa9bdd74449143df90e", + "src/storage/maintenance_origin.rs": "655de59e5269fdb55b5ce26ae3a022b60303dfef7f6119a6a5a89a57a14a4fb1", + "src/storage/maintenance_ownership.rs": "13e3afecf427797c663a81354d6d68021fca90f2eb88ef368b4279bab12c2976", + "src/storage/maintenance_projection.rs": "a2dfb99976a2aa82e73e633537b3452d3f7a7d9f7682d731606c1cfc233f7923", + "src/storage/maintenance_references.rs": "78094003a14879441255272657b4044080a9592b7ca59f9794926934813e762b", + "src/storage/maintenance_retirement.rs": "f0d0db19b03321bfd1890b94eefbb809866c58345759df8264beb06e39a22161", + "src/storage/maintenance_scheduler.rs": "a65509524dd191c22471da3fee6067e4272e4bad33bee818b1ab0367fba5949e", + "src/storage/maintenance_validation.rs": "403e3edf264b762edd852e32ce6c5dd628bdd29f9a0728ac80cf17cdd8e509c2", + "src/storage/maintenance_workspace_gate.rs": "5dcef864f6077eccaff80b9019e2a06d9b64839fc91cfb21c6977d992d09a6fc", + "src/storage/managed.rs": "8090dfa8926c954b569f84eb9db507f09fee04f16c6e5659abe352b5b239937c", + "src/storage/mod.rs": "c8d46bd2fcf6ada768a91b2fa0480b9c35f49c77158e5e57dd4a847bcbc1121a", + "src/storage/run_workspace.rs": "2b3284a6ff9b49b2249d95d52dc1ae2d96de8267c591d342a266dd910ca2f273", + "src/storage/test_harness.rs": "dd391c4db26b4314345fd6425df56bdeed8538bb02f54b20aa0f054f39a68500", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "tests/agent_hooks_process.rs": "364d029cd0d4daf328ba46a1254e1d9a6f5df4e7fc9b0a86bcbf7740b6030478", + "tests/auto_repo_root.rs": "09d5d09635409c18b2d9b1da8a2272766b4fa5be63d93efd3aa4fdaf0f93fc76", + "tests/coordinator_process.rs": "a1ec773c1ee3aeb9875a65c0de66b4d7f757a6a70ebd20d1d8791a02b2c2f980", + "tests/generation_storage.rs": "778c9c91dcc1729cf45e59de49b46c4f0d3a3908c9107165d2d56b052452c5d6", + "tests/http_admission_process.rs": "3dd5533bfd85467027710b37d77be2ebfc84832d23942f3a5398c335b151dbb9", + "tests/http_daemon_process.rs": "fc651df00823b1d3f854d6627dc80c0d10227d78597c1adab8e417d8e0a55d46", + "tests/native_request_identity.rs": "f624bd73ba6b282f415a6251edca1c8d0e85a7d9bcacad1fe2d020e732c74eb9", + "tests/refresh_recovery_process.rs": "158e6795af9b9dd0b812f9ae7df579694c15238b4a0de5c36cca6a603fc63b5c", + "tests/storage_maintenance.rs": "16ccd5ba363ce2b4774163c70036a3a7971e9dcccff36d942ee70e84dadf06d1", + "tests/support/lifecycle_faults.rs": "0e8e052b39dd704aad218ffe87f1c497ce55b925faa849e6776c25fb730dbd9b", + "tests/support/storage_maintenance/collector.rs": "89cec41b2a8b2890795652ebe3f4b8c8b0b3bd0cd6439014aa3b86473d6fca4d", + "tests/support/storage_maintenance/mod.rs": "7594eacd903f327c882f26ef1d5c49847e20afd3d7fc37f2334efc62702f0fd5", + "tests/support/storage_maintenance/process.rs": "212a9d91e62f9648ed9e911cbd2b0daa20bce9286bf0d76c4636575054ea4669", + "tests/support/storage_maintenance/workload.rs": "570e5c0d70bdfccda3e655f2285f97269dea4573e67d524841df2ad15fed9085", + "vendor/tree-sitter-wat/LICENSE": "28a1c2b92a241a705632869e5c3a5e83a53bb182eb273c851565ae381f12982c", + "vendor/tree-sitter-wat/UPSTREAM.md": "edb5f2e65b8131e5ac0f6081e4fa7b8e271034cfe1f8f6577f8f70a7a75ce225", + "vendor/tree-sitter-wat/src/node-types.json": "b8ee14131666f1553d8948fa661526b2aa5d4bc5f64aa5f28f662fb33ebf453a", + "vendor/tree-sitter-wat/src/parser.c": "ba3659413ddfa7acd66d1739ca96ace762e6d08f2c60a43d22670503fac87ec6", + "vendor/tree-sitter-wat/src/scanner.c": "e6197b66843756e44e67ff6e9f61790b7df44c930ed1ba5b5fc1169874fc6d60", + "vendor/tree-sitter-wat/src/tree_sitter/alloc.h": "b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea", + "vendor/tree-sitter-wat/src/tree_sitter/array.h": "5bdf6ed1a78e3409fd443e085ca967a64c188a5d082aaf7f819bccd53a471c94", + "vendor/tree-sitter-wat/src/tree_sitter/parser.h": "180b893c8734778fd32f372dfbc27bd6ad1cd2221f26150b31256ff6716320d2" + } +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/targeted.junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/targeted.junit.xml new file mode 100644 index 00000000..bc50d0a9 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/targeted.junit.xml @@ -0,0 +1,675 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/targeted.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/targeted.log.gz new file mode 100644 index 00000000..0fe45a2c Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/targeted.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.json new file mode 100644 index 00000000..ba7c03ed --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.json @@ -0,0 +1,6 @@ +{ + "method": "Actual memory module and FixedControlScratch constructor/test copied byte-for-byte except module-doc placement and native path alias fixed to the exact Windows [u16;32768] layout. Standalone build avoids unrelated native C++ dependencies; host execution does not qualify Windows OS operations.", + "locks_sha256": "3cf865ded0431c8d1822c65fbb7a9274bf81a39ab20ba62a55a953e82e4dbe4e", + "budget_sha256": "2d15689d5811386c87505cf8b89a0c90b066f781020f18e9fe5a69aa4ba5d8aa", + "probe_sha256": "8fe3b540667ee2b6389ae6da82d66982134abe4609ff335354d2e6246ec3799a" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.log.gz new file mode 100644 index 00000000..9e1bc8d2 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.rs.gz b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.rs.gz new file mode 100644 index 00000000..174ae7ed Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40074045438/windows-path-probe.rs.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/asset-recompression.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/asset-recompression.json new file mode 100644 index 00000000..3c0bcebe --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/asset-recompression.json @@ -0,0 +1,26 @@ +[ + { + "asset": "assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz", + "original": "/private/tmp/pr153-package-tuning/fts.original.xz", + "candidate": "/private/tmp/pr153-package-tuning/7zip-fts-fb264.xz", + "before_bytes": 3496500, + "after_bytes": 3495384, + "saved_bytes": 1116, + "decoded_sha256": "7f82059c8149bb7851420a9bfd0c46859e92efaeda0bcd5c65418d8f174edac4", + "before_sha256": "ef05f061c330115537be9051ca9d274e4502d92fdb05e628fd985c166b3dcced", + "after_sha256": "78e2febd0f3f29df5379a014cc755d5540f04282a6f5bd2128738266a7c25d01", + "actual_runtime_decoder_identical": true + }, + { + "asset": "assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz", + "original": "/private/tmp/pr153-package-tuning/json.original.xz", + "candidate": "/private/tmp/pr153-package-tuning/7zip-json-fb224.xz", + "before_bytes": 3373236, + "after_bytes": 3372056, + "saved_bytes": 1180, + "decoded_sha256": "bdecb5201dd6abccc06ddc1f6cb4fcf0be3adb7aa3ddaac2f383ed129fca2b1f", + "before_sha256": "e374f49a261ba8818cfff1386e7a1b01694b268f77f5cd308668b62aeca1c503", + "after_sha256": "6eb7946117aaffeaf6042a10a171bf77af9ee57d9d7983c4c3693e2dc311df87", + "actual_runtime_decoder_identical": true + } +] diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/clippy.log b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/clippy.log new file mode 100644 index 00000000..617ab21d --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/clippy.log @@ -0,0 +1,3 @@ + Checking codebase-graph v3.0.4 (/Users/rabii/.codex/worktrees/pr153-native-ci-fixes/codebaseGraph) + Checking k-wiki v3.0.4 (/Users/rabii/.codex/worktrees/pr153-native-ci-fixes/codebaseGraph/crates/k-wiki) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 16.86s diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused-176.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused-176.log.gz new file mode 100644 index 00000000..774a2126 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused-176.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused-4.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused-4.log.gz new file mode 100644 index 00000000..6599a3e6 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused-4.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused.junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused.junit.xml new file mode 100644 index 00000000..c809141a --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/focused.junit.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/generation.junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/generation.junit.xml new file mode 100644 index 00000000..1e76887b --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/generation.junit.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/generation.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/generation.log.gz new file mode 100644 index 00000000..546a0377 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/generation.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/report.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/report.json new file mode 100644 index 00000000..2b0afa36 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/report.json @@ -0,0 +1,54 @@ +{ + "baseline": "d2c6f7b23432df4c0247e45719538dd08169ea6d", + "failed_run": "https://github.com/rabii-chaarani/codebaseGraph/actions/runs/40083924114", + "failed_jobs": { + "windows": 120842881282, + "macos_arm64": 120842881378 + }, + "approved_plan": "User instruction: PLEASE IMPLEMENT THIS PLAN: Finish CI repairs for PR #153.", + "changes": [ + "Canonical generation-storage fixture paths before configuration writes.", + "Metadata-only rooted identity checks retain private creator sharing/security. Snapshot digests cover the accepted copied bytes without reopening the private temporary file.", + "Artifact publication releases child payload/receipt handles before directory rename. A consumed creator continuation validates receipt bytes, identities and nonce under the existing publication lease.", + "Windows replacement fixtures verify native rename exclusion and stale evidence after legitimate release.", + "Path envelopes use checked actual selector/projected string lengths and retain shared-account reservations for clones/joins.", + "Configuration projections use independently reopened Windows handles, preserving caller cursors. Preparation identity checks do not mint a mutation timer. Logical unit tests use explicit fixture grants, and real enrollment continuations accept exhausted grants only while an existing enrollment is pending.", + "Two losslessly recompressed Windows extension streams recover package headroom; decoded binaries, 16 MiB dictionaries and CRC64 integrity remain unchanged." + ], + "local_verification": { + "platform": "macOS-26.6.2-arm64-arm-64bit", + "focused_cases": 180, + "focused_result": "176/176 in focused-176.log.gz plus 4/4 in focused.junit.xml; zero ignored in either selection. The second run overwrote the first JUnit path, so only the preserved full 176-case log establishes that first run.", + "generation_cases": 5, + "generation_result": "5/5 passing, zero skipped, including atomic publication and ten-update convergence; 842.330 seconds.", + "clippy": "cargo clippy --offline --workspace --all-targets --all-features -- -D warnings: pass", + "format": "cargo fmt --all -- --check: pass", + "diff_check": "git diff --check: pass", + "package": { + "command": "cargo package --offline --locked --allow-dirty --no-verify", + "bytes": 10484560, + "limit": 10485760, + "sha256": "f6241b962b28b78c22c6f72d8aef55f74b284e421b7746bf7e851b0f7107796f" + }, + "runtime_decoder": "Pinned lzma_rs::xz_decompress verified identical 14,779,904-byte FTS and 14,178,304-byte JSON payloads. Encoder binary matched the recorded official 7-Zip 26.05 archive.", + "tested_input_note": "The tested-input manifest was captured before the two Windows resource recompressions. Their decoded payload equality is independently recorded; the changed streams are not consumed by the Darwin binaries." + }, + "unchanged_limits": { + "maintenance_bytes": 8388608, + "worker_stack_bytes": 262144, + "mutation_entries": 100, + "mutation_milliseconds": 100 + }, + "public_api_changes": false, + "qualification": "Native CI for the follow-up commit is pending. These local executions do not establish Windows native execution or aggregate closure.", + "files_sha256": { + "generation.junit.xml": "213a4b6be84fb6c43738d465220787a0cdbe0926c98d7bd2d4e0548d355b0486", + "asset-recompression.json": "59e6401fbde79ca939cce57846b60d5ebaf41927c46bdfa32169852b509f51c9", + "generation.log.gz": "5ed5638860c91a8eef55cd46125b04c5b56b9adc208fe225e75f25474d6fab1c", + "clippy.log": "82febda19b6f0366f63180bb550c06978b0d1c267feb66009fa0ebcc0d05583f", + "focused-176.log.gz": "e64039db7d619c574035f3605f6fd5f0f071efde63c3baa69fb1ecb7f4164844", + "tested-inputs.json": "8c2a3a24e54888ba1b5cb016f48e298c1ff6364c774792747ecced15bb654194", + "focused.junit.xml": "03817b8f5111f4b5f3ecd382eb088501d382b5364fbca653c4b6e434773d4e08", + "focused-4.log.gz": "88387e865f5e70d1fdb67cec21499181f4476ef4610ed56d51640417dc864a7d" + } +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/tested-inputs.json b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/tested-inputs.json new file mode 100644 index 00000000..61f2a9e0 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/ci-run-40083924114/tested-inputs.json @@ -0,0 +1,218 @@ +{ + "base": "d2c6f7b23432df4c0247e45719538dd08169ea6d", + "rustc": "rustc 1.96.0 (ac68faa20 2026-05-25)\nbinary: rustc\ncommit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96\ncommit-date: 2026-05-25\nhost: aarch64-apple-darwin\nrelease: 1.96.0\nLLVM version: 22.1.2\n", + "files": { + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "Cargo.toml": "0616e830566bae5a09ea67ac01126d4ab2920b084f8d60041e1d91938dbfdf52", + "assets/architecture_queries.json": "f14a2f43e0f1d1748f39aa0f7a92791e832955cf25a2435357d25bfa1d33a41c", + "assets/graph_schema.json": "244fbce950f51ed5db6b52fa8aadb418fdd96d73bc475b4a1a0d1fb66e34d3bd", + "assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz": "3bd371decbfbf2054c146f7723f44843bc88c95114b1bbc787497ad1d3d6cb2c", + "assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz": "87805c35df6a990470b29b2376fef386d463cdb9e568acab3af325e2f04cc407", + "assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz": "36a30b551e7293c4045a1e892e0475a67e057de4a8c641da0ed3e88073c517e0", + "assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz": "a0595f1509a3333f29e7a6eadb5a8089b4ac2f241538b64757fb2c24e1858d66", + "assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz": "a9d60bfae1e6121eeb8ea0f610a9c8177e3f439956eeab39127822dfd2fa4016", + "assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz": "28b2df80cfa9a3631de8f7c2f8b6a524f0dda3e81ad01cf5e15c43990eb61437", + "assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz": "066f43565dfca6c1cbe79f40f9102aeb52c98b68b7fb74861a6be81d4318df4a", + "assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz": "7d39d0ba2fe3eb41364febc09fb45aca02b37bb64a06e3a3666bfb70f49792cd", + "assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz": "ef05f061c330115537be9051ca9d274e4502d92fdb05e628fd985c166b3dcced", + "assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz": "e374f49a261ba8818cfff1386e7a1b01694b268f77f5cd308668b62aeca1c503", + "assets/mcp_tool_specs.json": "b7fd145e4885b4009e6d8ccecb0300eb6286ca7d117a915934478c64c72ab060", + "assets/query_helpers.json": "c91ab54b35de0c26a14cffbb531e0a4374763f4ccb723e6d8d3bac8f2a91b689", + "build.rs": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "00340ebf27a5738c4dee1f0db3203f05ac80055333115ef6e9be3aeae42f129a", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "e4e96687ae2452bd14c222f9b270f017adca30980e014281606a2c37f998c3d1", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "6f5a802b7cb5da3da0bdc1452ec3092101a0cd46d147a54d773aa4e2184dc4c1", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "addc21ef57da7daeb08033529a76f246121fa8ac48ba3ea21ed0af90c77193dc", + "src/api/graph_read.rs": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "d5391ee4e4bc50dcd0fa77d9fc2049aa91ab28d6796f840ded9134bbaab8ca24", + "src/api/mod.rs": "2ef0485007ca6758f9d30fbe94a371425db70e1882a346818937c106938b22ae", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "4486d7f007e20df0b057b6cf505b05fee96af835d6ffa4cd15b90d8a399945a2", + "src/api/refresh.rs": "eb03a82af7ef1d989d1d05e3d8952000fa62004666446584df490c6953dedcdd", + "src/artifact_store.rs": "01a279b5c6dfc3d59d00ee9f483b11bd4438a8c5c9a7323fc87628de7d497247", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator.rs": "a11b7ceb26cc38e7e331ec23e07c752fd00197fea2abede8f9dac6dfbd8d5879", + "src/coordinator/tests.rs": "cf08afb329a78a30dacd79e298564521bf1a531f0aa93ee9ec0603d2f36292fb", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "2de8241f421b3d0e9a4e5896f632698713bd3c00c703fc01a4268013f1d8a9c5", + "src/db_writer/phase.rs": "a0afc7c8be36c10ae7dcc839ccb9abd821dd3a2966b93564434e0d6473902b97", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "fb00bc867d9aaeeb903c62bae36c6d08bf20726c61efd41b726c053e0f3ff1f2", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "46851842698e2dfc87f7410bccd1976bed5d7ebdaa47b40faa0016ddb2283357", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "899acb0f8bf2cdeb55a1f232694ae6d5d4ef4aee61db33235eebfcd1d5dc3470", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "432fdf59a49379c72f45dc10306f20a180b53968000ad51960ee358a6696ab96", + "src/materialization_worker.rs": "e90314596351dbdfc4dd7219cab3757a89f888fd5533bf1337e7e436cd5881f0", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "b7b7bf8188609029abf769b4c44591f715fdbca908c0a2255f0d0cc9b9ea0326", + "src/process_lifecycle/unix.rs": "bbe2c2cc599f9472047faaad821eacb813653082bb16f713118636d7717093a5", + "src/process_lifecycle/windows.rs": "5dc75bc172428ac7c282c25b7aea04032c32a9614f00f6917d1c358669271682", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "7d18d8b1c202c0443717d632c2c5847aad4993d7b223792de85287dee8d81198", + "src/scan.rs": "bf8dc96640d90ae0780da0b7867e79f256eea32adde03984dc96aa62c4b02825", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "359385201e8af617957ee854be2069092b1cdebfc96c5b738962c874ce445481", + "src/staging_writer/files.rs": "2c65ec06747cca9a551a9c0f0ec9a5fab2fa70adebbe3fff0289d1bde6e4bbaf", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "4e0e8a8baadeb8e7d1904945ab11bdffec40dc68319451d0b3898d67baa7cac1", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "18a34283f11dad63417b527c2fd7483b265d98634e74b39abbc01b5fab1483ab", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "a8bb246054fb39d559bd7d89e9c8ba1370c4fe805d97dc73cdc35fd9cbf49d84", + "src/storage/direct.rs": "faad998af4467a00fd0cd946ecab5dbb3628c7bcc3aa1f24ace166472f1149d2", + "src/storage/inventory.rs": "f7c4c9d83f79f68ea2ca3cf16bca08a6a841536b3769e5f6ef6f18740ac628b7", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "aaa4749796db57209916aef34c001e25357955a94c6023eec1b5796e038bc549", + "src/storage/maintenance.rs": "d934271636dbff9c98f8186e5895adb9b26cd740238462e8262b3589b8d62741", + "src/storage/maintenance_admission.rs": "235e0633132cc88f6f5b316be68076215092466e412965a6684995275d4e25e0", + "src/storage/maintenance_automatic_direct.rs": "7516ebce83df47d3067f2538f54231c13fb435411599174a304aa04a22a1965e", + "src/storage/maintenance_backup.rs": "ef38f7d1c621f1f3b6d9deafdae1d2a72da63b74f8f5c024fddaaf2a9c983bd7", + "src/storage/maintenance_budget.rs": "2d15689d5811386c87505cf8b89a0c90b066f781020f18e9fe5a69aa4ba5d8aa", + "src/storage/maintenance_candidate.rs": "3265a5347a932d4d2c8c9884c0974a2f8bde99cdbeb41ecbec9ef3471a72cc27", + "src/storage/maintenance_controls.rs": "e849ddc57abebbb92c73aee720506a3d3ae4be78ce0e70ce6f2faaad9f72e434", + "src/storage/maintenance_generation_engine.rs": "639a9857928a57504fb059c5650f4a24ee7cd4cb7e4b6b8e31ab1d4d6614692b", + "src/storage/maintenance_generation_gate.rs": "e109a6e00394341d22abd7a3beb7ec478ac776e667fd592694722d22974001bc", + "src/storage/maintenance_generations.rs": "1936c84203b0a659bba927fcd9facb709c9fd3f401852fe66dc6d602b7d6775b", + "src/storage/maintenance_journal.rs": "d9f903b20bd05614af5246a70364e4c17b53f250b24dca242d673f83f52bbd48", + "src/storage/maintenance_json.rs": "7cf5baff196bbbab8a77fb646cb338b1a95f89c6ef657acd0bf2139a5b7fd3ac", + "src/storage/maintenance_legacy_association.rs": "49df4ef23a396866e48b2e0006e418521b34510885511ba5b25142e45c2ca151", + "src/storage/maintenance_legacy_bootstrap.rs": "8c79fc1dc2b3f46fe15d8af2a97acd688c1bbf669e73a15b4bc2f2067baab743", + "src/storage/maintenance_legacy_coverage.rs": "32e7eb2b5137bc4a5672da7e39db9e738d5c8376b542cce333e4227588adafd6", + "src/storage/maintenance_legacy_engine.rs": "806a916a2823dd395c9a2551276761ea0a46632e1f42d5ec7588ee9005c80aad", + "src/storage/maintenance_legacy_generations_engine.rs": "b00614a6da6417384648ef1a5e32f2a4e7b32c290644178886eed749e8ed2b3c", + "src/storage/maintenance_legacy_guard.rs": "842e07a1cd42daba36daa1b2379f581047fb0483b9749f1084757c4452618f75", + "src/storage/maintenance_legacy_source_recovery_tests.rs": "f2ea4f2be57684e66d85b4cbbd4cf526e4482661176303bf22a149a021767979", + "src/storage/maintenance_legacy_staging.rs": "9251f63d4924186d764d31da369b980424230a5634ad6e1a96fbae493325e9b2", + "src/storage/maintenance_legacy_stream.rs": "ecbe24522e7615b879c36b2c7fc80b79f8c54779ff192907902a4f836dd2708c", + "src/storage/maintenance_native_projection.rs": "a54f389b354944c3a5ca62230acbf67a28ce460c8ea59aa9bdd74449143df90e", + "src/storage/maintenance_origin.rs": "f7befcc03f5dbf76b81448178b6e14bab304148e63c412babbc78225509b7671", + "src/storage/maintenance_ownership.rs": "03d08e8698e4c4bc3bca6885f687e8e7b3d6c0f21578bbcea7b9ec23f59cdcfc", + "src/storage/maintenance_projection.rs": "48380c642762e9a6a928de8b0917490b5f3ee0a8263f8ed2c6ec64955ec0bbe2", + "src/storage/maintenance_references.rs": "78094003a14879441255272657b4044080a9592b7ca59f9794926934813e762b", + "src/storage/maintenance_retirement.rs": "f0d0db19b03321bfd1890b94eefbb809866c58345759df8264beb06e39a22161", + "src/storage/maintenance_scheduler.rs": "a65509524dd191c22471da3fee6067e4272e4bad33bee818b1ab0367fba5949e", + "src/storage/maintenance_validation.rs": "403e3edf264b762edd852e32ce6c5dd628bdd29f9a0728ac80cf17cdd8e509c2", + "src/storage/maintenance_workspace_gate.rs": "5dcef864f6077eccaff80b9019e2a06d9b64839fc91cfb21c6977d992d09a6fc", + "src/storage/managed.rs": "8090dfa8926c954b569f84eb9db507f09fee04f16c6e5659abe352b5b239937c", + "src/storage/mod.rs": "c8d46bd2fcf6ada768a91b2fa0480b9c35f49c77158e5e57dd4a847bcbc1121a", + "src/storage/run_workspace.rs": "2b3284a6ff9b49b2249d95d52dc1ae2d96de8267c591d342a266dd910ca2f273", + "src/storage/test_harness.rs": "dd391c4db26b4314345fd6425df56bdeed8538bb02f54b20aa0f054f39a68500", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "tests/agent_hooks_process.rs": "364d029cd0d4daf328ba46a1254e1d9a6f5df4e7fc9b0a86bcbf7740b6030478", + "tests/auto_repo_root.rs": "09d5d09635409c18b2d9b1da8a2272766b4fa5be63d93efd3aa4fdaf0f93fc76", + "tests/coordinator_process.rs": "a1ec773c1ee3aeb9875a65c0de66b4d7f757a6a70ebd20d1d8791a02b2c2f980", + "tests/generation_storage.rs": "fed3e5fb8989234d0dce778f7e3b793efd7bedf449eaeb1c2c6a7b522e5f1ded", + "tests/http_admission_process.rs": "3dd5533bfd85467027710b37d77be2ebfc84832d23942f3a5398c335b151dbb9", + "tests/http_daemon_process.rs": "fc651df00823b1d3f854d6627dc80c0d10227d78597c1adab8e417d8e0a55d46", + "tests/native_request_identity.rs": "f624bd73ba6b282f415a6251edca1c8d0e85a7d9bcacad1fe2d020e732c74eb9", + "tests/refresh_recovery_process.rs": "158e6795af9b9dd0b812f9ae7df579694c15238b4a0de5c36cca6a603fc63b5c", + "tests/storage_maintenance.rs": "16ccd5ba363ce2b4774163c70036a3a7971e9dcccff36d942ee70e84dadf06d1", + "tests/support/lifecycle_faults.rs": "0e8e052b39dd704aad218ffe87f1c497ce55b925faa849e6776c25fb730dbd9b", + "tests/support/storage_maintenance/collector.rs": "89cec41b2a8b2890795652ebe3f4b8c8b0b3bd0cd6439014aa3b86473d6fca4d", + "tests/support/storage_maintenance/mod.rs": "7594eacd903f327c882f26ef1d5c49847e20afd3d7fc37f2334efc62702f0fd5", + "tests/support/storage_maintenance/process.rs": "212a9d91e62f9648ed9e911cbd2b0daa20bce9286bf0d76c4636575054ea4669", + "tests/support/storage_maintenance/workload.rs": "570e5c0d70bdfccda3e655f2285f97269dea4573e67d524841df2ad15fed9085", + "vendor/tree-sitter-wat/LICENSE": "28a1c2b92a241a705632869e5c3a5e83a53bb182eb273c851565ae381f12982c", + "vendor/tree-sitter-wat/UPSTREAM.md": "edb5f2e65b8131e5ac0f6081e4fa7b8e271034cfe1f8f6577f8f70a7a75ce225", + "vendor/tree-sitter-wat/src/node-types.json": "b8ee14131666f1553d8948fa661526b2aa5d4bc5f64aa5f28f662fb33ebf453a", + "vendor/tree-sitter-wat/src/parser.c": "ba3659413ddfa7acd66d1739ca96ace762e6d08f2c60a43d22670503fac87ec6", + "vendor/tree-sitter-wat/src/scanner.c": "e6197b66843756e44e67ff6e9f61790b7df44c930ed1ba5b5fc1169874fc6d60", + "vendor/tree-sitter-wat/src/tree_sitter/alloc.h": "b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea", + "vendor/tree-sitter-wat/src/tree_sitter/array.h": "5bdf6ed1a78e3409fd443e085ca967a64c188a5d082aaf7f819bccd53a471c94", + "vendor/tree-sitter-wat/src/tree_sitter/parser.h": "180b893c8734778fd32f372dfbc27bd6ad1cd2221f26150b31256ff6716320d2" + }, + "binaries": [ + { + "target": "codebase_graph", + "path": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-ffa80bf96221ee79", + "sha256": "4cbdff3288cff4887ba408c7d108f165970a0a5970ef808fd5bfb78e2776542f" + } + ] +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/continuation-evidence.json b/docs/testing/graph-disk-cleanup-v2-evidence/continuation-evidence.json new file mode 100644 index 00000000..da520eaa --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/continuation-evidence.json @@ -0,0 +1,17 @@ +{ + "scope": "Historical tested and reviewed foundation checkpoints only; automatic cleanup remains under implementation.", + "last_code_commit": "61c9d26d52547bb52730fca3305b7e11863044d5", + "archive": "continuation-foundations-61c9d26.tar.gz", + "archive_sha256": "b8deceb0c639b55663298996d7aaeeedc2be74aae8e4181a45de8eb53d677f5b", + "archive_bytes": 5156327, + "raw_members": 1237, + "raw_bytes": 20175645, + "member_identity_manifest": "archive-members.json inside archive", + "verification": "Every archived member compared byte-for-byte by SHA-256 before original duplicate working copies were removed.", + "usage": "Extract into this evidence directory to resolve the original manifest-relative paths. Archive paths are relative to this directory.", + "limitations": [ + "Contains historical failed attempts and review inputs, not additional implementation approvals.", + "Current execution briefs and file ownership remain solely in Scryer.", + "Does not qualify a full collector, final native platforms, live rollout, or aggregate closure." + ] +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/continuation-foundations-61c9d26.tar.gz b/docs/testing/graph-disk-cleanup-v2-evidence/continuation-foundations-61c9d26.tar.gz new file mode 100644 index 00000000..3a46af31 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/continuation-foundations-61c9d26.tar.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/nonce-and-harness-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/nonce-and-harness-manifest.json new file mode 100644 index 00000000..d36e9c82 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/nonce-and-harness-manifest.json @@ -0,0 +1,167 @@ +{ + "scope": "Reviewed nonce restoration and combined all-target harness validation; no new collector/native Windows/Linux closure.", + "code_commit": "45a9eae45236f6a1527c1173c93e2cb652f67b8d", + "files": [ + { + "path": "s2-nonce-author/evidence.json", + "sha256": "50d03b4c943173728ed38e7461a4bc1b41bdfa526ccf640b4458c85289821c7f", + "bytes": 16415, + "source": "/private/tmp/cleanup-s2-nonce-successor-sxkwggsc/evidence.json", + "source_sha256": "50d03b4c943173728ed38e7461a4bc1b41bdfa526ccf640b4458c85289821c7f" + }, + { + "path": "s2-nonce-review/review-report.md", + "sha256": "a2a68efc0afc523e3fd6df4427e4e1544121b46370f355b4df8c18e49e5c82aa", + "bytes": 2697, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/review-report.md", + "source_sha256": "a2a68efc0afc523e3fd6df4427e4e1544121b46370f355b4df8c18e49e5c82aa" + }, + { + "path": "s2-nonce-review/review-report.json", + "sha256": "6ec580f052574dfdb6250a1d5d5d931ba2245465bfce0ec611e981885993b927", + "bytes": 15611, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/review-report.json", + "source_sha256": "6ec580f052574dfdb6250a1d5d5d931ba2245465bfce0ec611e981885993b927" + }, + { + "path": "s2-nonce-review/snapshot.json", + "sha256": "03539ee9352be8b73aa8adac67b4433912bb2c400c2fcdc0657a71de8eabb360", + "bytes": 756, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/snapshot.json", + "source_sha256": "03539ee9352be8b73aa8adac67b4433912bb2c400c2fcdc0657a71de8eabb360" + }, + { + "path": "s2-nonce-review/inputs-before.json", + "sha256": "633f6b85f709402197c306128c2baf790396419802ece90237e00f10088c82fc", + "bytes": 56656, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/inputs-before.json", + "source_sha256": "633f6b85f709402197c306128c2baf790396419802ece90237e00f10088c82fc" + }, + { + "path": "s2-nonce-review/inputs-after.json", + "sha256": "633f6b85f709402197c306128c2baf790396419802ece90237e00f10088c82fc", + "bytes": 56656, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/inputs-after.json", + "source_sha256": "633f6b85f709402197c306128c2baf790396419802ece90237e00f10088c82fc" + }, + { + "path": "s2-nonce-review/binaries-before.json", + "sha256": "9c6245e92f857a70db31d18896eaaf04d94b63e9a23e201ab36b35ea0cca41fb", + "bytes": 863, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/binaries-before.json", + "source_sha256": "9c6245e92f857a70db31d18896eaaf04d94b63e9a23e201ab36b35ea0cca41fb" + }, + { + "path": "s2-nonce-review/binaries-after.json", + "sha256": "9c6245e92f857a70db31d18896eaaf04d94b63e9a23e201ab36b35ea0cca41fb", + "bytes": 863, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/binaries-after.json", + "source_sha256": "9c6245e92f857a70db31d18896eaaf04d94b63e9a23e201ab36b35ea0cca41fb" + }, + { + "path": "s2-nonce-review/junit-lib-127.xml", + "sha256": "4d8f1cab7f7f3f6bc1ec872e4675698eb0f9300137ce0f95cfc537a442f518a7", + "bytes": 29106, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/junit-lib-127.xml", + "source_sha256": "4d8f1cab7f7f3f6bc1ec872e4675698eb0f9300137ce0f95cfc537a442f518a7" + }, + { + "path": "s2-nonce-review/junit-generation-5.xml", + "sha256": "a91de8ef7fdd13064f1ddae089c1b7a32cdd97de29867deeb8561c332b0cd68e", + "bytes": 1396, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/junit-generation-5.xml", + "source_sha256": "a91de8ef7fdd13064f1ddae089c1b7a32cdd97de29867deeb8561c332b0cd68e" + }, + { + "path": "s2-nonce-review/build.log.gz", + "sha256": "50a2114bd156d56d6162bfc00a544647fe98b7fbb7b130247e78db9dbdd167ad", + "bytes": 5639, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/build.log", + "source_sha256": "92165bb6da45e5c257683e7afd25c850c8aea55f7d6e5b0630cd9acc50ba23ad" + }, + { + "path": "s2-nonce-review/run-lib.log.gz", + "sha256": "f9499885b3dca35a79332e89bc1abf5a790cce340c82d762306bdb7a76e683ba", + "bytes": 4058, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/run-lib.log", + "source_sha256": "e8f7fa8229bcbcd931c8f832bc0315e05bf853f2f21d68b5a0ba8a8199bf0a06" + }, + { + "path": "s2-nonce-review/run-generation.log.gz", + "sha256": "7677c3e26eedd39b4bfc198c4c7ec7410aa9d0ac5e2ae89749773e473c9a5851", + "bytes": 5925, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/run-generation.log", + "source_sha256": "6ad099850cd1b28dd0de31ebc53776a10e85075c1186ebab772b56852efd2f34" + }, + { + "path": "s2-nonce-review/doctests.log.gz", + "sha256": "13922b48499aff08c7a1c5e7f649253db598335e75b6035722c810a95ea8c2d3", + "bytes": 5718, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/doctests.log", + "source_sha256": "837f823936b76b0082fe9a1f5fa8d699775382d4a881378a163365727e26d3a5" + }, + { + "path": "s2-nonce-review/original-regression-provenance.json", + "sha256": "b30dee6eb8db1571a14824767e3b41818ff694e6a0bb085793dfaf5c8011a4b9", + "bytes": 939, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/original-regression-provenance.json", + "source_sha256": "b30dee6eb8db1571a14824767e3b41818ff694e6a0bb085793dfaf5c8011a4b9" + }, + { + "path": "s2-nonce-review/original-regression.txt", + "sha256": "f9cf6fefd9f102fbb149c863cc4b38a17a271133dd1e86cde50fc40fe72a7cf5", + "bytes": 1006, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/original-regression.txt", + "source_sha256": "f9cf6fefd9f102fbb149c863cc4b38a17a271133dd1e86cde50fc40fe72a7cf5" + }, + { + "path": "s2-nonce-review/original-regression-ci-digest.txt", + "sha256": "d509a3b2257d4dae302ed1c43993d7476e10d8a355d395ae5c875bdcb6f91922", + "bytes": 1021, + "source": "/private/tmp/cleanup-s2-nonce-review-d6a30hff/original-regression-ci-digest.txt", + "source_sha256": "d509a3b2257d4dae302ed1c43993d7476e10d8a355d395ae5c875bdcb6f91922" + }, + { + "path": "combined-harness-176/snapshot.json", + "sha256": "d802a8dbcc2b8995c84bf92dde021c10c8da991a1e4f49a1832764c253631fbd", + "bytes": 270, + "source": "/private/tmp/cleanup-combined-harness-176/snapshot.json", + "source_sha256": "d802a8dbcc2b8995c84bf92dde021c10c8da991a1e4f49a1832764c253631fbd" + }, + { + "path": "combined-harness-176/report.json", + "sha256": "ef9c82cbc256ddd5727be613ea271bb2dcf5a7a08457fe2239728941b9e43eed", + "bytes": 1026, + "source": "/private/tmp/cleanup-combined-harness-176/report.json", + "source_sha256": "ef9c82cbc256ddd5727be613ea271bb2dcf5a7a08457fe2239728941b9e43eed" + }, + { + "path": "combined-harness-176/inputs-before.json", + "sha256": "6aa0241167b0c6f39e338fba18f9d7f2ad9ee26fde3f30bc64d9f2bce860c511", + "bytes": 20910, + "source": "/private/tmp/cleanup-combined-harness-176/inputs-before.json", + "source_sha256": "6aa0241167b0c6f39e338fba18f9d7f2ad9ee26fde3f30bc64d9f2bce860c511" + }, + { + "path": "combined-harness-176/inputs-after.json", + "sha256": "6aa0241167b0c6f39e338fba18f9d7f2ad9ee26fde3f30bc64d9f2bce860c511", + "bytes": 20910, + "source": "/private/tmp/cleanup-combined-harness-176/inputs-after.json", + "source_sha256": "6aa0241167b0c6f39e338fba18f9d7f2ad9ee26fde3f30bc64d9f2bce860c511" + }, + { + "path": "combined-harness-176/check.log.gz", + "sha256": "76cafb2f912ca77a31d4d2f83b64336920029eff6193f2cda7cd08b11f436f70", + "bytes": 9019, + "source": "/private/tmp/cleanup-combined-harness-176/check.log", + "source_sha256": "fa31a68842854240d89425991200b182f2e431fc3c0b30a2b89c8454205a291d" + }, + { + "path": "combined-harness-176/initial-wrong-target-interrupted.log.gz", + "sha256": "e7f7e7d20592317ea113228eabd67b5b0283f8a9490597d451194964765559b4", + "bytes": 1248, + "source": "/private/tmp/cleanup-combined-harness-176/initial-wrong-target-interrupted.log", + "source_sha256": "c927b3b08999260bc61305e65029c456369ced240bc40908d22386206f314b58" + } + ] +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/package-recompression.json b/docs/testing/graph-disk-cleanup-v2-evidence/package-recompression.json new file mode 100644 index 00000000..74ef2f96 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/package-recompression.json @@ -0,0 +1,332 @@ +{ + "result": { + "before_bytes": 10526627, + "after_bytes": 10478516, + "saved_bytes": 48111, + "limit_bytes": 10485760, + "headroom_bytes": 7244, + "before_sha256": "55b7328643aec86e6b2d549ccb369d014622a97f36b82bba688651578de5da04", + "after_sha256": "a23c31091569ece874f5ac8c987e4a9d20390577a948340ed67190fdac261b7b", + "member_count_before": 209, + "member_count_after": 209, + "member_names_unchanged": true, + "changed_members": [ + "codebase-graph-3.0.4/.cargo_vcs_info.json", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz", + "codebase-graph-3.0.4/assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz" + ], + "new_asset_count": 10, + "all_payloads_actual_lzma_rs_verified_identical": true + }, + "assets": { + "rows": [ + { + "path": "assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz", + "old_bytes": 548208, + "new_bytes": 546092, + "saved_bytes": 2116, + "old_sha256": "0954cd079bb3f24c633242e4cf31dfe949c54a5f575cd983edf45fb44b469e7f", + "new_sha256": "3bd371decbfbf2054c146f7723f44843bc88c95114b1bbc787497ad1d3d6cb2c", + "payload_bytes": 2225568, + "payload_sha256": "3ae7d7787aca397102e4a30a26ec2824884c3a66a2228c9e6997a9cfbc97a144", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 3145728, + "xz_filter": "--lzma2=dict=3MiB", + "profile": "lp1lc3" + }, + { + "path": "assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz", + "old_bytes": 273848, + "new_bytes": 272880, + "saved_bytes": 968, + "old_sha256": "f4349356314482b3c802017b56fb7b78efa31b81a85f9804c6f4273be15e6a58", + "new_sha256": "87805c35df6a990470b29b2376fef386d463cdb9e568acab3af325e2f04cc407", + "payload_bytes": 846920, + "payload_sha256": "39c51fa9b1915590a500eef732c76913aeb10cd942e46e1c259497e609b97426", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 1048576, + "xz_filter": "--lzma2=dict=1MiB", + "profile": "fb273" + }, + { + "path": "assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz", + "old_bytes": 478132, + "new_bytes": 469912, + "saved_bytes": 8220, + "old_sha256": "0494bdab7d96d07bc6693a8a62a6b852f64bab2670adbf6bd3c3c6fae57a8f8a", + "new_sha256": "36a30b551e7293c4045a1e892e0475a67e057de4a8c641da0ed3e88073c517e0", + "payload_bytes": 2264720, + "payload_sha256": "62b27ab48f033b2e480b10e2466afb9618af6cc2d58680d1f377aa4aae9de2f8", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 3145728, + "xz_filter": "--lzma2=dict=3MiB", + "profile": "lp2lc0" + }, + { + "path": "assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz", + "old_bytes": 246936, + "new_bytes": 241900, + "saved_bytes": 5036, + "old_sha256": "d649d27b7383277cc4764592fd2dd99a9dfd16d91f167da52dc71ed2fe75e5ba", + "new_sha256": "a0595f1509a3333f29e7a6eadb5a8089b4ac2f241538b64757fb2c24e1858d66", + "payload_bytes": 896464, + "payload_sha256": "e5bedcd4e51f4acbe9505efb9f4ab7ba970bb5359f64b131ef8bc9c3e026712f", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 1048576, + "xz_filter": "--lzma2=dict=1MiB", + "profile": "lp2lc0" + }, + { + "path": "assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz", + "old_bytes": 344032, + "new_bytes": 342516, + "saved_bytes": 1516, + "old_sha256": "8156ebd29b5bb2d0742994a9a738afefd807740d5ef74f5ab1d15cb2d10a8e03", + "new_sha256": "a9d60bfae1e6121eeb8ea0f610a9c8177e3f439956eeab39127822dfd2fa4016", + "payload_bytes": 1560888, + "payload_sha256": "5eac6f37333e29b25c6393cad6bbc3542c59a72e77e03b17caf6a5c0183ff7fa", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 1572864, + "xz_filter": "--lzma2=dict=1536KiB", + "profile": "fb273" + }, + { + "path": "assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz", + "old_bytes": 167480, + "new_bytes": 166616, + "saved_bytes": 864, + "old_sha256": "ff32b9298a5b271fffb8b10f48ef5425da3fa1f2a453c2522d894de7acbab753", + "new_sha256": "28b2df80cfa9a3631de8f7c2f8b6a524f0dda3e81ad01cf5e15c43990eb61437", + "payload_bytes": 639000, + "payload_sha256": "545b4bb45de54d0fe7d8137dc8370a7dce42b6006dc2b8048404a7309bfe764f", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 786432, + "xz_filter": "--lzma2=dict=768KiB", + "profile": "fb273" + }, + { + "path": "assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz", + "old_bytes": 317884, + "new_bytes": 313512, + "saved_bytes": 4372, + "old_sha256": "ac6faf62a5a6bd236ee27899327abcdc56a7bc2fd8267619f767ffe4e7254f93", + "new_sha256": "066f43565dfca6c1cbe79f40f9102aeb52c98b68b7fb74861a6be81d4318df4a", + "payload_bytes": 1562680, + "payload_sha256": "83ae4be9fa718577ca971f27ff890b3aea5789c9dd908784e28f52917febbfdb", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 1572864, + "xz_filter": "--lzma2=dict=1536KiB", + "profile": "lp2lc1" + }, + { + "path": "assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz", + "old_bytes": 145232, + "new_bytes": 142952, + "saved_bytes": 2280, + "old_sha256": "b36f55d57125ecf05b8ba6d9c53e4d6d2532d81f09e9a37b8294d8b46e01c957", + "new_sha256": "7d39d0ba2fe3eb41364febc09fb45aca02b37bb64a06e3a3666bfb70f49792cd", + "payload_bytes": 630440, + "payload_sha256": "e6482a025de31d662ee4b32a39c991706b8cc3c2ec49673ef5d8ce45cc23ce7a", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 786432, + "xz_filter": "--lzma2=dict=768KiB", + "profile": "lp2lc0" + }, + { + "path": "assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz", + "old_bytes": 3507432, + "new_bytes": 3496500, + "saved_bytes": 10932, + "old_sha256": "ffc724819419c3c9e93e810cde560acea93a445c9d260f1041c3c99aaa75c8df", + "new_sha256": "ef05f061c330115537be9051ca9d274e4502d92fdb05e628fd985c166b3dcced", + "payload_bytes": 14779904, + "payload_sha256": "7f82059c8149bb7851420a9bfd0c46859e92efaeda0bcd5c65418d8f174edac4", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 16777216, + "xz_filter": "--lzma2=dict=16MiB", + "profile": "fb273lp1lc3" + }, + { + "path": "assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz", + "old_bytes": 3385328, + "new_bytes": 3373236, + "saved_bytes": 12092, + "old_sha256": "07c20799a0ba85e43f5ad09fd599b708653b614f07ccd5b5743d7432438ceafd", + "new_sha256": "e374f49a261ba8818cfff1386e7a1b01694b268f77f5cd308668b62aeca1c503", + "payload_bytes": 14178304, + "payload_sha256": "bdecb5201dd6abccc06ddc1f6cb4fcf0be3adb7aa3ddaac2f383ed129fca2b1f", + "actual_lzma_rs_original_and_new_match": true, + "new_dictionary_bytes": 16777216, + "xz_filter": "--lzma2=dict=16MiB", + "profile": "fb273lp1lc3" + } + ], + "old_total": 9414512, + "new_total": 9366116, + "saved_total": 48396, + "applied": true + }, + "method": "Lossless LZMA2-only XZ recompression; CRC64; supported platform set and runtime-decoded payloads unchanged. Exact comparisons used actual lzma-rs0.3 runtime rlib. Dictionaries are lower than the original64MiB, with no decoder-budget increase. Private cargo package --offline --locked --no-verify --allow-dirty; full Cargo verify remains CI.", + "vendor_provenance": { + "source_page": "https://www.7-zip.org/download.html", + "release_url": "https://github.com/ip7z/7zip/releases/tag/26.05", + "asset": { + "url": "https://api.github.com/repos/ip7z/7zip/releases/assets/662536759", + "id": 662536759, + "node_id": "RA_kwDOG81nRs4nfYI3", + "name": "7z2605-mac.tar.xz", + "label": null, + "uploader": { + "login": "ip7z", + "id": 87184205, + "node_id": "MDQ6VXNlcjg3MTg0MjA1", + "avatar_url": "https://avatars.githubusercontent.com/u/87184205?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ip7z", + "html_url": "https://github.com/ip7z", + "followers_url": "https://api.github.com/users/ip7z/followers", + "following_url": "https://api.github.com/users/ip7z/following{/other_user}", + "gists_url": "https://api.github.com/users/ip7z/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ip7z/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ip7z/subscriptions", + "organizations_url": "https://api.github.com/users/ip7z/orgs", + "repos_url": "https://api.github.com/users/ip7z/repos", + "events_url": "https://api.github.com/users/ip7z/events{/privacy}", + "received_events_url": "https://api.github.com/users/ip7z/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 1868068, + "digest": "sha256:12ef6830317743cfd3f0ab1724bde43fcc270d8b6d8564f8c12c2a2b7f993570", + "download_count": 6251, + "created_at": "2026-10-21T13:05:12Z", + "updated_at": "2026-10-21T13:05:12Z", + "browser_download_url": "https://github.com/ip7z/7zip/releases/download/26.05/7z2605-mac.tar.xz" + }, + "actual_sha256": "12ef6830317743cfd3f0ab1724bde43fcc270d8b6d8564f8c12c2a2b7f993570" + }, + "after_platform_fixes": { + "final_archive": "/private/tmp/cgraph-package-size-private-final-256/package/codebase-graph-3.0.4.crate", + "final_bytes": 10479813, + "limit_bytes": 10485760, + "headroom_bytes": 5947, + "final_sha256": "bc87d245246f76649d8e9ad47cc2eb3385b97e93c3a3232414537d983688093e", + "member_count": 209, + "member_names_unchanged_since_recompression": true, + "changed_members_since_recompression": [ + "codebase-graph-3.0.4/.cargo_vcs_info.json", + "codebase-graph-3.0.4/src/process_lifecycle/unix.rs", + "codebase-graph-3.0.4/src/storage/maintenance_generation_engine.rs", + "codebase-graph-3.0.4/src/storage/maintenance_generations.rs" + ], + "initial_recompression_proof": "/private/tmp/cgraph-package-size-private-256/selected-verified-assets.json", + "initial_archive_comparison": "/private/tmp/cgraph-package-size-private-256/package-result.json", + "initial_recompressed_archive_bytes": 10478516, + "source_growth_since_recompression_bytes": 1297 + }, + "after_terminal_frame_fix": { + "archive": "/private/tmp/cgraph-package-size-private-after-terminal/package/codebase-graph-3.0.4.crate", + "bytes": 10480188, + "limit_bytes": 10485760, + "headroom_bytes": 5572, + "sha256": "90e5387edc4963967e8f44f3bbde2bd46dcab9070d3fee35ce322f6807ce276a", + "member_count": 209, + "member_names_unchanged": true, + "changed_members_since_final_package_result": [ + "codebase-graph-3.0.4/.cargo_vcs_info.json", + "codebase-graph-3.0.4/src/storage/maintenance_candidate.rs", + "codebase-graph-3.0.4/src/storage/maintenance_workspace_gate.rs" + ], + "previous_result": "/private/tmp/cgraph-package-size-private-256/final-package-result.json", + "initial_recompression_proof": "/private/tmp/cgraph-package-size-private-256/selected-verified-assets.json", + "growth_since_previous_bytes": 375 + }, + "after_legacy_cohort_frame_fix": { + "archive": "/private/tmp/cgraph-package-size-private-after-legacy-cohort-frame/package/codebase-graph-3.0.4.crate", + "bytes": 10480668, + "limit_bytes": 10485760, + "headroom_bytes": 5092, + "sha256": "d2356ff602488114038b56b97d05ac8527a0a6fa69c54d069ffd769cf7099e5e", + "member_count": 209, + "member_names_unchanged": true, + "changed_members_since_after_terminal": [ + "codebase-graph-3.0.4/.cargo_vcs_info.json", + "codebase-graph-3.0.4/src/storage/maintenance_legacy_bootstrap.rs", + "codebase-graph-3.0.4/src/storage/maintenance_legacy_generations_engine.rs", + "codebase-graph-3.0.4/src/storage/maintenance_workspace_gate.rs" + ], + "previous_result": "/private/tmp/cgraph-package-size-private-256/after-terminal-package-result.json", + "initial_recompression_proof": "/private/tmp/cgraph-package-size-private-256/selected-verified-assets.json", + "growth_since_previous_bytes": 480, + "source_readiness": "Association final no-opt ASM and main/unit private Clippy zero diagnostics; stable source hashes confirmed before archiving." + }, + "after_source_observation_frame_fix": { + "archive": "/private/tmp/cgraph-package-size-private-after-source-observation/package/codebase-graph-3.0.4.crate", + "bytes": 10481556, + "limit_bytes": 10485760, + "headroom_bytes": 4204, + "sha256": "d88a7e0565711e4484f7e37fd0ec6b26950ecb3dd3eb6886b44d134fe3bb05e0", + "member_count": 209, + "member_names_unchanged": true, + "changed_members_since_after_legacy_cohort_frame": [ + "codebase-graph-3.0.4/.cargo_vcs_info.json", + "codebase-graph-3.0.4/src/storage/maintenance_legacy_bootstrap.rs", + "codebase-graph-3.0.4/src/storage/maintenance_legacy_guard.rs" + ], + "previous_result": "/private/tmp/cgraph-package-size-private-256/after-legacy-cohort-frame-package-result.json", + "initial_recompression_proof": "/private/tmp/cgraph-package-size-private-256/selected-verified-assets.json", + "growth_since_previous_bytes": 888, + "source_readiness": "Association final no-opt ASM and main/unit private Clippy zero diagnostics with stable source hashes confirmed before archive.", + "qualification_scope": "Archive cap only; no runtime/native qualification inferred." + }, + "after_staging_scope_frame_fix": { + "archive": "/private/tmp/cgraph-package-size-private-after-staging-scope/package/codebase-graph-3.0.4.crate", + "bytes": 10481968, + "limit_bytes": 10485760, + "headroom_bytes": 3792, + "sha256": "a8ccb888849e9c941501a2aa55c5f391fde6e4018977925ff79f7223182fe5e7", + "member_count": 209, + "member_names_unchanged": true, + "changed_members_since_after_source_observation": [ + "codebase-graph-3.0.4/.cargo_vcs_info.json", + "codebase-graph-3.0.4/src/storage/maintenance_legacy_bootstrap.rs", + "codebase-graph-3.0.4/src/storage/maintenance_legacy_engine.rs" + ], + "previous_result": "/private/tmp/cgraph-package-size-private-256/after-source-observation-package-result.json", + "initial_recompression_proof": "/private/tmp/cgraph-package-size-private-256/selected-verified-assets.json", + "growth_since_previous_bytes": 412, + "source_readiness": "Association final no-opt ASM and main/unit private Clippy zero diagnostics with stable source hashes confirmed before archive.", + "qualification_scope": "Archive cap only; no runtime/native qualification inferred." + }, + "after_private_metadata_frame_fix": { + "archive": "/private/tmp/cgraph-package-size-private-after-private-metadata/package/codebase-graph-3.0.4.crate", + "bytes": 10482710, + "limit_bytes": 10485760, + "headroom_bytes": 3050, + "sha256": "c7ad475a276f11a9c4ddc249bb507cb49b8d75278ebacc4e95c1381604883477", + "member_count": 209, + "member_names_unchanged": true, + "changed_members_since_after_staging_scope": [ + "codebase-graph-3.0.4/.cargo_vcs_info.json", + "codebase-graph-3.0.4/src/storage/maintenance_admission.rs", + "codebase-graph-3.0.4/src/storage/maintenance_legacy_engine.rs", + "codebase-graph-3.0.4/src/storage/maintenance_legacy_stream.rs" + ], + "previous_result": "/private/tmp/cgraph-package-size-private-256/after-staging-scope-package-result.json", + "initial_recompression_proof": "/private/tmp/cgraph-package-size-private-256/selected-verified-assets.json", + "growth_since_previous_bytes": 742, + "source_readiness": "Association final no-opt ASM and main/unit private Clippy zero diagnostics with stable source hashes confirmed before archive.", + "qualification_scope": "Archive cap only; no runtime/native qualification inferred." + } +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/reviewed-foundations-continuation-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/reviewed-foundations-continuation-manifest.json new file mode 100644 index 00000000..f2f7ced2 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/reviewed-foundations-continuation-manifest.json @@ -0,0 +1,1054 @@ +{ + "scope": "Additional independently reviewed private foundation evidence; no full collector/runtime/native-platform/aggregate completion. Historical failures remain explicitly historical.", + "code_commits": { + "s2_bc": "993212c7f1ef0829e6d134f7ce3a0195e068bdc1", + "s3_validation": "2ad634f", + "s4_joined_driver": "956b46c", + "s5_fresh_build_harness": "dc5692f" + }, + "files": [ + { + "path": "s2-bc-review/review-report.md", + "bytes": 2586, + "sha256": "d08591d1690e04168d4cbd0026394cffac24856302569e6f39b7d30d4589d93b", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/review-report.md", + "source_sha256": "d08591d1690e04168d4cbd0026394cffac24856302569e6f39b7d30d4589d93b" + }, + { + "path": "s2-bc-review/review-report.json", + "bytes": 17435, + "sha256": "2c19ce40cc9d981f71f7c643834bb32ae95363dfedb7d973d2ca200020108c22", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/review-report.json", + "source_sha256": "2c19ce40cc9d981f71f7c643834bb32ae95363dfedb7d973d2ca200020108c22" + }, + { + "path": "s2-bc-review/snapshot.json", + "bytes": 1176, + "sha256": "615056086a60d4da3d77f28f7ea7354abf82a845c8a23c000434f3f76c93a435", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/snapshot.json", + "source_sha256": "615056086a60d4da3d77f28f7ea7354abf82a845c8a23c000434f3f76c93a435" + }, + { + "path": "s2-bc-review/inputs-before.json", + "bytes": 51483, + "sha256": "d8efe6e3aa2cbeb5ba6d57caa01e34104ddb444af389fbefc1b558a1443ec1dd", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/inputs-before.json", + "source_sha256": "d8efe6e3aa2cbeb5ba6d57caa01e34104ddb444af389fbefc1b558a1443ec1dd" + }, + { + "path": "s2-bc-review/inputs-after.json", + "bytes": 51483, + "sha256": "d8efe6e3aa2cbeb5ba6d57caa01e34104ddb444af389fbefc1b558a1443ec1dd", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/inputs-after.json", + "source_sha256": "d8efe6e3aa2cbeb5ba6d57caa01e34104ddb444af389fbefc1b558a1443ec1dd" + }, + { + "path": "s2-bc-review/binary-before.json", + "bytes": 2585, + "sha256": "32a780010edc6d8157c1d88aebb4effe3569e3823b44ce06a71f4ba59478af74", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/binary-before.json", + "source_sha256": "32a780010edc6d8157c1d88aebb4effe3569e3823b44ce06a71f4ba59478af74" + }, + { + "path": "s2-bc-review/binary-after.json", + "bytes": 2585, + "sha256": "32a780010edc6d8157c1d88aebb4effe3569e3823b44ce06a71f4ba59478af74", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/binary-after.json", + "source_sha256": "32a780010edc6d8157c1d88aebb4effe3569e3823b44ce06a71f4ba59478af74" + }, + { + "path": "s2-bc-review/junit.xml", + "bytes": 18004, + "sha256": "386fbe7dfea6fdb9fd78d1d45f494e0be7617c83a7fdbdaef893a529b14c3da5", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/junit.xml", + "source_sha256": "386fbe7dfea6fdb9fd78d1d45f494e0be7617c83a7fdbdaef893a529b14c3da5" + }, + { + "path": "s2-bc-review/build.log.gz", + "bytes": 155, + "sha256": "aba68acd45956929eddab9d67f47315495c62ec9f00bac2444ea0aac771a9268", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/build.log", + "source_sha256": "0ed08383b3b63178f4a91c8d858c77d61aba195fa3199ab8189835eeddcb904f" + }, + { + "path": "s2-bc-review/run.log.gz", + "bytes": 2627, + "sha256": "e99beff6e63706a3cbf3e83f192fffa484606a36c520a63b1bf7918aa38dcfac", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/run.log", + "source_sha256": "b60389d264c199587e93be2557d37fa101c4ef344006593704176c84db1fb039" + }, + { + "path": "s2-bc-review/trait-probes.json", + "bytes": 2065, + "sha256": "566fd751ad1fcd79c6b9125259749c2318347d2fe844fc818d4edfed398b86d4", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/trait-probes.json", + "source_sha256": "566fd751ad1fcd79c6b9125259749c2318347d2fe844fc818d4edfed398b86d4" + }, + { + "path": "s2-bc-review/trait-negative.rs", + "bytes": 456, + "sha256": "8cb6f9a438f929aad9dcffaa2d0f70bfca7338c385de2a60c3e718f445e4a3ab", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/trait-negative.rs", + "source_sha256": "8cb6f9a438f929aad9dcffaa2d0f70bfca7338c385de2a60c3e718f445e4a3ab" + }, + { + "path": "s2-bc-review/trait-positive.rs", + "bytes": 506, + "sha256": "f11fee1738a04237487d5deeac6d4fbba8a7e36d139c3f6849eff1c4243801df", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/trait-positive.rs", + "source_sha256": "f11fee1738a04237487d5deeac6d4fbba8a7e36d139c3f6849eff1c4243801df" + }, + { + "path": "s2-bc-review/trait-negative.log.gz", + "bytes": 610, + "sha256": "7dfa3c85ed716c1932caae187759e334ec933433441009805530bec6599f717b", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/trait-negative.log", + "source_sha256": "98ea182be94f8a95de420c62efe69b75dcb67ca390af246e49a74a4928e2a3af" + }, + { + "path": "s2-bc-review/trait-positive.log.gz", + "bytes": 20, + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/trait-positive.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s2-bc-review/trait-initial-missing-externs.log.gz", + "bytes": 752, + "sha256": "7ff1e2a1c62d704d55b09477d8a4845bb2c63f33845b7a17dd8cc882d4e96e2c", + "source": "/private/tmp/cleanup-s2-bc-review-rm5ezv2w/trait-initial-missing-externs.log", + "source_sha256": "04c06240ae24174d7ca26acf6e62bb3d55b6d3b6fcf31fd383661dcd69e1fc84" + }, + { + "path": "s3-validation-author/evidence-summary.json", + "bytes": 2655, + "sha256": "897530452d429525e69975169173e73e1bff80ee9e54ce3e16a4747e4a9b4163", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/evidence-summary.json", + "source_sha256": "897530452d429525e69975169173e73e1bff80ee9e54ce3e16a4747e4a9b4163" + }, + { + "path": "s3-validation-author/source-manifest.json", + "bytes": 1231, + "sha256": "e4b303cccc388e005a5522aae6fec3ed7b85f0e82193e3bc9af6b9564b8ff7bd", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/source-manifest.json", + "source_sha256": "e4b303cccc388e005a5522aae6fec3ed7b85f0e82193e3bc9af6b9564b8ff7bd" + }, + { + "path": "s3-validation-author/all-inputs-before.json", + "bytes": 65430, + "sha256": "4442a0131b8c40e7014dea196df54074197f66b2cdd17a9d9b27c0122dd12f38", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/all-inputs-before.json", + "source_sha256": "4442a0131b8c40e7014dea196df54074197f66b2cdd17a9d9b27c0122dd12f38" + }, + { + "path": "s3-validation-author/all-inputs-after.json", + "bytes": 65430, + "sha256": "4442a0131b8c40e7014dea196df54074197f66b2cdd17a9d9b27c0122dd12f38", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/all-inputs-after.json", + "source_sha256": "4442a0131b8c40e7014dea196df54074197f66b2cdd17a9d9b27c0122dd12f38" + }, + { + "path": "s3-validation-author/binaries-before.json", + "bytes": 230, + "sha256": "8dc74c6f4212330ff530a629714657dd3beaa1ac33e0a3f45ff42bc173aa43ed", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/binaries-before.json", + "source_sha256": "8dc74c6f4212330ff530a629714657dd3beaa1ac33e0a3f45ff42bc173aa43ed" + }, + { + "path": "s3-validation-author/binaries-after.json", + "bytes": 481, + "sha256": "71369ff1110de3fa2e94d161c357c81a665498d62be4baeda9c17d7dcea84c35", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/binaries-after.json", + "source_sha256": "71369ff1110de3fa2e94d161c357c81a665498d62be4baeda9c17d7dcea84c35" + }, + { + "path": "s3-validation-author/junit.xml", + "bytes": 18119, + "sha256": "5a44c64cf1685043b81826803cb410f4bfffecbc65bc9cb43722b68323481bbb", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/junit.xml", + "source_sha256": "5a44c64cf1685043b81826803cb410f4bfffecbc65bc9cb43722b68323481bbb" + }, + { + "path": "s3-validation-author/serde-json-features.txt", + "bytes": 1213, + "sha256": "6a1a94503dcc2729974394620ff1ccca525368ebdf92910d72d8b69ccf6d6763", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/serde-json-features.txt", + "source_sha256": "6a1a94503dcc2729974394620ff1ccca525368ebdf92910d72d8b69ccf6d6763" + }, + { + "path": "s3-validation-author/build-list.log.gz", + "bytes": 154, + "sha256": "5f65d07c84081bb62ef0010f71d7b57860797752885d7298031413d5865ca193", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/build-list.log", + "source_sha256": "4a06c21601e69d4bd8ba0c524db3600eb02a766a0c15e5cc310f10797ecbe95b" + }, + { + "path": "s3-validation-author/nextest-run.log.gz", + "bytes": 14068, + "sha256": "739f078c2d287169044e9758d2b4943da4a8a9e65be9106226d17e49c0ffc18a", + "source": "/private/tmp/dc-s3-validation-164-gjtbfe3o/nextest-run.log", + "source_sha256": "21bbfeb5282aebd46516f1b7be3c4dfa14441f42a6f403bc42ea3d6b5e63672a" + }, + { + "path": "s3-validation-review/review.md", + "bytes": 13821, + "sha256": "e72e5617a4deadc29ae5b2b9ca8caea614dabb1dcf34f22335f5e591ede95e0c", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/review.md", + "source_sha256": "e72e5617a4deadc29ae5b2b9ca8caea614dabb1dcf34f22335f5e591ede95e0c" + }, + { + "path": "s3-validation-review/verification.json", + "bytes": 4695, + "sha256": "7dca1298663efb455e5a4b7ffcf8286eaba1484ed4f94b1fc0bac1be114e00a4", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/verification.json", + "source_sha256": "7dca1298663efb455e5a4b7ffcf8286eaba1484ed4f94b1fc0bac1be114e00a4" + }, + { + "path": "s3-validation-review/review-artifact-sha256.json", + "bytes": 2231, + "sha256": "92a24f4e1e7f2cf72a0a3739fca96f34658d48742bd89959a6bdeeee321b403b", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/review-artifact-sha256.json", + "source_sha256": "92a24f4e1e7f2cf72a0a3739fca96f34658d48742bd89959a6bdeeee321b403b" + }, + { + "path": "s3-validation-review/source-manifest.json", + "bytes": 1231, + "sha256": "e4b303cccc388e005a5522aae6fec3ed7b85f0e82193e3bc9af6b9564b8ff7bd", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/source-manifest.json", + "source_sha256": "e4b303cccc388e005a5522aae6fec3ed7b85f0e82193e3bc9af6b9564b8ff7bd" + }, + { + "path": "s3-validation-review/all-inputs-before.json", + "bytes": 65430, + "sha256": "4442a0131b8c40e7014dea196df54074197f66b2cdd17a9d9b27c0122dd12f38", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/all-inputs-before.json", + "source_sha256": "4442a0131b8c40e7014dea196df54074197f66b2cdd17a9d9b27c0122dd12f38" + }, + { + "path": "s3-validation-review/snapshot-verification.json", + "bytes": 329, + "sha256": "be274a9914ae3bc937784decfb0341c40e13502fbcec85250d003b15057d2472", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/snapshot-verification.json", + "source_sha256": "be274a9914ae3bc937784decfb0341c40e13502fbcec85250d003b15057d2472" + }, + { + "path": "s3-validation-review/binaries.json", + "bytes": 8797, + "sha256": "1f8b5a40ac99ef5b3a051bea8b449ad20d6aac59bb328706268e8a1691b0fd0d", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/binaries.json", + "source_sha256": "1f8b5a40ac99ef5b3a051bea8b449ad20d6aac59bb328706268e8a1691b0fd0d" + }, + { + "path": "s3-validation-review/junit.xml", + "bytes": 18119, + "sha256": "7b70db1dde9093a33ecf0e3a8a360113612fa2661b4b1e7ab7e7310b3f9fb96e", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/junit.xml", + "source_sha256": "7b70db1dde9093a33ecf0e3a8a360113612fa2661b4b1e7ab7e7310b3f9fb96e" + }, + { + "path": "s3-validation-review/build-list.log.gz", + "bytes": 163, + "sha256": "8865aa63370b803f38feca7e7a0d3d2719c1c9e6710caa5071b45173b1da5456", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/build-list.log", + "source_sha256": "cd6c30156a68f8f8742e047cb6332dfb85deee6a611bd90bcd42a559a3fc7d64" + }, + { + "path": "s3-validation-review/nextest-run.log.gz", + "bytes": 2751, + "sha256": "9d81951d1fcef94603d35e0622a3d6bb26a10126b37b949e2281ef6ee22fca00", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/nextest-run.log", + "source_sha256": "bd570a2d4db8a02ffc08a7e5e82cbf38b69a29a2b53ec920d04916d54caf4546" + }, + { + "path": "s3-validation-review/probe-final-result.json", + "bytes": 182, + "sha256": "bc2897ae579ae1e2b64da8d9cd298b50f8c28c019f34d1ceb20d769c2720e728", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/probe-final-result.json", + "source_sha256": "bc2897ae579ae1e2b64da8d9cd298b50f8c28c019f34d1ceb20d769c2720e728" + }, + { + "path": "s3-validation-review/probe-final-result.stderr", + "bytes": 108, + "sha256": "559b8c0b3df3909a7988bf3ecd34c1c1b014bd9ef55fd87a0145cb0d9696546b", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/probe-final-result.stderr", + "source_sha256": "559b8c0b3df3909a7988bf3ecd34c1c1b014bd9ef55fd87a0145cb0d9696546b" + }, + { + "path": "s3-validation-review/probe-dependencies.json", + "bytes": 11307, + "sha256": "0ad7474c6fa5d422b9b87812339d0df86fec90083bad5b5cf264724e95cd538f", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/probe-dependencies.json", + "source_sha256": "0ad7474c6fa5d422b9b87812339d0df86fec90083bad5b5cf264724e95cd538f" + }, + { + "path": "s3-validation-review/probe-command.json", + "bytes": 647, + "sha256": "34bc788b843487080f2ed94edd2ded4258f56d081987d309b4c081d68eaf52e6", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/probe-command.json", + "source_sha256": "34bc788b843487080f2ed94edd2ded4258f56d081987d309b4c081d68eaf52e6" + }, + { + "path": "s3-validation-review/serde-json-features.txt", + "bytes": 1213, + "sha256": "6a1a94503dcc2729974394620ff1ccca525368ebdf92910d72d8b69ccf6d6763", + "source": "/private/tmp/cleanup-v2-s3-validation-review-4c1uud_k/serde-json-features.txt", + "source_sha256": "6a1a94503dcc2729974394620ff1ccca525368ebdf92910d72d8b69ccf6d6763" + }, + { + "path": "s4-driver-author-v6/evidence.json", + "bytes": 2192, + "sha256": "bf9ee86ef3751df98a68634983e1a799cae44f2a6e172cf5bd5df2d3c3d75a6a", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/evidence.json", + "source_sha256": "bf9ee86ef3751df98a68634983e1a799cae44f2a6e172cf5bd5df2d3c3d75a6a" + }, + { + "path": "s4-driver-author-v6/tracked-inputs-before.json", + "bytes": 65289, + "sha256": "1f5750e6a0f37568c3176a2cc47f9a658f8e4225645e6ac33a1a059f049347b3", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/tracked-inputs-before.json", + "source_sha256": "1f5750e6a0f37568c3176a2cc47f9a658f8e4225645e6ac33a1a059f049347b3" + }, + { + "path": "s4-driver-author-v6/tracked-inputs-after.json", + "bytes": 65289, + "sha256": "1f5750e6a0f37568c3176a2cc47f9a658f8e4225645e6ac33a1a059f049347b3", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/tracked-inputs-after.json", + "source_sha256": "1f5750e6a0f37568c3176a2cc47f9a658f8e4225645e6ac33a1a059f049347b3" + }, + { + "path": "s4-driver-author-v6/owned-source-manifest.json", + "bytes": 523, + "sha256": "2c86a77db628977c05ca291571b56fc4754ab7da6511f5e4f94928e4785dab48", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/owned-source-manifest.json", + "source_sha256": "2c86a77db628977c05ca291571b56fc4754ab7da6511f5e4f94928e4785dab48" + }, + { + "path": "s4-driver-author-v6/binary-manifest.json", + "bytes": 268, + "sha256": "07c1ab0215ee1bc9389a45f9ac5f2d2b3f132c0ea4e64d5cf7e3f32929f5cfff", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/binary-manifest.json", + "source_sha256": "07c1ab0215ee1bc9389a45f9ac5f2d2b3f132c0ea4e64d5cf7e3f32929f5cfff" + }, + { + "path": "s4-driver-author-v6/executed-test-inventory.json", + "bytes": 7015, + "sha256": "785d7f335c65070e8bc682eb674eabc827c88481ddcfdb871300acc956645ae9", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/executed-test-inventory.json", + "source_sha256": "785d7f335c65070e8bc682eb674eabc827c88481ddcfdb871300acc956645ae9" + }, + { + "path": "s4-driver-author-v6/compile-test.log.gz", + "bytes": 4779, + "sha256": "bed7f34e3f293bb15dab844074869c65d89f7bee05a52049151a885f9f57a5c4", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/compile-test.log", + "source_sha256": "42b2181033cac5eca34e1d2eba7d41a9dde527aa9691f988c01cf6e295b7eaf4" + }, + { + "path": "s4-driver-author-v6/v5-counterexample-panic-payload-probe.log.gz", + "bytes": 519, + "sha256": "1d54125604851d6239ee7b794c754d5cbd4c5d451acc4e5c5a14ac2989ab9c0a", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/v5-counterexample-panic-payload-probe.log", + "source_sha256": "49a9d9720670828f73394d927b1916a32c4c01aff9db0dcc5a5555ced5e928a3" + }, + { + "path": "s4-driver-author-v6/v5-counterexample-panic-payload-probe.rs", + "bytes": 1460, + "sha256": "c78722ce984ec73d9cdd5518b45eadb29ebdc79f1d462caa84690dd7de932d17", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/v5-counterexample-panic-payload-probe.rs", + "source_sha256": "c78722ce984ec73d9cdd5518b45eadb29ebdc79f1d462caa84690dd7de932d17" + }, + { + "path": "s4-driver-author-v6/allocation-probe.rs", + "bytes": 4958, + "sha256": "64e2fae2f0565f774945cfd3085bb8628195ef9f5c0d2f3c21ac8cc89e86bbcb", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/allocation-probe.rs", + "source_sha256": "64e2fae2f0565f774945cfd3085bb8628195ef9f5c0d2f3c21ac8cc89e86bbcb" + }, + { + "path": "s4-driver-author-v6/allocation-probe.stdout", + "bytes": 244, + "sha256": "3aaf0f7053e2ac9e3df32011bdb24f269428c0492af8716a2db39afaa2e9df55", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/allocation-probe.stdout", + "source_sha256": "3aaf0f7053e2ac9e3df32011bdb24f269428c0492af8716a2db39afaa2e9df55" + }, + { + "path": "s4-driver-author-v6/allocation-binary-manifest.json", + "bytes": 269, + "sha256": "cf2e80fd53a20162e7f8a6f67526f5c069ab260c85dc89e981a69c6a1debb256", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/allocation-binary-manifest.json", + "source_sha256": "cf2e80fd53a20162e7f8a6f67526f5c069ab260c85dc89e981a69c6a1debb256" + }, + { + "path": "s4-driver-author-v6/junit.xml", + "bytes": 7303, + "sha256": "c94030080bd5add545cee2fa5fb6bbe0131e788e6f623b9af0eeb8d6e5fc0a11", + "source": "/private/tmp/cleanup-s4-driver-foundation-v6/nextest/cleanup-s4/junit.xml", + "source_sha256": "c94030080bd5add545cee2fa5fb6bbe0131e788e6f623b9af0eeb8d6e5fc0a11" + }, + { + "path": "s4-driver-review-v6/review.md", + "bytes": 7273, + "sha256": "c4bb6d0ad1fa24d5ab9641dcb928b526b001170aa2988fcb86e2abab57526e23", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/review.md", + "source_sha256": "c4bb6d0ad1fa24d5ab9641dcb928b526b001170aa2988fcb86e2abab57526e23" + }, + { + "path": "s4-driver-review-v6/evidence.json", + "bytes": 3187, + "sha256": "d586cfdaa736dbf9aa213beef8d731eb5b5f95ecfb2ee0e00aa7c1ae5b9dbaa5", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/evidence.json", + "source_sha256": "d586cfdaa736dbf9aa213beef8d731eb5b5f95ecfb2ee0e00aa7c1ae5b9dbaa5" + }, + { + "path": "s4-driver-review-v6/context.json", + "bytes": 1122, + "sha256": "3efc929a56dbd3402fd53321313069e566129af3feb93b978003617eec15b64b", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/context.json", + "source_sha256": "3efc929a56dbd3402fd53321313069e566129af3feb93b978003617eec15b64b" + }, + { + "path": "s4-driver-review-v6/source-before.json", + "bytes": 65289, + "sha256": "1f5750e6a0f37568c3176a2cc47f9a658f8e4225645e6ac33a1a059f049347b3", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/source-before.json", + "source_sha256": "1f5750e6a0f37568c3176a2cc47f9a658f8e4225645e6ac33a1a059f049347b3" + }, + { + "path": "s4-driver-review-v6/source-after.json", + "bytes": 65289, + "sha256": "1f5750e6a0f37568c3176a2cc47f9a658f8e4225645e6ac33a1a059f049347b3", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/source-after.json", + "source_sha256": "1f5750e6a0f37568c3176a2cc47f9a658f8e4225645e6ac33a1a059f049347b3" + }, + { + "path": "s4-driver-review-v6/focused-module-suite.log.gz", + "bytes": 1426, + "sha256": "c7763fb0deb46684ef0a3bac925dba5aab04dc79c04c0c7e0125636bed6d2f68", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/focused-module-suite.log", + "source_sha256": "c4b4c09f32f2e9d89ef26f2b560a41f13502d7acd51e3d377a82c8f087ed170d" + }, + { + "path": "s4-driver-review-v6/focused-compile.log.gz", + "bytes": 334, + "sha256": "d7ed561dcce9e5350f1cb445d73abdebabb7736a1dafac7cbf1675caa34da92e", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/focused-compile.log", + "source_sha256": "53bd7edf1101034f7932f1a4ec80bea37ae1678803fe1c29602531f5ebead280" + }, + { + "path": "s4-driver-review-v6/independent-allocation-probe.log.gz", + "bytes": 162, + "sha256": "bf95c9f9b5e157e744a151da49557d732e84f2162ced54b79b49e8b7f0d504ae", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/independent-allocation-probe.log", + "source_sha256": "c5615c623083adcad33e5768bb92432428576390d7c02b1dd8d7e188123cb6b7" + }, + { + "path": "s4-driver-review-v6/independent-allocation-probe.rs", + "bytes": 4727, + "sha256": "9a9e27d364deed8b89bc40d37e39b15317e5d4658a28f2edfa40e262e51b2ef5", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/independent-allocation-probe.rs", + "source_sha256": "9a9e27d364deed8b89bc40d37e39b15317e5d4658a28f2edfa40e262e51b2ef5" + }, + { + "path": "s4-driver-review-v6/probe-root.rs", + "bytes": 118, + "sha256": "1fde76dd5b3571b6c67be75e517a62f1f15f84097908ab4b6cf2193191c9e1fe", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/probe-root.rs", + "source_sha256": "1fde76dd5b3571b6c67be75e517a62f1f15f84097908ab4b6cf2193191c9e1fe" + }, + { + "path": "s4-driver-review-v6/maintenance_scheduler.rs", + "bytes": 53340, + "sha256": "be1f9d87f6c4a8c1eab47ae0dd394cae68ccb31b8f0b68d0ab1efed2b2c1688d", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/maintenance_scheduler.rs", + "source_sha256": "be1f9d87f6c4a8c1eab47ae0dd394cae68ccb31b8f0b68d0ab1efed2b2c1688d" + }, + { + "path": "s4-driver-review-v6/maintenance_budget.rs", + "bytes": 12186, + "sha256": "3288f29d3133f7f403afcf275d35e8c55fb53cc487b0251b245070e19daa27e0", + "source": "/private/tmp/cleanup-s4-v6-final-review-q89atunk/maintenance_budget.rs", + "source_sha256": "3288f29d3133f7f403afcf275d35e8c55fb53cc487b0251b245070e19daa27e0" + }, + { + "path": "s5-harness-review/report.md", + "bytes": 8383, + "sha256": "47a4b5c530e5b73ef82a92e98571170fd4871794ecc3f98f87a163131b867875", + "source": "/private/tmp/s5-successor-review-49ju6cv3/report.md", + "source_sha256": "47a4b5c530e5b73ef82a92e98571170fd4871794ecc3f98f87a163131b867875" + }, + { + "path": "s5-harness-review/report.json", + "bytes": 6273, + "sha256": "b9a843b7ffa3b93de5054438409d6e2f98100a4853d5368f27e50961dad1e32d", + "source": "/private/tmp/s5-successor-review-49ju6cv3/report.json", + "source_sha256": "b9a843b7ffa3b93de5054438409d6e2f98100a4853d5368f27e50961dad1e32d" + }, + { + "path": "s5-harness-review/reviewer-checks-junit.xml", + "bytes": 3425, + "sha256": "840d3a3cdd98f3b0799f5e4f0a5acc3a0d91666e378a2355ae76ad9c549908ee", + "source": "/private/tmp/s5-successor-review-49ju6cv3/reviewer-checks-junit.xml", + "source_sha256": "840d3a3cdd98f3b0799f5e4f0a5acc3a0d91666e378a2355ae76ad9c549908ee" + }, + { + "path": "s5-harness-review/reviewer-checks-result.json", + "bytes": 84, + "sha256": "25ead99938593de9d2f3e91bd727f719559285eff62ae7aecbff7ef9cb233fdb", + "source": "/private/tmp/s5-successor-review-49ju6cv3/reviewer-checks-result.json", + "source_sha256": "25ead99938593de9d2f3e91bd727f719559285eff62ae7aecbff7ef9cb233fdb" + }, + { + "path": "s5-harness-review/author-counterexample-archive-check.json", + "bytes": 858, + "sha256": "9a56ccd97b4e8aa51e2b44fbc5b9cc4609dc14a7ccc4084f904b3f5d7f5baab7", + "source": "/private/tmp/s5-successor-review-49ju6cv3/author-counterexample-archive-check.json", + "source_sha256": "9a56ccd97b4e8aa51e2b44fbc5b9cc4609dc14a7ccc4084f904b3f5d7f5baab7" + }, + { + "path": "s5-harness-review/provided-archive-check.json", + "bytes": 376, + "sha256": "bc9e54060ee0bed1d39195dee5b58957ab0056e56d36adc1105a8dc471c0390c", + "source": "/private/tmp/s5-successor-review-49ju6cv3/provided-archive-check.json", + "source_sha256": "bc9e54060ee0bed1d39195dee5b58957ab0056e56d36adc1105a8dc471c0390c" + }, + { + "path": "s5-harness-review/original-inverse-results.json", + "bytes": 971, + "sha256": "d48692e73adc9e6adbfc31b141aa6b9810da77db31e773b4147760c5280a0ae6", + "source": "/private/tmp/s5-successor-review-49ju6cv3/original-inverse-results.json", + "source_sha256": "d48692e73adc9e6adbfc31b141aa6b9810da77db31e773b4147760c5280a0ae6" + }, + { + "path": "s5-harness-review/identity-before.json", + "bytes": 770, + "sha256": "cce727010833b10275ec64aea91e25ca7482aa4c925fc13c8035dfe4f8c6606c", + "source": "/private/tmp/s5-successor-review-49ju6cv3/identity-before.json", + "source_sha256": "cce727010833b10275ec64aea91e25ca7482aa4c925fc13c8035dfe4f8c6606c" + }, + { + "path": "s5-harness-review/identity-after.json", + "bytes": 701, + "sha256": "1c07d87b41400fff060bda1690f435bc78b7a74189d45f6e40a1bfd198605b6e", + "source": "/private/tmp/s5-successor-review-49ju6cv3/identity-after.json", + "source_sha256": "1c07d87b41400fff060bda1690f435bc78b7a74189d45f6e40a1bfd198605b6e" + }, + { + "path": "s5-harness-review/checks.stderr", + "bytes": 3158, + "sha256": "637147d4a49f7f83e39bfa76a3ed3456a5d19ab7f7a7a6be216a0154e93a9f78", + "source": "/private/tmp/s5-successor-review-49ju6cv3/checks.stderr", + "source_sha256": "637147d4a49f7f83e39bfa76a3ed3456a5d19ab7f7a7a6be216a0154e93a9f78" + }, + { + "path": "s5-harness-review/audit.stderr", + "bytes": 109, + "sha256": "bee3fde7d0657a2000f334596c5a6dca29ab48a7c4e55430ec61157ec8a5e2cf", + "source": "/private/tmp/s5-successor-review-49ju6cv3/audit.stderr", + "source_sha256": "bee3fde7d0657a2000f334596c5a6dca29ab48a7c4e55430ec61157ec8a5e2cf" + }, + { + "path": "s5-harness-review/run_successor_checks.py", + "bytes": 2099, + "sha256": "5de88dee0b2d065b9b3cef94ab8072c096ddf3192926d2ad96dd86a178d19673", + "source": "/private/tmp/s5-successor-review-49ju6cv3/run_successor_checks.py", + "source_sha256": "5de88dee0b2d065b9b3cef94ab8072c096ddf3192926d2ad96dd86a178d19673" + }, + { + "path": "s5-harness-review/reverse_original_probes.py", + "bytes": 3760, + "sha256": "e28858b97435aa04927c6fbda0eba5a35b694a32a94b088e56e49b71b1bffb17", + "source": "/private/tmp/s5-successor-review-49ju6cv3/reverse_original_probes.py", + "source_sha256": "e28858b97435aa04927c6fbda0eba5a35b694a32a94b088e56e49b71b1bffb17" + }, + { + "path": "proposal-v2/s2_v3_revision_review.md", + "bytes": 4415, + "sha256": "50f28ba27c49a232236ab8c906ff504653d29fec7b361bad53ea627f8a372051", + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s2_v3_revision_review.md", + "source_sha256": "50f28ba27c49a232236ab8c906ff504653d29fec7b361bad53ea627f8a372051" + }, + { + "path": "proposal-v2/s3_producer_effects_v2_review.md", + "bytes": 8354, + "sha256": "cec734298bb4beef5bf49def687024211017cfc22051983de0329f9329330f29", + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s3_producer_effects_v2_review.md", + "source_sha256": "cec734298bb4beef5bf49def687024211017cfc22051983de0329f9329330f29" + }, + { + "path": "proposal-v2/s4_refresh_ownership_review.md", + "bytes": 4115, + "sha256": "0ceeee5108925207950a98dcab5be934b225fe8512320c01b6911dae436e2886", + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s4_refresh_ownership_review.md", + "source_sha256": "0ceeee5108925207950a98dcab5be934b225fe8512320c01b6911dae436e2886" + }, + { + "path": "proposal-v2/s3_serializer_qualification.md", + "bytes": 2850, + "sha256": "9e21d3ebf4a762170f999a2f0b883a863a398ce22d55e056bae3c5420ca83258", + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s3_serializer_qualification.md", + "source_sha256": "9e21d3ebf4a762170f999a2f0b883a863a398ce22d55e056bae3c5420ca83258" + }, + { + "path": "proposal-v2/s3_origin_only_gc_qualification.md", + "bytes": 5653, + "sha256": "0f482f1c9ab80870f526e93acc09560e647812a6c5acdd8591fb19eb33748f6c", + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s3_origin_only_gc_qualification.md", + "source_sha256": "0f482f1c9ab80870f526e93acc09560e647812a6c5acdd8591fb19eb33748f6c" + }, + { + "path": "proposal-v2/s2-authority-v3.md", + "bytes": 41900, + "sha256": "88f647d10406eb44646f38430b3098cb7db6c1df07c3d99f5563866ef5c2478e", + "source": "/private/tmp/dc-s2-bounded-authority-selected-mechanism-20261021-v3.md", + "source_sha256": "88f647d10406eb44646f38430b3098cb7db6c1df07c3d99f5563866ef5c2478e" + }, + { + "path": "proposal-v2/s3-effects-v2.md", + "bytes": 17855, + "sha256": "44391219454b4265529f8d766f40603f9aee71a2b34548d6449549c92a4d4231", + "source": "/private/tmp/dc-s3-producer-leaf-effects-gate-20261021-v2.md", + "source_sha256": "44391219454b4265529f8d766f40603f9aee71a2b34548d6449549c92a4d4231" + }, + { + "path": "proposal-v2/s4-refresh-ownership.md", + "bytes": 11206, + "sha256": "5ea3b3280a0282262c4a1dce30ea4658ebc01f8aba134d79bff7d27be64c07fe", + "source": "/private/tmp/dc-s4-refresh-owned-join-proposal-20261021.md", + "source_sha256": "5ea3b3280a0282262c4a1dce30ea4658ebc01f8aba134d79bff7d27be64c07fe" + }, + { + "path": "s4-driver-history/v2/tracked-inputs-after.json", + "bytes": 65289, + "sha256": "7c35fafde431c3938222970df1f0967b1d91b84e1a907bdd55017b8ef40f1b1c", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/tracked-inputs-after.json", + "source_sha256": "7c35fafde431c3938222970df1f0967b1d91b84e1a907bdd55017b8ef40f1b1c" + }, + { + "path": "s4-driver-history/v2/junit-listener-blocked.xml", + "bytes": 1885, + "sha256": "5dbd99c7f5c75eafa300fcefa6a213e6ef627c1bdfe93090c7e280aaf510ffa9", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/junit-listener-blocked.xml", + "source_sha256": "5dbd99c7f5c75eafa300fcefa6a213e6ef627c1bdfe93090c7e280aaf510ffa9" + }, + { + "path": "s4-driver-history/v2/owned-source-manifest.json", + "bytes": 523, + "sha256": "c6dd78f89a0265134cf468a24025c969e32b0377039a4c18360e555bfb465aa9", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/owned-source-manifest.json", + "source_sha256": "c6dd78f89a0265134cf468a24025c969e32b0377039a4c18360e555bfb465aa9" + }, + { + "path": "s4-driver-history/v2/tracked-inputs-before.json", + "bytes": 65289, + "sha256": "7c35fafde431c3938222970df1f0967b1d91b84e1a907bdd55017b8ef40f1b1c", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/tracked-inputs-before.json", + "source_sha256": "7c35fafde431c3938222970df1f0967b1d91b84e1a907bdd55017b8ef40f1b1c" + }, + { + "path": "s4-driver-history/v2/compile-test.log.gz", + "bytes": 3119, + "sha256": "d2fab94c540ac1a338cede4316c2895b79ad0d36adac25a37dc3e51b5edd580f", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/compile-test.log", + "source_sha256": "26d2473bc008378aa249fb8cd72b34560e6dcddbd9499bd4fb7bc2125a13e9c5" + }, + { + "path": "s4-driver-history/v2/binary-manifest.json", + "bytes": 272, + "sha256": "659fd3f21f16a50ff1b45ddc3df81fdbd2b18e04040a761f5e5d0b87b6600c0e", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/binary-manifest.json", + "source_sha256": "659fd3f21f16a50ff1b45ddc3df81fdbd2b18e04040a761f5e5d0b87b6600c0e" + }, + { + "path": "s4-driver-history/v2/loopback-rerun.log.gz", + "bytes": 2296, + "sha256": "28dd0fbebc83d9cdd5f3ccd70d22faf0f7eb72b7e896424ca2aef28885a44ceb", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/loopback-rerun.log", + "source_sha256": "7ce48fbdb8215a9249eb73c6af982beb256351a9979ccd16569e9da55445c256" + }, + { + "path": "s4-driver-history/v2/snapshot/src/coordinator.rs", + "bytes": 50550, + "sha256": "93ae677a8d367fb1f99619df8a50c683100aeeba332aec8b0c58c9c21b26c60a", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/snapshot/src/coordinator.rs", + "source_sha256": "93ae677a8d367fb1f99619df8a50c683100aeeba332aec8b0c58c9c21b26c60a" + }, + { + "path": "s4-driver-history/v2/snapshot/src/coordinator/tests.rs", + "bytes": 36099, + "sha256": "52c9ca2587024604326eea07f291af6424b2e4222a8d88649fed205519238abf", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/snapshot/src/coordinator/tests.rs", + "source_sha256": "52c9ca2587024604326eea07f291af6424b2e4222a8d88649fed205519238abf" + }, + { + "path": "s4-driver-history/v2/snapshot/src/storage/mod.rs", + "bytes": 371, + "sha256": "aebd93713ac777348a5db89033f4e4a286cd0e881a47988a017a3e5159382325", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/snapshot/src/storage/mod.rs", + "source_sha256": "aebd93713ac777348a5db89033f4e4a286cd0e881a47988a017a3e5159382325" + }, + { + "path": "s4-driver-history/v2/snapshot/src/storage/maintenance_scheduler.rs", + "bytes": 26049, + "sha256": "4b4eae3b1477cd5b2c91f9244eaf1aba0ab7f85e9d8d9023f99e608d8c9fe479", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/snapshot/src/storage/maintenance_scheduler.rs", + "source_sha256": "4b4eae3b1477cd5b2c91f9244eaf1aba0ab7f85e9d8d9023f99e608d8c9fe479" + }, + { + "path": "s4-driver-history/v2/nextest/cleanup-s4/junit.xml", + "bytes": 7009, + "sha256": "223b78dea69536c403869dfe20308f418843967c814acd1b64a82db7a7e643fb", + "source": "/private/tmp/cleanup-s4-driver-foundation-v2/nextest/cleanup-s4/junit.xml", + "source_sha256": "223b78dea69536c403869dfe20308f418843967c814acd1b64a82db7a7e643fb" + }, + { + "path": "s4-driver-history/v3/tracked-inputs-after.json", + "bytes": 65289, + "sha256": "2bb0cb2b779dacc5c64cebafb0c692402518929278e4903033e51c7f8cc6f070", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/tracked-inputs-after.json", + "source_sha256": "2bb0cb2b779dacc5c64cebafb0c692402518929278e4903033e51c7f8cc6f070" + }, + { + "path": "s4-driver-history/v3/evidence.json", + "bytes": 1352, + "sha256": "94a9ff19cd5fb868e221560d9beceab96fc08c1591f1ca84ee3946a349703334", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/evidence.json", + "source_sha256": "94a9ff19cd5fb868e221560d9beceab96fc08c1591f1ca84ee3946a349703334" + }, + { + "path": "s4-driver-history/v3/owned-source-manifest.json", + "bytes": 523, + "sha256": "b6a04e660cc40148987dd6da532ce96f74739839d22f4e0b0b804ffc1da1dc7f", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/owned-source-manifest.json", + "source_sha256": "b6a04e660cc40148987dd6da532ce96f74739839d22f4e0b0b804ffc1da1dc7f" + }, + { + "path": "s4-driver-history/v3/tracked-inputs-before.json", + "bytes": 65289, + "sha256": "2bb0cb2b779dacc5c64cebafb0c692402518929278e4903033e51c7f8cc6f070", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/tracked-inputs-before.json", + "source_sha256": "2bb0cb2b779dacc5c64cebafb0c692402518929278e4903033e51c7f8cc6f070" + }, + { + "path": "s4-driver-history/v3/compile-test.log.gz", + "bytes": 3422, + "sha256": "c4dcc6a4cb385a0b513e1b6dad7e4304f7d09dadf610fab6697d71e18522796b", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/compile-test.log", + "source_sha256": "3967e3f5cf7ad6a86661954ec31a365860d77f98e82bbcf17379b3230c442920" + }, + { + "path": "s4-driver-history/v3/binary-manifest.json", + "bytes": 329, + "sha256": "8593d7471199cc75082fe96c9c82261e8bec2e9d405e507a156e4041d83600c5", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/binary-manifest.json", + "source_sha256": "8593d7471199cc75082fe96c9c82261e8bec2e9d405e507a156e4041d83600c5" + }, + { + "path": "s4-driver-history/v3/executed-test-inventory.json", + "bytes": 5240, + "sha256": "7aa96dfd1ceeabbdaa13fc5094cbadae3ada14da3c02dae00f4d063d86cfd9a4", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/executed-test-inventory.json", + "source_sha256": "7aa96dfd1ceeabbdaa13fc5094cbadae3ada14da3c02dae00f4d063d86cfd9a4" + }, + { + "path": "s4-driver-history/v3/snapshot/src/coordinator.rs", + "bytes": 50550, + "sha256": "93ae677a8d367fb1f99619df8a50c683100aeeba332aec8b0c58c9c21b26c60a", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/snapshot/src/coordinator.rs", + "source_sha256": "93ae677a8d367fb1f99619df8a50c683100aeeba332aec8b0c58c9c21b26c60a" + }, + { + "path": "s4-driver-history/v3/snapshot/src/coordinator/tests.rs", + "bytes": 36099, + "sha256": "52c9ca2587024604326eea07f291af6424b2e4222a8d88649fed205519238abf", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/snapshot/src/coordinator/tests.rs", + "source_sha256": "52c9ca2587024604326eea07f291af6424b2e4222a8d88649fed205519238abf" + }, + { + "path": "s4-driver-history/v3/snapshot/src/storage/mod.rs", + "bytes": 371, + "sha256": "aebd93713ac777348a5db89033f4e4a286cd0e881a47988a017a3e5159382325", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/snapshot/src/storage/mod.rs", + "source_sha256": "aebd93713ac777348a5db89033f4e4a286cd0e881a47988a017a3e5159382325" + }, + { + "path": "s4-driver-history/v3/snapshot/src/storage/maintenance_scheduler.rs", + "bytes": 26050, + "sha256": "ac2973d2f93ad6563af2e0b3f6229a3d2ad7ec91e1a9c5f914c146444cedfc28", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/snapshot/src/storage/maintenance_scheduler.rs", + "source_sha256": "ac2973d2f93ad6563af2e0b3f6229a3d2ad7ec91e1a9c5f914c146444cedfc28" + }, + { + "path": "s4-driver-history/v3/nextest/cleanup-s4/junit.xml", + "bytes": 5535, + "sha256": "387234b1e12854e28b02135df32763a87a2657e5a3057bfca5b7951e8ea005be", + "source": "/private/tmp/cleanup-s4-driver-foundation-v3/nextest/cleanup-s4/junit.xml", + "source_sha256": "387234b1e12854e28b02135df32763a87a2657e5a3057bfca5b7951e8ea005be" + }, + { + "path": "s4-driver-history/v4/tracked-inputs-after.json", + "bytes": 65289, + "sha256": "4c2af01067419324c7f9326720588ac9a25ce0acf2f991b098fdc26470af17eb", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/tracked-inputs-after.json", + "source_sha256": "4c2af01067419324c7f9326720588ac9a25ce0acf2f991b098fdc26470af17eb" + }, + { + "path": "s4-driver-history/v4/evidence.json", + "bytes": 633, + "sha256": "f93c0e11caaa281e26eaf95b44fd200b038a814872a0ec0216f19d810ab3c029", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/evidence.json", + "source_sha256": "f93c0e11caaa281e26eaf95b44fd200b038a814872a0ec0216f19d810ab3c029" + }, + { + "path": "s4-driver-history/v4/allocation-probe.rs", + "bytes": 4958, + "sha256": "469d17b50a8b739c333bd5bbba917d136327db8260d79f7b203ea4e08d6a6431", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/allocation-probe.rs", + "source_sha256": "469d17b50a8b739c333bd5bbba917d136327db8260d79f7b203ea4e08d6a6431" + }, + { + "path": "s4-driver-history/v4/owned-source-manifest.json", + "bytes": 523, + "sha256": "9bf99e59128cd686a51fa5c1f3196b0701f93936f7deccbb8079a5e952f9971f", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/owned-source-manifest.json", + "source_sha256": "9bf99e59128cd686a51fa5c1f3196b0701f93936f7deccbb8079a5e952f9971f" + }, + { + "path": "s4-driver-history/v4/tracked-inputs-before.json", + "bytes": 65289, + "sha256": "4c2af01067419324c7f9326720588ac9a25ce0acf2f991b098fdc26470af17eb", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/tracked-inputs-before.json", + "source_sha256": "4c2af01067419324c7f9326720588ac9a25ce0acf2f991b098fdc26470af17eb" + }, + { + "path": "s4-driver-history/v4/compile-test.log.gz", + "bytes": 4665, + "sha256": "65a53bbe2195f5165e51d7bfe58872fdfe361d627b8730362622f8ad4805f7ec", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/compile-test.log", + "source_sha256": "01dbee987642238a9b82cec0167bd8bf60485f664d81e28545bb063c7436281a" + }, + { + "path": "s4-driver-history/v4/allocation-probe.stdout", + "bytes": 121, + "sha256": "ffdb7646c636b4a2f6a67a5d2a3d7da25b28b495fe832f50e11f561050c2b23d", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/allocation-probe.stdout", + "source_sha256": "ffdb7646c636b4a2f6a67a5d2a3d7da25b28b495fe832f50e11f561050c2b23d" + }, + { + "path": "s4-driver-history/v4/binary-manifest.json", + "bytes": 268, + "sha256": "3b1e016b0bffa659e1bfe98e1ee8b40d5f7691551b974b30d40301d597b6af05", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/binary-manifest.json", + "source_sha256": "3b1e016b0bffa659e1bfe98e1ee8b40d5f7691551b974b30d40301d597b6af05" + }, + { + "path": "s4-driver-history/v4/allocation-compile.log.gz", + "bytes": 1397, + "sha256": "4c1a28c490f1d96bb6ca11f56318f3e46026592c68758e2f47524172ec038196", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/allocation-compile.log", + "source_sha256": "2a78db78e7af5866c746a179278a5af283087b49bee5245a53724c4a76eebe62" + }, + { + "path": "s4-driver-history/v4/allocation-probe.stderr", + "bytes": 102, + "sha256": "b8882ace057368ca5a64066f9ecde8358677850750313d9c08855dbbfb22768a", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/allocation-probe.stderr", + "source_sha256": "b8882ace057368ca5a64066f9ecde8358677850750313d9c08855dbbfb22768a" + }, + { + "path": "s4-driver-history/v4/snapshot/src/coordinator.rs", + "bytes": 50550, + "sha256": "93ae677a8d367fb1f99619df8a50c683100aeeba332aec8b0c58c9c21b26c60a", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/snapshot/src/coordinator.rs", + "source_sha256": "93ae677a8d367fb1f99619df8a50c683100aeeba332aec8b0c58c9c21b26c60a" + }, + { + "path": "s4-driver-history/v4/snapshot/src/coordinator/tests.rs", + "bytes": 36099, + "sha256": "52c9ca2587024604326eea07f291af6424b2e4222a8d88649fed205519238abf", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/snapshot/src/coordinator/tests.rs", + "source_sha256": "52c9ca2587024604326eea07f291af6424b2e4222a8d88649fed205519238abf" + }, + { + "path": "s4-driver-history/v4/snapshot/src/storage/mod.rs", + "bytes": 371, + "sha256": "aebd93713ac777348a5db89033f4e4a286cd0e881a47988a017a3e5159382325", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/snapshot/src/storage/mod.rs", + "source_sha256": "aebd93713ac777348a5db89033f4e4a286cd0e881a47988a017a3e5159382325" + }, + { + "path": "s4-driver-history/v4/snapshot/src/storage/maintenance_scheduler.rs", + "bytes": 44469, + "sha256": "3ee7b9f16a2d1bf367b8569f5658cd2fb76e5e34cad267b1de8298d41508383d", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/snapshot/src/storage/maintenance_scheduler.rs", + "source_sha256": "3ee7b9f16a2d1bf367b8569f5658cd2fb76e5e34cad267b1de8298d41508383d" + }, + { + "path": "s4-driver-history/v4/nextest/cleanup-s4/junit.xml", + "bytes": 8693, + "sha256": "3c5c758a5c8b933a41ae6de44838966bafda25f5c89ee9c11b3196843ca89efc", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/nextest/cleanup-s4/junit.xml", + "source_sha256": "3c5c758a5c8b933a41ae6de44838966bafda25f5c89ee9c11b3196843ca89efc" + }, + { + "path": "s4-driver-history/v4/baseline/src/coordinator.rs", + "bytes": 50064, + "sha256": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/baseline/src/coordinator.rs", + "source_sha256": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e" + }, + { + "path": "s4-driver-history/v4/baseline/src/coordinator/tests.rs", + "bytes": 23716, + "sha256": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/baseline/src/coordinator/tests.rs", + "source_sha256": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252" + }, + { + "path": "s4-driver-history/v4/baseline/src/storage/mod.rs", + "bytes": 333, + "sha256": "9ac2905364228f9376c69fbc6347fe84b043b5ca6e63e27046bb5eeedaac6081", + "source": "/private/tmp/cleanup-s4-driver-foundation-v4/baseline/src/storage/mod.rs", + "source_sha256": "9ac2905364228f9376c69fbc6347fe84b043b5ca6e63e27046bb5eeedaac6081" + }, + { + "path": "s4-driver-history/v5/tracked-inputs-after.json", + "bytes": 65289, + "sha256": "762c86917ce86c00349b866e8de0af85e924f07126fafd88464a5e80b23504b4", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/tracked-inputs-after.json", + "source_sha256": "762c86917ce86c00349b866e8de0af85e924f07126fafd88464a5e80b23504b4" + }, + { + "path": "s4-driver-history/v5/evidence.json", + "bytes": 1772, + "sha256": "975c18e772407a993070fb09b9355fa587bad8205facbde9557c651775ba5950", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/evidence.json", + "source_sha256": "975c18e772407a993070fb09b9355fa587bad8205facbde9557c651775ba5950" + }, + { + "path": "s4-driver-history/v5/allocation-probe.rs", + "bytes": 4958, + "sha256": "6dcc256579a42e283b66aad97395329118ea8a9d2a0f1264d9c880b97a883d6f", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/allocation-probe.rs", + "source_sha256": "6dcc256579a42e283b66aad97395329118ea8a9d2a0f1264d9c880b97a883d6f" + }, + { + "path": "s4-driver-history/v5/owned-source-manifest.json", + "bytes": 523, + "sha256": "6b58b8f625b62225a57a3d6bf3ac0a02849f8a342915b7ab09068a374bd32bb0", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/owned-source-manifest.json", + "source_sha256": "6b58b8f625b62225a57a3d6bf3ac0a02849f8a342915b7ab09068a374bd32bb0" + }, + { + "path": "s4-driver-history/v5/tracked-inputs-before.json", + "bytes": 65289, + "sha256": "762c86917ce86c00349b866e8de0af85e924f07126fafd88464a5e80b23504b4", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/tracked-inputs-before.json", + "source_sha256": "762c86917ce86c00349b866e8de0af85e924f07126fafd88464a5e80b23504b4" + }, + { + "path": "s4-driver-history/v5/compile-test.log.gz", + "bytes": 4683, + "sha256": "41b53d5bb4fce748ce3f4e529ebf95201a8c4032a595ea2fd041eac3a138e36b", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/compile-test.log", + "source_sha256": "5340a335dc82f7b103dd190585d4b042ca8989e2e37f8041c551f554636085a6" + }, + { + "path": "s4-driver-history/v5/allocation-binary-manifest.json", + "bytes": 269, + "sha256": "37f380cab2b01d779bb21d1e0f60f2ebf26de23577036d8d971ad1f4bb50952c", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/allocation-binary-manifest.json", + "source_sha256": "37f380cab2b01d779bb21d1e0f60f2ebf26de23577036d8d971ad1f4bb50952c" + }, + { + "path": "s4-driver-history/v5/allocation-probe.stdout", + "bytes": 244, + "sha256": "3aaf0f7053e2ac9e3df32011bdb24f269428c0492af8716a2db39afaa2e9df55", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/allocation-probe.stdout", + "source_sha256": "3aaf0f7053e2ac9e3df32011bdb24f269428c0492af8716a2db39afaa2e9df55" + }, + { + "path": "s4-driver-history/v5/binary-manifest.json", + "bytes": 268, + "sha256": "4e6f6df3efd379d03a7daece5c91aac6b2c02bffd22b1538b844b9f0e10c9de9", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/binary-manifest.json", + "source_sha256": "4e6f6df3efd379d03a7daece5c91aac6b2c02bffd22b1538b844b9f0e10c9de9" + }, + { + "path": "s4-driver-history/v5/executed-test-inventory.json", + "bytes": 6768, + "sha256": "e86be8f88629b2387bc74fd91075e1338c5e6b70782713eb35e2a55d533a72d1", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/executed-test-inventory.json", + "source_sha256": "e86be8f88629b2387bc74fd91075e1338c5e6b70782713eb35e2a55d533a72d1" + }, + { + "path": "s4-driver-history/v5/allocation-compile.log.gz", + "bytes": 1398, + "sha256": "2fd557e9f34362285761f3269ad4c827c13bf4d73860ea2fd8d82cc8eb24b793", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/allocation-compile.log", + "source_sha256": "39558b8487312f3d5a067fb93c2f8a336a3d99ab55bb50ece2d5b9babeea0172" + }, + { + "path": "s4-driver-history/v5/allocation-probe.stderr", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/allocation-probe.stderr", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-driver-history/v5/snapshot/src/coordinator.rs", + "bytes": 50550, + "sha256": "93ae677a8d367fb1f99619df8a50c683100aeeba332aec8b0c58c9c21b26c60a", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/snapshot/src/coordinator.rs", + "source_sha256": "93ae677a8d367fb1f99619df8a50c683100aeeba332aec8b0c58c9c21b26c60a" + }, + { + "path": "s4-driver-history/v5/snapshot/src/coordinator/tests.rs", + "bytes": 36099, + "sha256": "52c9ca2587024604326eea07f291af6424b2e4222a8d88649fed205519238abf", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/snapshot/src/coordinator/tests.rs", + "source_sha256": "52c9ca2587024604326eea07f291af6424b2e4222a8d88649fed205519238abf" + }, + { + "path": "s4-driver-history/v5/snapshot/src/storage/mod.rs", + "bytes": 371, + "sha256": "aebd93713ac777348a5db89033f4e4a286cd0e881a47988a017a3e5159382325", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/snapshot/src/storage/mod.rs", + "source_sha256": "aebd93713ac777348a5db89033f4e4a286cd0e881a47988a017a3e5159382325" + }, + { + "path": "s4-driver-history/v5/snapshot/src/storage/maintenance_scheduler.rs", + "bytes": 44633, + "sha256": "ba921a6aac8847b83084f8adb2e03c53171bf3077d8bc9ee24afb077b994cde0", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/snapshot/src/storage/maintenance_scheduler.rs", + "source_sha256": "ba921a6aac8847b83084f8adb2e03c53171bf3077d8bc9ee24afb077b994cde0" + }, + { + "path": "s4-driver-history/v5/nextest/cleanup-s4/junit.xml", + "bytes": 7057, + "sha256": "fd1b7c083e8e07ca89e008db0cd6766ec2b6078ac568e38d3fe160d992047272", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/nextest/cleanup-s4/junit.xml", + "source_sha256": "fd1b7c083e8e07ca89e008db0cd6766ec2b6078ac568e38d3fe160d992047272" + }, + { + "path": "s4-driver-history/v5/baseline/src/coordinator.rs", + "bytes": 50064, + "sha256": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/baseline/src/coordinator.rs", + "source_sha256": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e" + }, + { + "path": "s4-driver-history/v5/baseline/src/coordinator/tests.rs", + "bytes": 23716, + "sha256": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/baseline/src/coordinator/tests.rs", + "source_sha256": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252" + }, + { + "path": "s4-driver-history/v5/baseline/src/storage/mod.rs", + "bytes": 333, + "sha256": "9ac2905364228f9376c69fbc6347fe84b043b5ca6e63e27046bb5eeedaac6081", + "source": "/private/tmp/cleanup-s4-driver-foundation-v5/baseline/src/storage/mod.rs", + "source_sha256": "9ac2905364228f9376c69fbc6347fe84b043b5ca6e63e27046bb5eeedaac6081" + } + ] +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/reviewed-foundations-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/reviewed-foundations-manifest.json new file mode 100644 index 00000000..54f4b301 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/reviewed-foundations-manifest.json @@ -0,0 +1,141 @@ +{ + "scope": "Accepted v0.2 wrapper and facts foundations; no full cleanup/platform qualification", + "code_commits": { + "facts": "9836234", + "wrapper": "7b0ad57ba26152c9fe19a9145a7c83c363f87b23" + }, + "files": { + "s2-wrapper-author-evidence.json": { + "sha256": "4d9d466f31c02de409fa03c915e3246f9c7dfd08ad53c2a0f697b7324f7fa401", + "bytes": 4323 + }, + "s2-wrapper-author-parallel-junit.xml": { + "sha256": "0545f63aa2d69444b6e7d93a38b1f05aaf970d341b06904a10631a16eaced580", + "bytes": 6783 + }, + "s2-wrapper-author-parallel.log.gz": { + "sha256": "26f027ec684a0e7f7b9ed534ec7de26f8e00db6b7665405e954fcf78a6210d33", + "bytes": 1355 + }, + "s2-wrapper-author-serial-junit.xml": { + "sha256": "379e8d170ad21d976015991470bb8ed894232b091876f84e31226b128345bfb7", + "bytes": 6783 + }, + "s2-wrapper-review-binaries.json": { + "sha256": "0269a6b5dd9910ecf56beb76f83fc07c1a73c7e40e41bb49b1fdd2d40dfb8af5", + "bytes": 423 + }, + "s2-wrapper-review-build.log.gz": { + "sha256": "fc0ad3ac1c32db088edbc60e8048978e0e0f4cf15cc5836b4477ce1ebf20fac3", + "bytes": 188 + }, + "s2-wrapper-review-doctests.log.gz": { + "sha256": "c10d346c06f1b0dce6ed6dcd0ecddb678f9a4e829b5e92ce5f7ed61dc8e7218f", + "bytes": 3494 + }, + "s2-wrapper-review-evidence.json": { + "sha256": "b86a47a9a5d001abe3fe00a03b66df99b76f2223257665fd79b3abb246570b64", + "bytes": 11175 + }, + "s2-wrapper-review-junit.xml": { + "sha256": "b539f8fa4d2cc86f107177ed8951a5858797c34e70bb17e43dcf260c1b4a6fee", + "bytes": 6784 + }, + "s2-wrapper-review-run.log.gz": { + "sha256": "761a485904342ea52248c623fa487a71d7aa11b83887e33348cfac3c49088496", + "bytes": 1312 + }, + "s2-wrapper-review-snapshot.json": { + "sha256": "1be27a626cc5300c2d9026e27e4071f216ca8b4bc09516f3ec4e2623cc88d77c", + "bytes": 1034 + }, + "s2-wrapper-review-source-manifest.json": { + "sha256": "948869d96918ed4f98b9f237569e3c4059b0c67beb7ed8fa7b98b9992f7105a3", + "bytes": 51171 + }, + "s2-wrapper-review.md": { + "sha256": "f07605e110eb62c874d99372e9dc5bc8a04123dd937ffa17f4a736a9ad923e4d", + "bytes": 2703 + }, + "s2-wrapper-tilde-probe.log": { + "sha256": "417acd28bdc8143feeda839a24ffe0d01cbb1cbe3ef3f125a16dc1d370c06bb6", + "bytes": 21794 + }, + "s2-wrapper-tilde-probe.rs": { + "sha256": "b24770c4ca6526c0d200eec787da638442dc8eedb3936cca1f4330ee51686cc5", + "bytes": 2008 + }, + "s2-wrapper-tilde-review.json": { + "sha256": "b947d90c3e29f0774a4d92d09e34cf7166aaae0ef666d96b3a3e89ec6457f930", + "bytes": 8215 + }, + "s2-wrapper-tilde-review.md": { + "sha256": "ee46e7b085b791a1d115e528406996724a1bee872289ff78b44315c146ab90fe", + "bytes": 3166 + }, + "s2-wrapper-windows-prefix-review.json": { + "sha256": "dab6d886ea2a9377b8475ea94a9b24b1743dc08e6b899ae62bda6fc0a93cfe85", + "bytes": 9711 + }, + "s2-wrapper-windows-prefix-review.md": { + "sha256": "583c1db9663998b036ed62b31c4e8fee9269b82d5b0eede713492f52671f0220", + "bytes": 3017 + }, + "s2-wrapper-windows-scope.json": { + "sha256": "3f54b537c928eeba44b5055c764540c32ecc9424da48c2c0af0176bec9098d90", + "bytes": 4361 + }, + "s3-facts-author-evidence.json": { + "sha256": "6c5f1e170737986c81462d7db4b6573c589a3704e5b8cf47a142e94b692e0540", + "bytes": 1959 + }, + "s3-facts-author-junit.xml": { + "sha256": "e522dad23d13903d49a0c712eb11acc323a670718f6c92b793c0029e3bee1618", + "bytes": 15415 + }, + "s3-facts-author-source-manifest.json": { + "sha256": "5dfe99c6a4f959caa850ffeb716c047fd951fc9331ca8c33d097b15a85e2c5b3", + "bytes": 938 + }, + "s3-facts-negative-traits.rs": { + "sha256": "4093f1f0d456229717ebfe9cecd64d8f4b90e5bd375fd33f18906ffdf38a705a", + "bytes": 319 + }, + "s3-facts-negative-traits.stderr": { + "sha256": "cc7485801f6bb80c9171c83cca83ec3c0adf9a546ea557061abe39d466e6fd91", + "bytes": 2400 + }, + "s3-facts-probe-result.json": { + "sha256": "f4a0a5267368cf8790ed916d538bbbf1bd4346ba98133dd7c2715b1845911768", + "bytes": 214 + }, + "s3-facts-probe.rs": { + "sha256": "a0bcf2101afc8f0d860123aceea0ce98e5d6987eab2e88f175c45e35eee799fe", + "bytes": 11800 + }, + "s3-facts-review-build.log.gz": { + "sha256": "8b7a14f02ccb91ec270235efe6babca3e3aa8bbe6b85073bf2b115071e03816c", + "bytes": 182 + }, + "s3-facts-review-evidence.json": { + "sha256": "5d2a7b6844562af42acd9b532069fb18a4341ce0ea152329028ec41c52d1913a", + "bytes": 3144 + }, + "s3-facts-review-junit.xml": { + "sha256": "e25b7e41502e1a46fabfa82fa8c04edd67802310dbf393e9ee0298e517d1b8d8", + "bytes": 15415 + }, + "s3-facts-review-nextest.log.gz": { + "sha256": "97647b2e6496741d69a3bcc32ee122e48b232e4adf10d2d29716f6d0d1c4950c", + "bytes": 2423 + }, + "s3-facts-review-source-manifest.json": { + "sha256": "714756d3f2dda06b71111bdda56bd8405c1dbd2456880b0ec34ac4d3f3d7cba2", + "bytes": 65147 + }, + "s3-facts-review.md": { + "sha256": "238862119d90368ad64bd34c7c6239ef8a1d0a852d5fe2d299834e4204249c6b", + "bytes": 11598 + } + } +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-cap-controls-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-cap-controls-manifest.json new file mode 100644 index 00000000..1ea7046d --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-cap-controls-manifest.json @@ -0,0 +1,400 @@ +{ + "scope": "Actual live producer capability and pure control codecs only; no complete Ready/effects/native platform closure. Actual accounting test failure179 preserved by author; focused originalP2 counterexample also archived.", + "commit": "a8b1c5b", + "files": [ + { + "path": "s2-cap-controls-author/inputs-after.json", + "sha256": "38de44f2e70fe71da18452dd920704cbaec8a521605586d8ec808ce227c3f665", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/inputs-after.json", + "source_sha256": "38de44f2e70fe71da18452dd920704cbaec8a521605586d8ec808ce227c3f665" + }, + { + "path": "s2-cap-controls-author/evidence.json", + "sha256": "0efd64fad14876445b35cb72e8e553069f6149ce6ae3746767507064c1caeb1a", + "bytes": 17090, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/evidence.json", + "source_sha256": "0efd64fad14876445b35cb72e8e553069f6149ce6ae3746767507064c1caeb1a" + }, + { + "path": "s2-cap-controls-author/junit-lib-133.xml", + "sha256": "96a4ae26d76421a19ffcaabfb8c40b75ca4667cbc72ebe146cb86162bce5bbcf", + "bytes": 30512, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/junit-lib-133.xml", + "source_sha256": "96a4ae26d76421a19ffcaabfb8c40b75ca4667cbc72ebe146cb86162bce5bbcf" + }, + { + "path": "s2-cap-controls-author/fmt.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/fmt.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s2-cap-controls-author/run.log.gz", + "sha256": "864cffb747ffee0fa2a4787b5d1afd1214fcaf33eeac20851f3cae17e41654ae", + "bytes": 4220, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/run.log", + "source_sha256": "225d587a7e9a66195925c47f03b421916210075f44d7d3edcd468efc2337872f" + }, + { + "path": "s2-cap-controls-author/harness-build.log.gz", + "sha256": "cc2dd83f1730709fb6fe62c3fadde0370cb1fd2fc7a1861bcf92c3da21289736", + "bytes": 9828, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/harness-build.log", + "source_sha256": "f9672b49fca0640cb215ca63c82e8e8206762fd43f6eca752010e6222771337d" + }, + { + "path": "s2-cap-controls-author/binary-after.json", + "sha256": "876747a8c935b1affa8f1e93ba25b6a63a028a51b7e5bf303e08ee7bed59a724", + "bytes": 14609, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/binary-after.json", + "source_sha256": "876747a8c935b1affa8f1e93ba25b6a63a028a51b7e5bf303e08ee7bed59a724" + }, + { + "path": "s2-cap-controls-author/snapshot.json", + "sha256": "7d5bc949e2f1c04df32d09186649fea86c8606040a94d8649d4e56a7cfcd49c4", + "bytes": 805, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/snapshot.json", + "source_sha256": "7d5bc949e2f1c04df32d09186649fea86c8606040a94d8649d4e56a7cfcd49c4" + }, + { + "path": "s2-cap-controls-author/junit-harness-6.xml", + "sha256": "e60905739eeb3abe6873baecaa4e7873970e510a11c3dfe26b124070f9ecdb14", + "bytes": 1865, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/junit-harness-6.xml", + "source_sha256": "e60905739eeb3abe6873baecaa4e7873970e510a11c3dfe26b124070f9ecdb14" + }, + { + "path": "s2-cap-controls-author/inputs-before.json", + "sha256": "38de44f2e70fe71da18452dd920704cbaec8a521605586d8ec808ce227c3f665", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/inputs-before.json", + "source_sha256": "38de44f2e70fe71da18452dd920704cbaec8a521605586d8ec808ce227c3f665" + }, + { + "path": "s2-cap-controls-author/binary-before.json", + "sha256": "876747a8c935b1affa8f1e93ba25b6a63a028a51b7e5bf303e08ee7bed59a724", + "bytes": 14609, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/binary-before.json", + "source_sha256": "876747a8c935b1affa8f1e93ba25b6a63a028a51b7e5bf303e08ee7bed59a724" + }, + { + "path": "s2-cap-controls-author/harness-run.log.gz", + "sha256": "1773e8a3a542ad0b2b38fc3da772363d2fce8c150dc9bcf2e21fc288a8520cb0", + "bytes": 10174, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/harness-run.log", + "source_sha256": "f88d7559775588f76aa98e922acf93bb9a31423becdab3cf885be512df535f30" + }, + { + "path": "s2-cap-controls-author/harness-binary-before.json", + "sha256": "2ae9926263631019dc2fd0a958b2cb2e2a00d69ae868626e14fc6e8407246c03", + "bytes": 964, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/harness-binary-before.json", + "source_sha256": "2ae9926263631019dc2fd0a958b2cb2e2a00d69ae868626e14fc6e8407246c03" + }, + { + "path": "s2-cap-controls-author/inventory.json", + "sha256": "699899307b9009b71bb670f67fb9360961e8ab6f6c481c5a24d2d3f71cc5f436", + "bytes": 108498, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/inventory.json", + "source_sha256": "699899307b9009b71bb670f67fb9360961e8ab6f6c481c5a24d2d3f71cc5f436" + }, + { + "path": "s2-cap-controls-author/check.log.gz", + "sha256": "d3b24d2cd61691ca4647a18a3a098376de41af810e70a4c52697c0371fb671c2", + "bytes": 9905, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/check.log", + "source_sha256": "c856140be0224bd4271608ff2309140a36448116ae66ae3230a2c2dddd83025f" + }, + { + "path": "s2-cap-controls-author/harness-inventory.json", + "sha256": "43a00e43cda540f4b7c246189899c9dff2d3637efd97d34dfe039e857837d12a", + "bytes": 33119, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/harness-inventory.json", + "source_sha256": "43a00e43cda540f4b7c246189899c9dff2d3637efd97d34dfe039e857837d12a" + }, + { + "path": "s2-cap-controls-author/harness-binary-after.json", + "sha256": "2ae9926263631019dc2fd0a958b2cb2e2a00d69ae868626e14fc6e8407246c03", + "bytes": 964, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/harness-binary-after.json", + "source_sha256": "2ae9926263631019dc2fd0a958b2cb2e2a00d69ae868626e14fc6e8407246c03" + }, + { + "path": "s2-cap-controls-author/build.log.gz", + "sha256": "b1f34d327a9417f7aff80b72e2bd3f88a20a15ffadf6826a3c8ba10dd8788126", + "bytes": 161, + "source": "/private/tmp/cleanup-s2-cap-controls-successor-rdqnse82/build.log", + "source_sha256": "946f6103dc7ea27bfb7704e4655fc8d6e7b1e47abd0849b4920bb0f7f6fde6b0" + }, + { + "path": "s2-cap-controls-review/inputs-after.json", + "sha256": "a5d16616bb18398c65232b2e3b437b406ac2e096b4ac869bcf19ddb6cbd15d14", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/inputs-after.json", + "source_sha256": "a5d16616bb18398c65232b2e3b437b406ac2e096b4ac869bcf19ddb6cbd15d14" + }, + { + "path": "s2-cap-controls-review/review-report.md", + "sha256": "86a827eedd4d89f24879e3a3371b9922c77e89c86131bee094006b098a3b04c9", + "bytes": 2392, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/review-report.md", + "source_sha256": "86a827eedd4d89f24879e3a3371b9922c77e89c86131bee094006b098a3b04c9" + }, + { + "path": "s2-cap-controls-review/fmt.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/fmt.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s2-cap-controls-review/cap-trait-negative_serde.log.gz", + "sha256": "8ca75e40bb696f206b9377b81ccf37c065ed3cd65b9df2581a0e2afb5b0e19cb", + "bytes": 679, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-negative_serde.log", + "source_sha256": "6b7e3826516eb1ef655ff5b6f83eb2e0874ea61b1005d73d0f0f7998ebe3ce20" + }, + { + "path": "s2-cap-controls-review/codec-boundary-run.log.gz", + "sha256": "b5c812a92f210e6378bc016ef99ce8a88bfb35b17973493b2f5f5fdb0780a756", + "bytes": 150, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/codec-boundary-run.log", + "source_sha256": "ca7c69f26c5bc38f82fa6741d47f2ac9b8300de02a4bc1cb3f6ef48a7379b6a3" + }, + { + "path": "s2-cap-controls-review/cap-trait-negative_clone.log.gz", + "sha256": "721508589d4f9192de790beb81a197d0283c4f6e9f87280fc12998126206fae6", + "bytes": 439, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-negative_clone.log", + "source_sha256": "df312639877057382f2c4addb7da274b7f19392450fdcfa9b3ca62b7b0849fc1" + }, + { + "path": "s2-cap-controls-review/review-report.json", + "sha256": "281cb48cbbbab4e5851c43376d37082a219866eed2103dc381f135382a8d7d40", + "bytes": 18437, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/review-report.json", + "source_sha256": "281cb48cbbbab4e5851c43376d37082a219866eed2103dc381f135382a8d7d40" + }, + { + "path": "s2-cap-controls-review/capability-3.log.gz", + "sha256": "0c6bdfb5a1ba4a2182e0947c7f85551c1b2c5adb8b593f42746918b3691795d8", + "bytes": 181, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/capability-3.log", + "source_sha256": "d3a86ffa84b9514dafd3d52a529a6614840c507d22d0ada4f0f95ee4e271e48d" + }, + { + "path": "s2-cap-controls-review/capability-2.log.gz", + "sha256": "1a1073a13f7e948d5bda1c3b2b5940a0b56a67f6aaa5c53b61298efce22e9a64", + "bytes": 187, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/capability-2.log", + "source_sha256": "e4bffbc4173211d8c1ae23c93d7fe905a2abcc57434e15b880d858f9a6d71234" + }, + { + "path": "s2-cap-controls-review/snapshot.json", + "sha256": "4a18d281715a82c53772cdbc2c14b30efb1e40c9cb1c958a4016f4df97be4cf2", + "bytes": 963, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/snapshot.json", + "source_sha256": "4a18d281715a82c53772cdbc2c14b30efb1e40c9cb1c958a4016f4df97be4cf2" + }, + { + "path": "s2-cap-controls-review/cap-trait-negative_send_sync.rs", + "sha256": "236ec2d9dd5025f3078d4370454d2120a829252589ae982dc85ece03f12a8e25", + "bytes": 1162, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-negative_send_sync.rs", + "source_sha256": "236ec2d9dd5025f3078d4370454d2120a829252589ae982dc85ece03f12a8e25" + }, + { + "path": "s2-cap-controls-review/capability-1.log.gz", + "sha256": "f3fdf8e30a31c051e664b2698d5607bdb792b231c65f6bb1965b01d37c95e58f", + "bytes": 183, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/capability-1.log", + "source_sha256": "a0db291bc704746c7a0a75932d5de6a329bb994e822b1f0d1ad36fea89a7c975" + }, + { + "path": "s2-cap-controls-review/cap-trait-negative_clone.rs", + "sha256": "8ac903dcd2efcbe533c508c5c4687184fe367a903d72cfa617a8636428ea7029", + "bytes": 1158, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-negative_clone.rs", + "source_sha256": "8ac903dcd2efcbe533c508c5c4687184fe367a903d72cfa617a8636428ea7029" + }, + { + "path": "s2-cap-controls-review/codec-boundary-provenance.json", + "sha256": "d97972b274d3788946aba920a6a571799da90b522eb3e3594373035b0e420fcc", + "bytes": 521, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/codec-boundary-provenance.json", + "source_sha256": "d97972b274d3788946aba920a6a571799da90b522eb3e3594373035b0e420fcc" + }, + { + "path": "s2-cap-controls-review/inputs-before.json", + "sha256": "a5d16616bb18398c65232b2e3b437b406ac2e096b4ac869bcf19ddb6cbd15d14", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/inputs-before.json", + "source_sha256": "a5d16616bb18398c65232b2e3b437b406ac2e096b4ac869bcf19ddb6cbd15d14" + }, + { + "path": "s2-cap-controls-review/controls-actual-source-probe.rs", + "sha256": "ca466847c4a29f9f39f8f168403e2bdbda3561fd1e443cfd5350efc411944132", + "bytes": 308, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/controls-actual-source-probe.rs", + "source_sha256": "ca466847c4a29f9f39f8f168403e2bdbda3561fd1e443cfd5350efc411944132" + }, + { + "path": "s2-cap-controls-review/cap-trait-provenance.json", + "sha256": "47a4952c891a538d760130074f29e26f2b846a504cb46d274e2e1e87c92d2aa8", + "bytes": 5771, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-provenance.json", + "source_sha256": "47a4952c891a538d760130074f29e26f2b846a504cb46d274e2e1e87c92d2aa8" + }, + { + "path": "s2-cap-controls-review/codec-boundary-build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/codec-boundary-build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s2-cap-controls-review/cap-trait-negative_send_sync.log.gz", + "sha256": "92cd2f442f56cd9b4182f18b98ba0726abde82948d74b8dcd69b0f12adf6827f", + "bytes": 627, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-negative_send_sync.log", + "source_sha256": "bf1c94d58f399cb6fd9c2e0d786a2022add530b5e3f2425f697db1d446eb561f" + }, + { + "path": "s2-cap-controls-review/cap-trait-positive_debug.rs", + "sha256": "b78fc7a954565a1bdec391f98ee7bb7d2c1decf5466324545820f598f906e92c", + "bytes": 1158, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-positive_debug.rs", + "source_sha256": "b78fc7a954565a1bdec391f98ee7bb7d2c1decf5466324545820f598f906e92c" + }, + { + "path": "s2-cap-controls-review/cap-trait-positive_debug.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-positive_debug.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s2-cap-controls-review/controls-probe-provenance.json", + "sha256": "b1a66bfbf7365fb00148349619a79828300c191dc9c36235cafaeef8fc10c1de", + "bytes": 742, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/controls-probe-provenance.json", + "source_sha256": "b1a66bfbf7365fb00148349619a79828300c191dc9c36235cafaeef8fc10c1de" + }, + { + "path": "s2-cap-controls-review/codec-boundary-probe.rs", + "sha256": "12bc782eedd56f6da643e6b13d00697a40d76538f81466692d63c24076fe4b52", + "bytes": 2013, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/codec-boundary-probe.rs", + "source_sha256": "12bc782eedd56f6da643e6b13d00697a40d76538f81466692d63c24076fe4b52" + }, + { + "path": "s2-cap-controls-review/cap-trait-negative_serde.rs", + "sha256": "37aa10a3cdbb5f6358a5981675c8b5b5cdccf06ba94fbdfaeda69ba1d219b699", + "bytes": 1158, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/cap-trait-negative_serde.rs", + "source_sha256": "37aa10a3cdbb5f6358a5981675c8b5b5cdccf06ba94fbdfaeda69ba1d219b699" + }, + { + "path": "s2-cap-controls-review/controls-probe-run.log.gz", + "sha256": "d22e1f7b5f05246114d9785abc8c7995673171c2a40fad2519bbef1ae20d94da", + "bytes": 378, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/controls-probe-run.log", + "source_sha256": "b1103805f71cbea1669c5bb1af68c2f569ee9d80147daa12d204c4daf5275ecc" + }, + { + "path": "s2-cap-controls-review/capability-probe-provenance.json", + "sha256": "2ab873f1f8dbdf3feb74e4d030cf64f6a3673411ff562d6c240c6838f51915d8", + "bytes": 2555, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/capability-probe-provenance.json", + "source_sha256": "2ab873f1f8dbdf3feb74e4d030cf64f6a3673411ff562d6c240c6838f51915d8" + }, + { + "path": "s2-cap-controls-review/controls-probe-build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s2-cap-controls-fix-review-kh0ry6yd/controls-probe-build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s2-cap-controls-first-review/snapshot.json", + "sha256": "1af34cf3c940fd9ee019499b90e4addef0719766b705c536d36a13654f992548", + "bytes": 946, + "source": "/private/tmp/cleanup-s2-cap-controls-review-v1be53fm/snapshot.json", + "source_sha256": "1af34cf3c940fd9ee019499b90e4addef0719766b705c536d36a13654f992548" + }, + { + "path": "s2-cap-controls-first-review/inputs-before.json", + "sha256": "84d8d76280c8983f50d15d4ade9b3b50e352d854cf4b5594ddf437a72edcce2b", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-cap-controls-review-v1be53fm/inputs-before.json", + "source_sha256": "84d8d76280c8983f50d15d4ade9b3b50e352d854cf4b5594ddf437a72edcce2b" + }, + { + "path": "s2-cap-controls-first-review/controls-account-failure.log.gz", + "sha256": "4ce166f8165b8245b00a1dab246d9e463725393ea6d412aae1afe93618b26bd9", + "bytes": 389, + "source": "/private/tmp/cleanup-s2-cap-controls-review-v1be53fm/controls-account-failure.log", + "source_sha256": "24f5f4e5e85f9cc21af322c764afdd578b7556fa2d55d085b42a18662afedde6" + }, + { + "path": "s2-cap-controls-first-review/controls-actual-source-probe.rs", + "sha256": "3ea407dfc5e1720127ea087907423729c8a2b4d0f14733b12f695299eebbad05", + "bytes": 300, + "source": "/private/tmp/cleanup-s2-cap-controls-review-v1be53fm/controls-actual-source-probe.rs", + "source_sha256": "3ea407dfc5e1720127ea087907423729c8a2b4d0f14733b12f695299eebbad05" + }, + { + "path": "s2-cap-controls-first-review/controls-all-original.log.gz", + "sha256": "9ea70879f4a4c90db0f297d82f0bf988f55e58721446c0491b10560db0dadb88", + "bytes": 614, + "source": "/private/tmp/cleanup-s2-cap-controls-review-v1be53fm/controls-all-original.log", + "source_sha256": "5db623bb326f943499f22a64148972e473a4cd73beaeb21de9de3c276b891c16" + }, + { + "path": "s2-cap-controls-first-review/controls-probe-provenance.json", + "sha256": "2a96ffbffea2842d965ea79b72cc610b0cf6bb0425ac31220d9c6333e6c5c06c", + "bytes": 856, + "source": "/private/tmp/cleanup-s2-cap-controls-review-v1be53fm/controls-probe-provenance.json", + "source_sha256": "2a96ffbffea2842d965ea79b72cc610b0cf6bb0425ac31220d9c6333e6c5c06c" + }, + { + "path": "s2-cap-controls-first-review/controls-probe-build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s2-cap-controls-review-v1be53fm/controls-probe-build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "proposal-v2/s2_publication_factory_extension_review.md", + "sha256": "4001b6ad34ae10a99be64e79b853c78347488f446c9d5f131d022a54bed63111", + "bytes": 8926, + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s2_publication_factory_extension_review.md" + }, + { + "path": "proposal-v2/legacy_direct_minimum_origin_review.md", + "sha256": "beea9a010d508368d5f9c0833e61e97519de917106c7e5620aa2c6fea6d8891c", + "bytes": 8147, + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/legacy_direct_minimum_origin_review.md" + }, + { + "path": "proposal-v2/dc-s2-publication-factory-extension-20261021.md", + "sha256": "b2d6ce12530c4f96a9b1985d3ab2449b4cc41c4d9ba1f36e0f99ce3d691b8f96", + "bytes": 9274, + "source": "/private/tmp/dc-s2-publication-factory-extension-20261021.md" + }, + { + "path": "proposal-v2/dc-s2-publication-factory-extension-addendum-20261021.md", + "sha256": "1bd90983b6591fb3c29d753cca85ccb4e8282c9891cf27f290b389e02c530564", + "bytes": 4554, + "source": "/private/tmp/dc-s2-publication-factory-extension-addendum-20261021.md" + }, + { + "path": "proposal-v2/dc-s3-legacy-native-projection-proposal-20261021.md", + "sha256": "9ab1f97bd62d8bbe4cbc866b4d94d578a6f142aa587f34baa43b30f0c5602442", + "bytes": 11254, + "source": "/private/tmp/dc-s3-legacy-native-projection-proposal-20261021.md" + } + ] +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-evidence.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-evidence.json new file mode 100644 index 00000000..dd5d0d4d --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-evidence.json @@ -0,0 +1,72 @@ +{ + "snapshot": "baseline88a369f plus only coordinator Cargo.toml feature and4wrapperfiles", + "inputs_identical": true, + "input_count": 424, + "binary": { + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-721629b084bb149b", + "sha256": "dde40bdbda09e7dc015c280977b933c9619693ab963c0a45ca6148f34a4c0bfd", + "cwd": "/private/tmp/cleanup-s2-wrapper-windowsfix-09wodcy6/repo", + "selected": [ + "api::context::tests::dc_epoch_optional_direct_cleanup_keeps_both_runtime_selectors_readable_and_pending", + "api::context::tests::dc_epoch_optional_managed_cleanup_keeps_runtime_readable_and_pending", + "api::graph_read::tests::manifest_without_backend_metadata_uses_legacy_ladybug_fts", + "db_writer::tests::guarded_database_failed_native_open_releases_exclusion", + "db_writer::tests::guarded_database_move_and_connection_retain_physical_family", + "db_writer::tests::guarded_database_native_path_rejects_full_non_utf8_before_effects", + "db_writer::tests::guarded_database_native_path_rejects_transforms_before_filesystem_effects", + "db_writer::tests::guarded_database_readonly_rejects_parent_without_coordination_write_access", + "db_writer::tests::guarded_database_rejects_symlink_and_hardlink_resources_before_open", + "db_writer::tests::guarded_database_relative_native_open_uses_one_absolute_guarded_resource", + "db_writer::tests::guarded_database_symlink_parent_traversal_cannot_bind_another_native_resource", + "db_writer::tests::guarded_database_unicode_alias_retains_physical_parent_exclusion", + "db_writer::tests::guarded_database_unwind_closes_native_handle_and_releases_exclusion", + "db_writer::tests::guarded_database_windows_encoding_policy_checks_the_complete_native_path", + "db_writer::tests::native_writer_loads_json_staging_through_ladybug_copy", + "db_writer::tests::native_writer_loads_multiple_chunks_into_one_primary_key_table", + "db_writer::tests::transient_database_error_detection_matches_ladybug_lock_failures", + "db_writer::tests::transient_database_retry_replays_operation_until_success", + "storage::maintenance_ownership::tests::dc_epoch_optional_direct_cleanup_preserves_committed_read_but_not_journal_replay", + "storage::maintenance_ownership::tests::dc_epoch_optional_managed_cleanup_preserves_committed_read_and_residue", + "storage::maintenance_ownership::tests::dc_s2_family_group_deduplicates_same_parent_and_retains_all_guards", + "storage::maintenance_ownership::tests::dc_s2_family_group_handles_distinct_parents_and_duplicate_resource_keys", + "storage::maintenance_ownership::tests::dc_s2_family_macos_long_s_consumer_blocks_ascii_direct_even_with_valid_record", + "storage::maintenance_ownership::tests::dc_s2_family_macos_parent_aliases_deduplicate_physical_family", + "storage::maintenance_ownership::tests::dc_s2_family_macos_unicode_nfc_nfd_existing_and_creation_aliases_exclude", + "storage::maintenance_ownership::tests::dc_s2_family_producer_conversion_holds_parent_until_session_release", + "storage::maintenance_ownership::tests::dc_s2_family_recreated_coordination_invalidates_destination_proof", + "storage::maintenance_ownership::tests::dc_s2_family_replaced_parent_incarnation_invalidates_held_use", + "storage::maintenance_ownership::tests::dc_s2_family_shared_existing_readable_coordination_requires_no_write_permission" + ] + }, + "runtime_parallel": { + "passed": 29, + "leaky": 3, + "log": "run.log", + "junit": "junit-parallel.xml" + }, + "runtime_serial": { + "passed": 29, + "leaky": 0, + "log": "run-serial.log", + "junit": "junit-serial.xml" + }, + "doctests": { + "passed": 3, + "log": "doctests.log" + }, + "workspace_all_targets_check": { + "passed": true, + "existing_dead_code_warnings": 75, + "log": "workspace-check.log" + }, + "format": { + "passed": true, + "log": "fmt.log" + }, + "limitations": [ + "cfg(windows) implementation and native tests were neither compiled nor executed by Darwin", + "UNC preservation is static/source coverage, not native UNC execution", + "parallel leaked-output-handle observations remain preserved; serial result is not proof of their root cause", + "no fullS2/collector/aggregate qualification" + ] +} \ No newline at end of file diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-parallel-junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-parallel-junit.xml new file mode 100644 index 00000000..f5b65438 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-parallel-junit.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-parallel.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-parallel.log.gz new file mode 100644 index 00000000..eef7b0d5 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-parallel.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-serial-junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-serial-junit.xml new file mode 100644 index 00000000..c7c3aed8 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-serial-junit.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-binaries.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-binaries.json new file mode 100644 index 00000000..3f88cc26 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-binaries.json @@ -0,0 +1,8 @@ +{ + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-5cc476c357a26360", + "sha256": "99e91c39f8ddc1eb49e36b346be65653d8cbf977e5ddb468c346a2e5db8bc78e", + "package": "path+file:///private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/repo#codebase-graph@3.0.4", + "cwd": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/repo", + "test_count": 537, + "encoding_test_listed": true +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-build.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-build.log.gz new file mode 100644 index 00000000..1b5a6012 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-build.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-doctests.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-doctests.log.gz new file mode 100644 index 00000000..3a0d3cb8 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-doctests.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-evidence.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-evidence.json new file mode 100644 index 00000000..9cb8093f --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-evidence.json @@ -0,0 +1,154 @@ +{ + "reviewer": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max", + "independent_from_implementer": true, + "skill": "/Users/rabii/.codex/skills/adversarial-review-engineer/SKILL.md" + }, + "scope": "Focused verification of canonical Windows-prefix P1 correction, native path code-page identity, and affected wrapper paths. Read-only reviewed source; isolated source reconstruction and evidence only.", + "snapshot": { + "baseline": "88a369f5820c7c21e05a4336f061887cc5f5d52f", + "repo": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/repo", + "patch_sha256": "c0d22b8bcbbdd6b838dac8506e29cf1dd698b87f924a22b1152df622a806211b", + "source": { + "Cargo.toml": "308ca32ea3d86311d11700d9c435030abf145d3d9e9a73f658f41a97c146beaa", + "src/db_writer/access.rs": "104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d", + "src/db_writer/mod.rs": "519f8c9c5f0482d0ca4d9cea6430fe831ea753b3a13b7ba5af06499f91a0f81e", + "src/db_writer/tests.rs": "fb00bc867d9aaeeb903c62bae36c6d08bf20726c61efd41b726c053e0f3ff1f2", + "src/api/graph_read.rs": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b" + }, + "limitations": [ + "Darwin verification requested; no Windows compile/runtime available in this session", + "UNC syntax checks are not native UNC execution" + ], + "root": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl", + "patch": "/private/tmp/cleanup-s2-wrapper-windowsfix-09wodcy6/wrapper-and-feature.patch" + }, + "dispositions": { + "WRAPPER-PATH-1": { + "status": "Previously corrected tilde/path interpretation finding remains corrected in source and passing current Darwin regressions.", + "historical_report": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/review-report.json" + }, + "WRAPPER-WINDOWS-1": { + "status": "CONFIRMED SOURCE DEFECT CORRECTED STATICALLY; native Windows qualification remains pending.", + "historical_report": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/windows-prefix-followup.json", + "correction": "access.rs204-210 now accepts Disk, UNC, VerbatimDisk and VerbatimUNC. normalized_windows_native_path248-290 preserves valid canonical extended namespace spelling without prefix stripping, rejecting slash/repeated separator/dot/dot-dot tails that native lexical normalization could reinterpret. Therefore ordinary canonicalizer-produced paths are no longer blanket-rejected by the validator.", + "unchanged_flow": "One captured absolute owned path still feeds both DatabaseUseLease and Database::new at access.rs146-150. Raw persisted Direct address-derived keys and graph caller SystemConfig flags are unchanged." + } + }, + "codepage_assessment": { + "pinned_native_boundary": "lbug0.19.0 src/database.rs passes Path display text (UTF-8 after helper validation); packaged native LocalFileSystem::openFile uses CreateFileA, and StorageUtils::expandPath uses std::filesystem conversion/lexical normalization.", + "guard": "The helper validates the entire absolute path. Pure policy allows ASCII at any checked code page, or non-ASCII only when effective FileAPI code page equals65001. Before coordination it calls ensure_windows_native_encoding; the constructor repeats the check immediately before native open.", + "mode": "windows_effective_file_api_code_page301-315 reads AreFileApisANSI and selects GetACP or GetOEMCP accordingly. No process-global setters are introduced. Console code page is not used as proof.", + "feature_and_bindings": "Cargo.toml adds windows-sys Win32_Globalization. Inspected installed windows-sys0.61.2 exports GetACP/GetOEMCP under that feature and AreFileApisANSI under already enabled Storage_FileSystem. This is signature/source inspection, not Windows compilation.", + "primary_evidence": "Microsoft documents AreFileApisANSI as selecting process ANSI/OEM filename decoding and GetACP/GetOEMCP as retrieving the corresponding code pages. Current Microsoft STL source __std_fs_code_page gives UTF-8 locale priority, otherwise selects ANSI/OEM FileAPI mode. The allowed UTF-8 subset is consistent with that source, but the actual Windows compiler/CRT binary used by the eventual build was not qualified here.", + "limitations": "Read-only rechecks observe but do not freeze external process-global FileAPI mode. End-to-end Windows CRT/SDK behavior, mode variants, and any external mode mutation during native use remain unexecuted; no supported native-platform guarantee is inferred from this static assessment." + }, + "new_confirmed_findings": [], + "independent_checks": { + "platform": "aarch64-apple-darwin", + "root_test_override": "profile.test.package.codebase-graph.codegen-units=149", + "compilation_path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/repo", + "nextest": { + "passed": 29, + "failures": 0, + "errors": 0, + "leak_annotations": 0, + "unselected": 508, + "execution": "serial, test-threads1", + "run_id": "853e45ac-213f-49b3-ae26-3fa6d245940b", + "junit_sha256": "b539f8fa4d2cc86f107177ed8951a5858797c34e70bb17e43dcf260c1b4a6fee", + "binary": { + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-5cc476c357a26360", + "sha256": "99e91c39f8ddc1eb49e36b346be65653d8cbf977e5ddb468c346a2e5db8bc78e", + "package": "path+file:///private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/repo#codebase-graph@3.0.4", + "cwd": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/repo", + "test_count": 537, + "encoding_test_listed": true + } + }, + "doctests": { + "passed": 3, + "failures": 0, + "coverage": "One positive no-run compile and two compile-fail lifetime/raw-extraction cases." + }, + "formatting": "cargo fmt --all --check exited0; empty fmt.log", + "stability": "All424 frozen inputs, including Cargo feature plus four wrapper files, and test binary hash identical before/after.", + "coverage_note": "The platform-independent encoding policy test runs on Darwin; cfg(windows) path normalization, Win32 calls and native Windows tests do not compile or execute on Darwin." + }, + "author_evidence": { + "qualification": "/private/tmp/cleanup-s2-wrapper-windowsfix-09wodcy6/qualification.json", + "qualification_sha256": "4d9d466f31c02de409fa03c915e3246f9c7dfd08ad53c2a0f697b7324f7fa401", + "workspace_all_targets": "Author reports passing Darwin workspace/all-targets check with75 preexisting warnings; not independently rerun by this reviewer.", + "parallel_leak": "Author parallel29 run had3 LEAK statuses, including a no-native-open fixture; raw run.log/junit-parallel.xml retained. Author serial29 and independent serial29 are clean. Cause remains unknown; no new source defect is inferred and no parallel qualification is asserted." + }, + "remaining_qualification": [ + "Actual Windows compilation and execution of canonicalized disk paths, with native query plus physical-family exclusion.", + "Native Windows UNC where available, extended long paths, non-ASCII parents and relevant ANSI/OEM/effective UTF-8 modes; record which native branches actually execute.", + "Windows canonical/long-path tests can return after a deliberate encoding rejection when the temporary root contains non-ASCII on a non-UTF8 code page. Such a branch is rejection coverage, not successful native-open qualification.", + "Actual Windows compiler/CRT path conversion behavior and stable process FileAPI assumptions across native use.", + "Unknown author parallel leaked-output-handle observations; clean serial execution does not diagnose their cause.", + "75 existing pending-foundation warnings, full S2 fast seal/phase admission, collector, automatic runtime, Linux/Windows platform and aggregate closure remain pending." + ], + "workflow_limits": [ + "Configured graph indexes older original checkout; graph-first discovery plus explicit frozen/Scryer anchors guided source reads.", + "Source-only archive has no Scryer model; previously fetched approved chg-6vx5tj node-9emgn1/resp-2x4van and node-2xwzve intent retained without model mutation/substitution.", + "No source/test/wiki/Scryer/Git mutation. Historical reports and logs preserved unchanged." + ], + "primary_sources": [ + "https://doc.rust-lang.org/std/fs/fn.canonicalize.html", + "https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-arefileapisansi", + "https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getacp", + "https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page", + "https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea", + "https://github.com/microsoft/STL/blob/main/stl/src/filesystem.cpp" + ], + "evidence": { + "snapshot.json": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/snapshot.json", + "sha256": "1be27a626cc5300c2d9026e27e4071f216ca8b4bc09516f3ec4e2623cc88d77c" + }, + "inputs-before.json": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/inputs-before.json", + "sha256": "948869d96918ed4f98b9f237569e3c4059b0c67beb7ed8fa7b98b9992f7105a3" + }, + "inputs-after.json": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/inputs-after.json", + "sha256": "948869d96918ed4f98b9f237569e3c4059b0c67beb7ed8fa7b98b9992f7105a3" + }, + "binary-before.json": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/binary-before.json", + "sha256": "0269a6b5dd9910ecf56beb76f83fc07c1a73c7e40e41bb49b1fdd2d40dfb8af5" + }, + "binary-after.json": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/binary-after.json", + "sha256": "0269a6b5dd9910ecf56beb76f83fc07c1a73c7e40e41bb49b1fdd2d40dfb8af5" + }, + "inventory.json": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/inventory.json", + "sha256": "1ef4acf972d72e3205bbc48f861fb67b2751488ff22b1711b7e3c7f31bb546bb" + }, + "build.log": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/build.log", + "sha256": "1063effcc8018939cbdb6803d4e2c1ab89634509db512c1c2fa701e5603cedac" + }, + "run-serial.log": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/run-serial.log", + "sha256": "c7bd425fbcf52a12ca590d954cfd2a5482312650339ae3326230d022af9d9ccb" + }, + "junit-serial.xml": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/junit-serial.xml", + "sha256": "b539f8fa4d2cc86f107177ed8951a5858797c34e70bb17e43dcf260c1b4a6fee" + }, + "doctests.log": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/doctests.log", + "sha256": "7eff7df4c5c6bedfe3976f440d2563f1704606089292ba5a0285923e340bae21" + }, + "fmt.log": { + "path": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/fmt.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + "memory_decision": "Durable lesson remains exact native-path/encoding identity at FFI ownership boundaries; coordinator owns wiki/memory recording. No reviewer memory mutation.", + "cargo": "RELEASED" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-junit.xml new file mode 100644 index 00000000..98ce6dec --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-junit.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-run.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-run.log.gz new file mode 100644 index 00000000..ecb7ecae Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-run.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-snapshot.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-snapshot.json new file mode 100644 index 00000000..a5143320 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-snapshot.json @@ -0,0 +1,18 @@ +{ + "baseline": "88a369f5820c7c21e05a4336f061887cc5f5d52f", + "repo": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/repo", + "patch_sha256": "c0d22b8bcbbdd6b838dac8506e29cf1dd698b87f924a22b1152df622a806211b", + "source": { + "Cargo.toml": "308ca32ea3d86311d11700d9c435030abf145d3d9e9a73f658f41a97c146beaa", + "src/db_writer/access.rs": "104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d", + "src/db_writer/mod.rs": "519f8c9c5f0482d0ca4d9cea6430fe831ea753b3a13b7ba5af06499f91a0f81e", + "src/db_writer/tests.rs": "fb00bc867d9aaeeb903c62bae36c6d08bf20726c61efd41b726c053e0f3ff1f2", + "src/api/graph_read.rs": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b" + }, + "limitations": [ + "Darwin verification requested; no Windows compile/runtime available in this session", + "UNC syntax checks are not native UNC execution" + ], + "root": "/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl", + "patch": "/private/tmp/cleanup-s2-wrapper-windowsfix-09wodcy6/wrapper-and-feature.patch" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-source-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-source-manifest.json new file mode 100644 index 00000000..d1c9e310 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-source-manifest.json @@ -0,0 +1,426 @@ +{ + ".cargo/config.toml": "388ad5407d7e78cae8dacd1c3c556f8c97f43d998c4e09273dcf8fca00eb0f82", + ".github/actions/setup-native/action.yml": "76c67a676d283d59c8fed8b1f0db71df540ed0a7ad4b48b976ed9cf161d91a08", + ".github/actions/verified-release/action.yml": "b69e532e353090199ff7c8c98e1c71dc1d060cf7207af00c10e65cd8e7ebd230", + ".github/dependabot.yml": "960870ef6e187996b45b672a2590490efd9558d6103235234834d34a697318dd", + ".github/workflows/ci.yml": "28a2ad81d82476a7d5faa8234b30b4fead29130b4eda480bf046368b454fc9de", + ".github/workflows/native.yml": "c707e4eabcef7886436f426cf13bc627082ede7409f0ecc061b5c631fd784a92", + ".github/workflows/release.yml": "a5b43d36f805848793c9bcd2c03b456a4c8779dd2d318fcb8be6af081fd32f30", + ".gitignore": "c1539fae5f820edf177fb1f3f695d63fcb2e375654ea09bfbd8675d3419f8e47", + ".release-please-manifest.json": "cd76ee03fb0ca1f78049405025ad82d24d8c13a7c2a11e2db4556456939b963f", + "CHANGELOG.md": "cfde98196d430a4077eff9bff66c7575ff05095f38dc4b58c740eb08bd39084d", + "Cargo.lock": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6", + "Cargo.toml": "308ca32ea3d86311d11700d9c435030abf145d3d9e9a73f658f41a97c146beaa", + "LICENSE": "d6a96f62ba19e3c410e26b40970ff842fb9d29afacaaca1e3b8cab33eb46eb61", + "README.md": "79ad620f0a8c4fe84425faefa5406e09ba04f14ae83410f62762cca30163b1b5", + "SECURITY.md": "a838500eac12bd53304f485c21052df40435b4ed37cb2823fe63f3ae225e44e5", + "assets/architecture_queries.json": "f14a2f43e0f1d1748f39aa0f7a92791e832955cf25a2435357d25bfa1d33a41c", + "assets/graph_schema.json": "244fbce950f51ed5db6b52fa8aadb418fdd96d73bc475b4a1a0d1fb66e34d3bd", + "assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz": "0954cd079bb3f24c633242e4cf31dfe949c54a5f575cd983edf45fb44b469e7f", + "assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz": "f4349356314482b3c802017b56fb7b78efa31b81a85f9804c6f4273be15e6a58", + "assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz": "0494bdab7d96d07bc6693a8a62a6b852f64bab2670adbf6bd3c3c6fae57a8f8a", + "assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz": "d649d27b7383277cc4764592fd2dd99a9dfd16d91f167da52dc71ed2fe75e5ba", + "assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz": "8156ebd29b5bb2d0742994a9a738afefd807740d5ef74f5ab1d15cb2d10a8e03", + "assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz": "ff32b9298a5b271fffb8b10f48ef5425da3fa1f2a453c2522d894de7acbab753", + "assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz": "ac6faf62a5a6bd236ee27899327abcdc56a7bc2fd8267619f767ffe4e7254f93", + "assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz": "b36f55d57125ecf05b8ba6d9c53e4d6d2532d81f09e9a37b8294d8b46e01c957", + "assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz": "ffc724819419c3c9e93e810cde560acea93a445c9d260f1041c3c99aaa75c8df", + "assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz": "07c20799a0ba85e43f5ad09fd599b708653b614f07ccd5b5743d7432438ceafd", + "assets/mcp_tool_specs.json": "b7fd145e4885b4009e6d8ccecb0300eb6286ca7d117a915934478c64c72ab060", + "assets/query_helpers.json": "c91ab54b35de0c26a14cffbb531e0a4374763f4ccb723e6d8d3bac8f2a91b689", + "build.rs": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + "conda-forge/recipe/meta.yaml": "47a6b0c02a84fbfada104cc5806a3629040fd6db83651957a66a8e2ff396590d", + "crates/k-wiki/Cargo.toml": "f6b8c6e0814637c4f9def32a93ffbb40eda34cac385399eb3a4675cb945c8a46", + "crates/k-wiki/assets/wiki.css": "0c8c4a8b828c92aa838fcbe4d5cb823630aa1d3f9280befc479e6f89cc9cd775", + "crates/k-wiki/src/adapters/cli.rs": "b5d5d054a2a063f5b8f881547aa3d34c75aae74dbf94465a0a5ad353c1926bf4", + "crates/k-wiki/src/adapters/http.rs": "8ae80aa4601a8776369cbf6426f23ab986c51626d95673561673fb29663f2aa4", + "crates/k-wiki/src/adapters/install.rs": "7be961ca1516892e4b63f4c32a8536996bdca1e1f5447d294028972b20c5e4bf", + "crates/k-wiki/src/adapters/mcp.rs": "4baad85ade6fcf8f2eb7a69b5c07929bacc783e65d2bb8c3192495604f2b804d", + "crates/k-wiki/src/adapters/mod.rs": "9fba7cf7de016731656f301a2ff86a7334ed898bced55df89650511422e5ef84", + "crates/k-wiki/src/api/contracts.rs": "019f08b4dad8f1c250b0b01de71f16d262056970979e84dc1c0ae23f45f553ea", + "crates/k-wiki/src/api/facade.rs": "559933fe8aa20ac49582617993a31df620f0e89360151fc3ddc0d53ac0a07bdf", + "crates/k-wiki/src/api/mod.rs": "cecdbbe2ddda8302be2e99e8e95afe32910df6aa367dc3065270d91cb7bc5145", + "crates/k-wiki/src/api/registry.rs": "596fbe985b6e2af9e5cd0b5653baca585572cf9904f9732c7eda9464188ad0d1", + "crates/k-wiki/src/authoring/bundle.rs": "4cc974d441227b576dc0ccbb873ea7fe232e1f87f4ecf01b52fc927357e41e32", + "crates/k-wiki/src/authoring/mod.rs": "739484eaf8e3e02614c9ef74c36508fb6751aafcb3152ecbe68f9bd73862d533", + "crates/k-wiki/src/authoring/page.rs": "4ac9552f9407cc8511c874d1d12e4c290eec933345c51045b0c179a5fe40aaea", + "crates/k-wiki/src/authoring/write.rs": "f5b6822859ca46e37c7121420de749eacdcc454863f12261b48e5fd345821dbf", + "crates/k-wiki/src/bin/k-wiki.rs": "7688bb538d30ed6d3c1aa5147f52b604f364efd52104466233ba71b88ccfafba", + "crates/k-wiki/src/bundle/mod.rs": "89648f228ce2bb1f60106ac451c68c0cfae17d407a7898c4e82e7daa2230614f", + "crates/k-wiki/src/compiler/mod.rs": "ba3be8236266cb742b5432484cc8ec4042e3da47dd74259329008ae25a876365", + "crates/k-wiki/src/conformance.rs": "d70c4149a337268e82d4cb1bbd9cb069c423c3b59f452de9a19f03eab71bb108", + "crates/k-wiki/src/diagnostic.rs": "05d96b05a6c569e7378e6d61ee7ad73e524e60ee0cf29d02537a6be75e2a1204", + "crates/k-wiki/src/graph_context.rs": "23fba237d4b6117ac914965a87504a7ffec4c02b958b887f536f7d86f2293e1c", + "crates/k-wiki/src/lib.rs": "b38e9b31eabda34565ab5be588cdc291d67006204ba27385b02634be1be24c8c", + "crates/k-wiki/src/memory.rs": "8e6402586f2190e536ecb1d6144735a77b08640ef85e554c148d0049cbfbaeea", + "crates/k-wiki/src/model.rs": "37495fe19bdb12bcf4247b06b1cb534891e834cf1b13becade4f917e8bdaef57", + "crates/k-wiki/src/projection/mod.rs": "91b213bc252e2b895f1fef6e70cde41f93f2989bc7a98712d13a6b99b9845594", + "crates/k-wiki/src/refresh.rs": "fa3c1e2199b8ef6ed0a50e8c59d3e5b0819e896ba262673d071f91175d1b1b5d", + "crates/k-wiki/src/render/mod.rs": "72d933e017228db06805568232c3dab45ad9bb2caf9336e954b7442f5ed6e8dd", + "crates/k-wiki/src/search/mod.rs": "09b2dc7d91aee673ff137f1d09ba0a4320a42f39972ea0ebc45b3f6a723914b3", + "crates/k-wiki/src/service.rs": "9aee89282f778101705752e4f597f3b1642b0d92cb464372ab41cc169833a9e4", + "crates/k-wiki/templates/page.html": "9322ef692171d3a94683105d0fdf641caa2e8a597b7c5e8d47ff39906f4c9c3e", + "crates/k-wiki/tests/api_integration.rs": "1bbfa6812a737c07d7f3b566e82125e24aefdd54409eaf83faa1877b0cc04c00", + "crates/k-wiki/tests/authoring.rs": "ccf184723141f0a80c5015d18e467deb70480a57a3da099ffc34207997414f5c", + "crates/k-wiki/tests/bundle_reader.rs": "e823f28854b532e109525c961151164cec00c4f24d391249daa428bd18c44831", + "crates/k-wiki/tests/compilation.rs": "036e0c6ad7a6ecca6158d3b04a84032b1ddf83aec7f9519a0094ddfc91ec7a6d", + "crates/k-wiki/tests/conformance.rs": "2110c007ca8bcb2c77be5f5b2592ab8865d26b422bb4ec2cf0501243f0c27f4d", + "crates/k-wiki/tests/diagnostic_transports.rs": "6340ce2a5d43ac63d947ac44909b69bd9cb5129fd2e3e80600e623eb89520a59", + "crates/k-wiki/tests/diagnostics.rs": "7b6e8cc14482d99a004f2115607da0ad675d9b113b467d194b97ed3081abca54", + "crates/k-wiki/tests/fixtures/comprehensive/decisions/adr-002.md": "ee5c69e25b8f92d32378116a2e42ade139cd4de9f27b8f80c870fd9780c1862d", + "crates/k-wiki/tests/fixtures/comprehensive/guides/index.md": "74050508a412220f57386f01eb0b4371f122cf47b8b5a652a39990fe0a4782fe", + "crates/k-wiki/tests/fixtures/comprehensive/guides/log.md": "4e0876ba1910e475f81c85fe0f821d31e37860edae1d81a72c740b6ce7f978cf", + "crates/k-wiki/tests/fixtures/comprehensive/guides/overview.md": "e43357817795d828af91dee0398c4ecf8ede018147319dfc965e7d5b38638c4a", + "crates/k-wiki/tests/fixtures/comprehensive/index.md": "ec44e48209becae3111c1bd883f218923c6b2d3cc885c017824ba26a460fb5d6", + "crates/k-wiki/tests/fixtures/malformed/invalid-frontmatter.md": "08243906665df2b1bb64ab1beefe56662b1ca220a89711c0b5899ad0ee572987", + "crates/k-wiki/tests/fixtures/malformed/log.md": "d26bf0adce832f845e82a01e6e2d84c00c35383d7e04e86f1ac25ff76f2eda34", + "crates/k-wiki/tests/fixtures/malformed/missing-type.md": "8616868d3786290e5241ae7af997745318573f6dc623163d721708eede778174", + "crates/k-wiki/tests/fixtures/malformed/nested/index.md": "c9d1642dafd370c907cf06ae498162b9447f3648eea2729e862eeb249137a3f6", + "crates/k-wiki/tests/fixtures/malformed/no-frontmatter.md": "9975ae24341afd498f46f74e0e6d03de80b33a857322e4f4f3041a1d717dfd18", + "crates/k-wiki/tests/fixtures/malformed/stray-version.md": "d949cba772124200cb4baa256980447f30189eb65cf46c4f0de8a3fc404753a1", + "crates/k-wiki/tests/fixtures/malformed/tagged-extension.md": "d2cd20fb5604c48279c8d88a5f52e7d83e7ceb5115cf26e5345d8f79badbbfa0", + "crates/k-wiki/tests/fixtures/minimal/decisions/adr-001.md": "cf2c2dcb1b5f1ed96212aec1a1f3f463d9be5b0bcf11639221ced40edb2cc63e", + "crates/k-wiki/tests/fixtures/minimal/index.md": "57294be90498ad361c2156ac3f4986572b672713299597587d7612254819cd63", + "crates/k-wiki/tests/fixtures/multi_bundle/alpha/index.md": "e91e48f91cff58e2249470c36da84313fc043b8b32ba0367d525be78dc7d7124", + "crates/k-wiki/tests/fixtures/multi_bundle/alpha/page.md": "893778b136b09da256a4801c71ada991ba7806e2b1dc3098f5d56e0556cf5758", + "crates/k-wiki/tests/fixtures/multi_bundle/beta/index.md": "cf2d6b5e006222ca29ce1fc532f0331d7d51d22282abb605cd26fb9c8119aaa0", + "crates/k-wiki/tests/fixtures/multi_bundle/beta/page.md": "4b2e49cd21250f1efdba35b787578f147779e9abb3871c1d45c5298133ab83c8", + "crates/k-wiki/tests/memory.rs": "a4b8b4e03c2ed19d6169bab98291f89d81108736d642fb3aec3657fa08949262", + "crates/k-wiki/tests/projection.rs": "6add0a4bbc02401cb5ebd733ebba64d7e47e3c6f97f60d842f6cbc40df2da314", + "crates/k-wiki/tests/refresh.rs": "a8b90c02df7a6ced0ef78b9055ba25fc92cef0f2af2b00d9f1383971114ef82d", + "crates/k-wiki/tests/rendering.rs": "ea6328e283ec169824fad347d446a79e9ef0e4f5d10fcad8664bc862b991b014", + "crates/k-wiki/tests/search.rs": "73cf862aa1e154bb830f43d6d64d237ecd0e7f9924af7d3add37d4a93f58e002", + "crates/k-wiki/tests/transports.rs": "4908e8f16af4e57fbfe56158986d7c4cf807f5bbfdf4ca85203f48ada1959830", + "crates/xtask/Cargo.toml": "33982fd763e2b03e72ae459e516bd76925c7f9a033b539be7b6ec6ce13c9edef", + "crates/xtask/src/main.rs": "a4eb81730661eddb10a133c3c106c492cd611b5d7b827996edcd81a797e713e9", + "crates/xtask/src/release.rs": "dc74317037a68df27c98c24678d2b46589ef64fc1ef0d484b415cae33435ebd7", + "crates/xtask/src/release/workflow_tests.rs": "549e2b6cfabfe0e60717dc186ecf94b1a3e598d7ee670857460f1f3bb1b59861", + "docs/assets/codebasegraph-map.png": "fc11a221d1a64ee9fd7c82d1436a11516aff9b6c0a7f4d0c8cb516417b5de328", + "docs/graph_output_token_comparison.md": "da24d64e7e78edcd74ff0d8c76498af037740e3caf8c71fab092127fac08d332", + "docs/k-wiki.md": "a88834489a890251912da9153df030eed7577cd71382c8d1af15b85d8e377a8a", + "docs/mcp.md": "6267772875d91ce2c63840248933b1056ad0f175d91351e7e057e07ea05ceb83", + "docs/readme-ux-rewrite-proposal.md": "cb6b3d736a571d3609e87b0bdad3f935afa7ba7355c743a1cb0bac43059a99ed", + "docs/release.md": "e7456beda70e58b2049ed13f1484731544a9fb89cf49d8bf0f7d2435723d02bf", + "docs/testing/graph-disk-cleanup-evidence.json": "8cb9670ace63fc10d02447c4f612f306242a060e7d1882e4992cae7ba6347d40", + "docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json": "c750c93245f79df2379200a4a43fb8b5b94c48a7d351b22da1eefb8e05bcc91d", + "docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr": "37deacba5b2b624d0962949aa5cb60ce368a873c072cfff764cf836bb098a66b", + "docs/testing/graph-disk-cleanup-evidence/audit-regressions.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/audit.py": "316630c38e1638370b8c1873174d4311cc32ae4d0d9bcbbf0045e1083ccfb518", + "docs/testing/graph-disk-cleanup-evidence/foundation-reports.json": "88eff4652ee0d4914980912c2a9600481bb891bc539706fea1677062133b3bb8", + "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regression-execution.json": "55763bb75fe573cf97fbe8878687d8e12c30836fa6ee991ee7b7a71451779611", + "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stderr": "e4ed00080d9030eba59f6c9af6ac3d6b784e69ad1e9dcb2ce9107c19fb07be0d", + "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json": "0dc7bd221b6d210f260677b1c2027b707a3bd0f8a0a314d35eefb64d0402e151", + "docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627", + "docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627", + "docs/testing/graph-disk-cleanup-evidence/review-outcomes.json": "bac2d4ad1d32aa54f709c6ccfd34b4a3b5917f0822d3524e1b8f70378cc5ccbe", + "docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml": "43789b2717a390d201e486c0c1def0e0f7aa8cfe3548a61fe2ee4fc8546e0603", + "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876", + "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml": "ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92", + "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json": "65d9f8a5c1124a215f66e63f5819f60aef52382559cb627d807ce7a7d5bab718", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt": "ffd58ad5367a31890d30ae14515523e74e76c810742d011f028c46a4729ae6ed", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json": "95f89f93b01ad3c9b02cbab6b97278cdbb14d604080963a4a2b8a08deee39c5c", + "docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt.gz": "9deb649f914ef65bc0ad3dc7632409c2b4d4615091c8ee74781b4e390abf806a", + "docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml": "8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414", + "docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml": "c4d0cbb89af4996d859c7da5b1bed3b04a1a9b90dd44135bc7c9d2d2c9ecfb3f", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml": "3b8c4e663c2c93d3c52f93da6af4b17096b529deff117ce4f9b191ad766e44b7", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json": "c2dd357024899ff4a64e9d4de15f181a4f9b827e460e509d2ed7e8b1a09ba3f3", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml": "8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6", + "docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json": "ea74ad1208875efa0e56d2f168bcec49c409cfe9219387e8e0b3f85a15645f86", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr": "38b1121bacbb008a468e084a6453f3b81fb7972fc81e6670176e212381b8aa3e", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json": "a0dbc8b2f618b3e0c2157cdbbdba9e6a58676e8b02d5210e75263d33cb287651", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.json": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stdout": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr": "df966e27aff804782c1143e8aa9325367cfb66ead53b60fdb86348e6175bea1a", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stdout": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr": "3789ba8ed22ae4a82b1fc7adaea6cd8d86d3c94beb1f30f698bd4afb3f7e6ff0", + "docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr": "d75191f480933414ac1b27956d159e407be16eecac57c0d782ec127207ea333e", + "docs/testing/graph-disk-cleanup-evidence/s5-narrow-audit-fix-checkpoint.json": "330a275563716dcd528c145b9ff785ac7ab686088a86572d1379f11aa9d278be", + "docs/testing/graph-disk-cleanup-evidence/s5-narrow-checkpoint.json": "47dd5518ed1d1cc5dac128acda06e2431ced50586af17fd79bc27245861f1db9", + "docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json": "ff7043693f6cd3bf978921262ae7c9e83ad4772bf760d1db777f169f93694600", + "docs/testing/graph-disk-cleanup-evidence/test_audit.py": "b317ac2a5f4ab7ec3c52e98169e37090d65c9a7a92d397bd898962516f6f545c", + "docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-admission-reproduction.tar.gz": "0c8f21fb29f1137b5f509aa0a904d2ae441729bb935a1e02aaed8d5c8d80a5b9", + "docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-registration-fix-verification.tar.gz": "9141fd2c55d0a8b074f6e0b324c4984c5ba0e429c6dda15a7160af01aef1b722", + "docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-junit.xml": "698d41772c871f1398e5cf926c415a93af41ef65bd73b298220323266a7e761f", + "docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-verification.tar.gz": "3703829e77e3f4d2f47b64b40e187bbda7b3e66b7628b1e73f5f7ab0757b2a74", + "docs/testing/graph-disk-cleanup-foundation-evidence/integrated-fix-verification.json": "5fd30a710e42520ee3732b73470870687f8511830cca98ce2e218062abd3279e", + "docs/testing/graph-disk-cleanup-foundation-evidence/integrated-foundation-review.json": "f3286fa16726d54717d3fa90d79ed817e86878af3a6bab13f416462d9ffe9549", + "docs/testing/graph-disk-cleanup-foundation-evidence/integration-binaries.json.gz": "2a9cf8f1e9abbcb72dd0b84efa3dcc3fc7e670d780dff63603c2b5fcfd0046b0", + "docs/testing/graph-disk-cleanup-foundation-evidence/integration-check.log.gz": "d8568eedc674aabe90e1a136722e71275602269e4132c7a90654ccb28c563911", + "docs/testing/graph-disk-cleanup-foundation-evidence/integration-junit.xml": "3562f1cb18f27b76ecd94f3b71770d2659b91836e24c37f9bebc7c1c2adb8e76", + "docs/testing/graph-disk-cleanup-foundation-evidence/integration-tests.log.gz": "aeb4eca36c07170d46718e713a6b6a568b2786295a3ede992dd802a41d3aa221", + "docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-independent-junit.xml": "640d1dc448e77ea726b0468255a488c9badce39a955176e7fa63158263fcdc76", + "docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-rebuild-provenance.json": "acef411c4c6d5a68565001d23d69da28bd483350e1ea74dfc64a15e98fbbfdc0", + "docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-independent-junit.xml": "3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216", + "docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-review-evidence.json": "6101c50b30b24228d473ff86ab98c4d6d16128fd8e98195705df595ea5d22729", + "docs/testing/graph-disk-cleanup-foundation-evidence/s4-reused-target-excluded-junit.xml": "ed70ff0327920ea194f4df6fd76ceac547cd67db2ccdc03b96c06f8c9c4d408c", + "docs/testing/graph-disk-cleanup-foundation-evidence/unfinished-work-and-scryer-source.tar.gz": "b938db71e07857f73dabfc852c94e6006321caf0dc360c3cfbe0250423066294", + "docs/testing/graph-disk-cleanup-foundation-integration.json": "f33d2a4410e0c2b6785c14bda9330f878973827751fa65e3f4521c98a46168d0", + "docs/testing/worker-recovery-evidence-2026-10-20.json": "a74d172ee20714ab3d61341b04cde56acaee60e84091ef660868ba046f4227d0", + "docs/troubleshooting.md": "4ef9e29cb7edac9d1918d9dfe5e7316fda4a4a322c2b4c313c21d6854b74e2cd", + "knowledge/architecture/agent-loop-hooks.md": "fa6d63c3202ff1e113028c7a8567293410599ac6b3307bd7a1cd90f2bbe86029", + "knowledge/architecture/agent-memory.md": "54c5a3591a4f96c1ad3f100f8dad1529bccebc437fdc6cedb16118363e141527", + "knowledge/architecture/graph-disk-cleanup-contract.md": "ee96ef50c37f98126a4e907b70317a413d3ac5a2813f3d60f85d65fc7ad3cdd1", + "knowledge/architecture/graph-disk-cleanup-spec-reviews-2026-10-20.md": "c47a3e6be27346ed997cc5b43ef7433f4251ef103005fc06af1f8659a1abc9c7", + "knowledge/architecture/graph-disk-retention-review-2026-10-20.md": "a3af205590764a958631ee9bbb27e82a056b11648f7278c1fb45eda785e81bff", + "knowledge/architecture/graph-freshness-recovery.md": "08f20247d9c730a247179a66787bebc9efb540feff4cc6cf3e82cbbf631a6872", + "knowledge/architecture/graph-runtime.md": "f32d3ac114ee6fbe0f3303ef35ad239d9ee633bbbc8499474a72d4ec65911bd4", + "knowledge/architecture/graph-storage-lifecycle.md": "953eebfcdca3fec7e0d520767f0d09ec6c465d6ba4f4411e255f2d0a6c9a8353", + "knowledge/architecture/invariants.md": "778c4fe26697b8947f0b08c7ec70f85ba5c1718656002edb9ac8a77d922b015a", + "knowledge/architecture/knowledge-wiki.md": "b1738e07955e3529569852c0a7f7ae1014561ca43a39cc04c92cf6d65c74885e", + "knowledge/architecture/language-css.md": "da78f20e76ffe519161cd6d8e0c13df113ad9ee7508ab9c6512026bc82d25a15", + "knowledge/architecture/language-html.md": "2e2b1a2a1997b81c735361d8acc72027c1fe919f15ffba443bb92e5eb70ed512", + "knowledge/architecture/language-javascript.md": "06e7603156ee08348414c0b903dffb000805bb2c868fc3fbb77d974cc33259db", + "knowledge/architecture/language-typescript-tsx.md": "d6520c8d3d01e5681c8984f6d610698b4a49c1c88b73b026936ec9dae62bf46c", + "knowledge/architecture/language-webassembly.md": "e5f2308d5c7fef6009e335a7b9b3cfa9b77bc8ef8dbb61760d0dedfd5a55b3ca", + "knowledge/architecture/materialization-pipeline.md": "99a5056c059cd9c499897845c26a15a4dbc460729cebed0b0b22f31b22f4ade9", + "knowledge/architecture/operation-paths.md": "f700053df572091aed938c64bfb63c3050f99d312843d5908c22f5518b283657", + "knowledge/architecture/overview.md": "3710c89eae9a6d3f30899b0f1b9e4948c4b9fb09bae34962f507ef9a85dd7e78", + "knowledge/architecture/release-verification.md": "099f8dee41675237965ca1d54d61a506de96fc3ffbcd674dfa97a4552d32b649", + "knowledge/architecture/repository-map.md": "ad0273bd362d37346e16b8fd446a65d62462f3ae7b5ce1e502f5e44961d1c0f1", + "knowledge/architecture/worker-recovery-regression-contract.md": "ade6d75a85a272e993b3e5752def9307782915a9fd40843f82eee6061f49632c", + "knowledge/index.md": "87aece4723c5682ea96f4569b0b1813d48ae6c59d285ebd9c8a42c9be211a474", + "knowledge/memory/episodic/agent-hook-live-mcp-budget-eagain-2026-09-23.md": "e1bfe57c3611584a4355357ebf9836b3b16a07b3cbfc1b39cc725c75eeaf2cb3", + "knowledge/memory/episodic/atomic-temp-path-process-scope-2026-08-21.md": "873044c1ded70c01d5e048de1d234a37d391a2927e18136da2ac6216be535027", + "knowledge/memory/episodic/ci-release-artifact-recipe-drift-2026-08-13.md": "8abc08782c4de0b0732d3e17d83389f936b161cb72ed9071f4424a4708491811", + "knowledge/memory/episodic/config-root-loss-stops-refresher-2026-09-08.md": "25749a43e8d29514164f3a0b9ea3a0aaa0873a02f858a3b29dee6b22f81da84d", + "knowledge/memory/episodic/containment-proof-must-survive-candidate-cleanup-2026-10-15.md": "34c176cc6f641df51959f04d0694cf7ead4abbcefb218cac533bb4796d1e80ae", + "knowledge/memory/episodic/coordinator-frame-timeout-retry-2026-08-21.md": "80bf6d574cda7e7725b517fcd5d2d9d48e48fa79bc72d050bfa53e456d5e1ecf", + "knowledge/memory/episodic/coordinator-stale-route-authentication-recovery-2026-08-20.md": "cca5202d3efd03cedef7fa09ee3e502bbf78e224cc101303042b99c4cd2455ba", + "knowledge/memory/episodic/coordinator-watcher-readiness-race-2026-08-21.md": "19825664cf3499746857f6f02edcced26605a03755b054e09ef5ca048cb8b41f", + "knowledge/memory/episodic/copilot-cli-native-hook-payload-omits-event-2026-09-22.md": "32345f16b266bb7f2cb9f2c36e908888fea928837d2f8d216b9379c15e3fea58", + "knowledge/memory/episodic/database-use-unicode-alias-parent-family-2026-10-21.md": "01d6d14432a32b2bf3f43244a5b8811426e1250df7d93c65837543acfb50a84d", + "knowledge/memory/episodic/direct-destination-spelling-recovery-identity-2026-09-08.md": "ed15cd682e7725d2a0366ecc883ea889fc688b9516c2b4d5ba051186759b7d95", + "knowledge/memory/episodic/direct-prepared-replay-deletes-promoted-sidecar-2026-09-23.md": "b792bbcc60b629ce871d350b77455173bf147ac1e84d6c9bcf1bcac428d279dc", + "knowledge/memory/episodic/index.md": "98d6b26ae479e29b0253cf2de40ac081bb4fe08f27ea07efd64fd76a8c2e1308", + "knowledge/memory/episodic/k-wiki-domain-errors-need-wire-boundary-regressions-2026-10-16.md": "86a67e567ff6299810850668095411b5ca671ab4050f1e5393af1d821b2e4356", + "knowledge/memory/episodic/k-wiki-yaml-errors-need-origin-redaction-2026-10-16.md": "dd36fbe95e9edade8fdaf983549f121dbb4d3a3c489d7547b75b3025fc1b9db1", + "knowledge/memory/episodic/lifecycle-fixtures-must-recognize-retired-startups-2026-10-16.md": "a9a22fc2bb20309451aac96df351748b4c6226bec7c503b0ab7fd18364391b3d", + "knowledge/memory/episodic/macos-current-rss-materialization-supervision-2026-08-18.md": "96013fdf89242f107a8b506e883dff0ae037d0b5d3294e461857b8cd59a50267", + "knowledge/memory/episodic/materialization-reference-memory-gates-2026-08-18.md": "de4ef170b04a3e979f37c5242f39e74d549b3916876d97bd0069ad0facef75ef", + "knowledge/memory/episodic/mcp-daemon-client-disconnect-broken-pipe-2026-09-22.md": "c3c46bbb6a371af191fa82a7f2b113c9dec73fd90a20fa96b75cfce604c48fb3", + "knowledge/memory/episodic/mcp-refresh-rebuild-convoy-memory-overload-2026-08-14.md": "b6d8d43d95a6a106ed8d171522593bbb8c3cb265323d7d00ab5f604e3de01ff0", + "knowledge/memory/episodic/native-startup-backend-label-ci-2026-09-08.md": "6f51c9792ca69a08f4ae5b0b0026d083da270740c35e843a8aa0904490d1da03", + "knowledge/memory/episodic/native-wiki-smoke-needs-recommended-metadata-2026-10-16.md": "19616adf6edc905ab2ef5c1946d6435cc76205adaefdc41831eaabe6281fb056", + "knowledge/memory/episodic/recovery-fixture-caller-and-handoff-preconditions-2026-10-20.md": "1b1d95b8a78540f0d7cfc017a6eeec9184136b3e25d23c15181ef2c89626dd1f", + "knowledge/memory/episodic/recovery-review-branch-and-live-version-alignment-2026-10-20.md": "d44b6c2c037b34f29903b62d907e7ba55e34d627a4430bd5f9405c941b15d681", + "knowledge/memory/episodic/slow-reader-test-waits-for-response-transmission-2026-09-24.md": "16992cc061c05a114d0fab558ed0b86283767b0611d2903cf4d0281417945e26", + "knowledge/memory/episodic/stopped-http-daemon-transport-send-error-2026-08-24.md": "0ec06e9cfc462cf20f6c264905db3943cfb8ea4635d57c29feee8043f97792a8", + "knowledge/memory/episodic/successful-release-merge-starved-by-main-tip-guard-2026-09-23.md": "2af2037c6f0ff099b602fe17193db91233e24d0bc6ed15975879b97a730d4f02", + "knowledge/memory/episodic/untagged-release-pr-blocks-later-proposals-2026-09-24.md": "efdf88a699dc3543ba8acae4c2912d1227cbf978c56f451f158daf6ce705aa24", + "knowledge/memory/episodic/wiki-recall-utf8-snippet-panic-2026-10-01.md": "c323039532274caf784ffa6c0eaffc8f15cd60386427cf0d8ee3427d0961dee3", + "knowledge/memory/episodic/windows-direct-materialization-path-limit-2026-08-13.md": "689e41a8df9ae3d8d8eb1e2afdc5a89a1a8116f071a2b7670a2dd317e80d93af", + "knowledge/memory/episodic/windows-emptiness-probe-must-not-terminate-jobs-2026-10-15.md": "fb8f2f903d100211666c0850a600d6b3e8286c369dffbdfe936a5af69aeff94b", + "knowledge/memory/episodic/windows-mcp-daemon-state-publishes-before-api-readiness-2026-09-21.md": "e7db20c695cf90435fde23e54f4c113bb0ff38c8097154e5cc95926e8d9d2148", + "knowledge/memory/episodic/windows-notify-verbatim-watch-paths-2026-08-21.md": "fc1c15f94a6beac896dbe4fbc0c7851172c7307435c0bd2c0383d5f54c00cc78", + "knowledge/memory/episodic/windows-watch-test-marker-lock-2026-08-20.md": "5a7c02c054f3c6b1700e65ecbf559af5099736022ab37469a81e19bdd71ab078", + "knowledge/memory/index.md": "7838cd3370acc8f93714d31822cae464af95c2de077d31d69fa6909d27dc1d19", + "knowledge/memory/procedural/agent-hook-lifecycle-stale-ownership-and-daemon-2026-09-22.md": "a79c40872b1aa67ce79b221325fb2d818d60020a8d49af336ec523004c816a64", + "knowledge/memory/procedural/agent-hook-lifecycle-transaction-and-ownership-2026-09-22.md": "5bd6caafd6776e60d03e195882be02b0fd1e410aede45919c8d5abfbf8fe7b63", + "knowledge/memory/procedural/agent-hook-verification-per-event-and-portable-temp-paths-2026-09-22.md": "fc668276ba53c62ea1d5c9bfe29a1dd1f7be5422fbb10ced0f1e00305f4cd248", + "knowledge/memory/procedural/cargo-package-ignored-metadata-clean-worktree-2026-08-25.md": "04e53b15116be3d12b57ee66b941d3edfe523075655509561c78eb53761f283b", + "knowledge/memory/procedural/checkout-free-gh-release-repository-context-2026-08-17.md": "00f0df984ad6a6401f38f8d5fa5cd34386274c85cde44b511fee73b200607c43", + "knowledge/memory/procedural/crates-io-package-size-gate-2026-08-24.md": "3ec14615163e2ed7ebbcd06b8f9e36428a49066f6f501ec2b8bb6233dd8bdd3c", + "knowledge/memory/procedural/crates-io-publish-idempotent-retry-2026-08-24.md": "1185a270b17580ff4f4b2f2efc6e1c90ab063de4d5f8825142fea221c80b4974", + "knowledge/memory/procedural/exact-commit-release-publication-and-recovery-2026-09-24.md": "96659e7bb666dcf239dbb18fe49e6aabe514dbae79a6a864a3a65b1db8147a36", + "knowledge/memory/procedural/index.md": "f90a5641d854a85107a58e99505c7b01db844cf895a291825c66d85bea760546", + "knowledge/memory/procedural/ladybug-0183-link-and-extension-alignment-2026-08-14.md": "8c12286fb0e657ef282794491fcd7a1dd83f6e8fcd2d2cbb09c3d1a98243cbed", + "knowledge/memory/procedural/ladybug-macos-x86-compiler-runtime-2026-08-14.md": "65f3868ea257af1e98d29e5a4f2a92c93e72ad6291ca75904623f0257bbc9a33", + "knowledge/memory/procedural/ladybug-release-extension-cache-variants-2026-08-13.md": "adaeb22ab532a90ad3305150b2f6d67c06158561eda9146821f79c694e7d0150", + "knowledge/memory/procedural/large-self-hosting-rebuild-memory-budget-2026-08-28.md": "55a2284660069450bb37c7ba4b9c518a1aca1e731817d80ceaa9daf60eb54bef", + "knowledge/memory/procedural/mcp-restart-session-aliasing-regression-2026-09-24.md": "8acf4b3da1f8ba705517e0ee975c3ad3cbcc2101f89c99e8e45b0b6536140215", + "knowledge/memory/procedural/refresh-context-metrics-clippy-2026-08-17.md": "4ac71e4ec991f32706563edbca2e798ee686165273fdf520cd1a92a6c4e88352", + "knowledge/memory/procedural/release-please-stale-pending-tag-guard-2026-08-17.md": "2eca810128ef6ba7632cbb7dabb5b7f1f8592685798c498e943d201c38a1c541", + "knowledge/memory/procedural/release-publication-skipped-ancestor-chain-2026-08-17.md": "57d7a0c9d0b155decf9923158b2604983592aaee0dd0a1b18f806b0396b98b7f", + "knowledge/memory/procedural/rust-modular-code-procedure.md": "01d0bbcab9943839ffe38712fa697b896dc075ac53e533451c190537a635c0d8", + "knowledge/memory/procedural/rust-performance-memory-efficiency-procedure.md": "9659831c4a9111858ac326818e94cd2247f97c3b1f0b02efe41fa535d4821291", + "knowledge/memory/procedural/rust-secure-coding-procedure.md": "7a81bc947232c400fde58e53f77174f8c3d49ba77dd216d9ef4d1fc2ed237495", + "knowledge/memory/procedural/shared-cargo-target-frozen-binary-provenance-2026-10-21.md": "1cddca5ed7337fb0ffb24626cc21a621fbd5339526e1c1e275a7de88b2594e5b", + "knowledge/memory/procedural/shared-http-daemon-upgrade-requires-registration-migration-2026-08-21.md": "bb0d2d4241670411eb82fab1a14aaab667d79c726b9d94a08f55a5ff165debd8", + "knowledge/memory/procedural/stable-nonblocking-hook-cache-claim-lock-2026-09-24.md": "cd7aeca0b0bbb525bfeb55e0276b7aa981a740b72973e41c9ea731010a35e431", + "knowledge/memory/procedural/vendored-tree-sitter-scanner-callback-contract-2026-08-25.md": "bf4df0f23f902bede8db064fb0036a9f0199290689dc8601204e9aa2a4b6ea02", + "knowledge/memory/procedural/windows-ci-bundled-extension-feature-2026-08-13.md": "01460b3810257f59424363ffe017a3811f4865a831e851acf322441c32774afe", + "knowledge/memory/procedural/windows-managed-run-cleanup-disappearance-race-2026-09-22.md": "b4b9fe1959b68837aa11456b6dd7e2e5eac85310e6992d52f1137ec1f527722b", + "knowledge/memory/procedural/windows-native-lifecycle-test-profile-2026-10-15.md": "9bf1f41d57a7f47ad9eca3c63f13bd9f66e04611aebf0172618a909bac6699e7", + "knowledge/memory/procedural/workflow-run-release-exact-run-binding-2026-08-14.md": "9efd65d18f6aac69fdfde9724ff963ff34c2eae61bc59fc6c778bee461c0078f", + "knowledge/memory/semantic/coordinator-predispatch-only-replay-2026-09-23.md": "d11eaa95e5980ae1c1628ca15e2ada897b391c1d2151f7121ba8c92ba1495f30", + "knowledge/memory/semantic/index.md": "1500ba656dab6f82f12a492f47ad8f62e09d05dbbec31f919dce65dd89ed9889", + "knowledge/memory/semantic/k-wiki-graph-layer-contract-2026-08-13.md": "2acc8b852dc645901506445c260a07510b27f293ff4f5dac4871b5c93697f61b", + "knowledge/memory/semantic/mcp-coordinator-worker-ownership-boundary.md": "bebdc0b1a199f9aa6f055b430510bf58d25508066cee03dba7dcaf739fefb655", + "knowledge/memory/semantic/mcp-stdio-no-idle-exit-2026-08-20.md": "ec403397fcaf44e7609ac9b67780f3df7dd400bf1c1446bfb79283b02fc94665", + "knowledge/memory/semantic/raw-artifact-reuse-rebuild-boundary-2026-09-23.md": "8819ede99354fc445ebe3fb5fdce4f2914b2f0af0fe025ebc54bdaec256fcac6", + "knowledge/memory/semantic/refresh-status-dirty-epoch-foundation-2026-09-08.md": "c65da2618578cc251befcc630785e6ad0f5b26be3e46391d294074cd33fa0c49", + "knowledge/memory/semantic/semantic-artifact-reuse-schema-upgrade-boundary.md": "ced98c016647c22d94ff8dddfb7f79713345bcebcc1b26ff4de21381fcd7ccad", + "knowledge/memory/semantic/shared-managed-mcp-http-daemon-per-repository-2026-08-20.md": "ab4669c486bdd7c0d3cbff612999a66927196ef0bddc4abe0917ca6910155b6c", + "knowledge/stability-backlog.md": "b70f603576e685282b7b454ec9039f833daeb780cc0b0992b256550cf13b698e", + "release-please-config.json": "56ef4e6d7afada2d1db5747986d928dca733d19f85094d73a2e84cc081b6716f", + "release/install/install.ps1": "d91997cffc0aadde95e30f57a180b864685a3cc3b9919a903dc0084809d6ea68", + "release/install/install.sh": "ce8a9dee18939fa14cec78ba6c30c99ab2c6473158666756cd4ecb805d830fb4", + "src/adapters/cli/agent_hooks.rs": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "src/adapters/cli/dispatch.rs": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "src/adapters/cli/format/help.rs": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "src/adapters/cli/format/mod.rs": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "src/adapters/cli/graph/mod.rs": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "src/adapters/cli/graph/options.rs": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "src/adapters/cli/install/command.rs": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "src/adapters/cli/install/mod.rs": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "src/adapters/cli/install/options.rs": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "src/adapters/cli/install/verify.rs": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "src/adapters/cli/materialization/command.rs": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "src/adapters/cli/materialization/mod.rs": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "src/adapters/cli/materialization_input.rs": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "src/adapters/cli/mcp_command.rs": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "src/adapters/cli/mod.rs": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "src/adapters/cli/reinstall.rs": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "src/adapters/cli/setup.rs": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "src/adapters/cli/tests/dispatch_materialize.rs": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "src/adapters/cli/tests/fixtures.rs": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "src/adapters/cli/tests/graph.rs": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7", + "src/adapters/cli/tests/install.rs": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "src/adapters/cli/tests/mcp.rs": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "src/adapters/cli/tests/mod.rs": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "src/adapters/cli/tests/watch.rs": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "src/adapters/cli/uninstall.rs": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "src/adapters/cli/util.rs": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "src/adapters/cli/watch/command.rs": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "src/adapters/cli/watch/mod.rs": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "src/adapters/cli/watch/options.rs": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "src/adapters/cli/watch/output.rs": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "src/adapters/mcp/block.rs": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "src/adapters/mcp/daemon.rs": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "src/adapters/mcp/dispatcher.rs": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "src/adapters/mcp/http.rs": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "src/adapters/mcp/mod.rs": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "src/adapters/mcp/options.rs": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "src/adapters/mcp/protocol.rs": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "src/adapters/mcp/refresh.rs": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "src/adapters/mcp/state.rs": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "src/adapters/mcp/stdio.rs": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "src/adapters/mcp/tools.rs": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "src/adapters/mod.rs": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "src/agent_hooks.rs": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "src/api/boundary_tests.rs": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "src/api/catalog.rs": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "src/api/catalog_support.rs": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "src/api/context.rs": "b038192e4cc8258c94c7c524f9ed565017e45d7e28092b09e5490d4836db0eed", + "src/api/contracts.rs": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "src/api/core.rs": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462", + "src/api/execution_context.rs": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "src/api/facade.rs": "51230173cd6ea2767f1ec95306f3d3d2c42a8b07b20e56ec3c7661cbb4b96775", + "src/api/graph_read.rs": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b", + "src/api/lifecycle.rs": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "src/api/materialization.rs": "446e276c15cab8f5a35ad48e2df77ba52475812c904ef04caa545ef216622651", + "src/api/mod.rs": "3e2b72f33f55a3670764ecde9d90c844b6cb8d5d59adee131aaef2dd79dd769b", + "src/api/normalization.rs": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "src/api/presentation_support.rs": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "src/api/presenter.rs": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52", + "src/api/refresh.rs": "1cebe4ab394d446e4c2b99c34447182b8b2ac4135612040ffda200eefb3ac595", + "src/artifact_store.rs": "8a248d0f2a9e33b6b22b75548441de661fc8413a346fe0bb5512d85b1cbfa598", + "src/bin/codebase-graph.rs": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "src/bootstrap.rs": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "src/coordinator.rs": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e", + "src/coordinator/tests.rs": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252", + "src/daemon_service.rs": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "src/db_writer/access.rs": "104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d", + "src/db_writer/extensions.rs": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "src/db_writer/mod.rs": "519f8c9c5f0482d0ca4d9cea6430fe831ea753b3a13b7ba5af06499f91a0f81e", + "src/db_writer/phase.rs": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "src/db_writer/request.rs": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "src/db_writer/rss.rs": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "src/db_writer/schema.rs": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "src/db_writer/tests.rs": "fb00bc867d9aaeeb903c62bae36c6d08bf20726c61efd41b726c053e0f3ff1f2", + "src/db_writer/write.rs": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "src/error.rs": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "src/execution/mod.rs": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "src/execution/parallel.rs": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0", + "src/execution/plan.rs": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "src/execution/run.rs": "f2b7ef995dd0abdb4a927a3b2a20b9ab39d9f74c238711d1ab7d806c3ad4ed44", + "src/execution/timing.rs": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "src/hash.rs": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "src/lib.rs": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e", + "src/materialization_worker.rs": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496", + "src/mcp_client.rs": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "src/normalize.rs": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "src/parser/captures.rs": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "src/parser/fields.rs": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "src/parser/markdown.rs": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "src/parser/mod.rs": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "src/parser/tree_sitter.rs": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "src/parser/wat.rs": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "src/partition_builder.rs": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "src/process_lifecycle/mod.rs": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663", + "src/process_lifecycle/unix.rs": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d", + "src/process_lifecycle/windows.rs": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8", + "src/profiles.rs": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "src/protocol.rs": "c56b04d18060e0ae2ce43e4c9d5b59535078d7186bfcac819005d2605ae01b3d", + "src/scan.rs": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e", + "src/search_index/build.rs": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "src/search_index/io.rs": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "src/search_index/mod.rs": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "src/search_index/model.rs": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "src/search_index/read.rs": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "src/search_index/tests.rs": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "src/source_selection.rs": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "src/staging_writer/accumulator.rs": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da", + "src/staging_writer/files.rs": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871", + "src/staging_writer/merge.rs": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "src/staging_writer/mod.rs": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "src/staging_writer/result.rs": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79", + "src/staging_writer/rows.rs": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "src/staging_writer/spill.rs": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff", + "src/staging_writer/tests.rs": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "src/staging_writer/writer.rs": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "src/storage/atomic.rs": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "src/storage/containment.rs": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "src/storage/direct.rs": "c40e99468c2ab0a465b7a5b5ee2a8e755c872d450da5e88cd212d91365691e25", + "src/storage/inventory.rs": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958", + "src/storage/layout.rs": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "src/storage/locks.rs": "c6296037d04a0120cccdc405052046355d721fc2255929c7bace0e4d1a2def2b", + "src/storage/maintenance_ownership.rs": "6566a1cd2f86084f2b4549e499d93854bb8e8c48a15794e9085b7d407ea21d06", + "src/storage/managed.rs": "aee08a9c6f09640bde3d2d0a3b50690391e294d308561544e94079c9abe2041d", + "src/storage/mod.rs": "1549d70674632db6b7233f23f0789055e162c224cf1625f8f15cd8b208bb3eb6", + "src/storage/run_workspace.rs": "120d4c61691a97aa4c9bb4c29c2487a3a2b53e45a63ce5288e1f5a0e587eb877", + "src/storage/test_harness.rs": "3d94ecbe47000f8dcc77f0f2dff3a7ec3c0bdc5087b0ab3e01b3d78d1b5cdf58", + "src/syntax_materializer/arena.rs": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "src/syntax_materializer/builder/edges.rs": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "src/syntax_materializer/builder/mod.rs": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "src/syntax_materializer/builder/nodes.rs": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "src/syntax_materializer/builder/semantic.rs": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "src/syntax_materializer/builder/traversal.rs": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "src/syntax_materializer/capture.rs": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "src/syntax_materializer/ids.rs": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "src/syntax_materializer/mod.rs": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "src/syntax_materializer/relations.rs": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "src/syntax_materializer/rows.rs": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "src/syntax_materializer/tests.rs": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "tests/agent_hooks_process.rs": "364d029cd0d4daf328ba46a1254e1d9a6f5df4e7fc9b0a86bcbf7740b6030478", + "tests/auto_repo_root.rs": "09d5d09635409c18b2d9b1da8a2272766b4fa5be63d93efd3aa4fdaf0f93fc76", + "tests/coordinator_process.rs": "f58387496162899a4611dda84781c98d87e6252c61844009b7ec7def4b64844c", + "tests/generation_storage.rs": "17e4ae34bc255345158f879f48dd6a8124ad07722ff75b8fff3f071dce70e6ce", + "tests/http_admission_process.rs": "3dd5533bfd85467027710b37d77be2ebfc84832d23942f3a5398c335b151dbb9", + "tests/http_daemon_process.rs": "fc651df00823b1d3f854d6627dc80c0d10227d78597c1adab8e417d8e0a55d46", + "tests/native_request_identity.rs": "f624bd73ba6b282f415a6251edca1c8d0e85a7d9bcacad1fe2d020e732c74eb9", + "tests/refresh_recovery_process.rs": "158e6795af9b9dd0b812f9ae7df579694c15238b4a0de5c36cca6a603fc63b5c", + "tests/storage_maintenance.rs": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693", + "tests/support/lifecycle_faults.rs": "c075bf1d9f247920d5052d607de2a42b2c2f76fc0cbdc120f846b42b8cf581f8", + "tests/support/storage_maintenance/mod.rs": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09", + "vendor/tree-sitter-wat/LICENSE": "28a1c2b92a241a705632869e5c3a5e83a53bb182eb273c851565ae381f12982c", + "vendor/tree-sitter-wat/UPSTREAM.md": "edb5f2e65b8131e5ac0f6081e4fa7b8e271034cfe1f8f6577f8f70a7a75ce225", + "vendor/tree-sitter-wat/src/node-types.json": "b8ee14131666f1553d8948fa661526b2aa5d4bc5f64aa5f28f662fb33ebf453a", + "vendor/tree-sitter-wat/src/parser.c": "ba3659413ddfa7acd66d1739ca96ace762e6d08f2c60a43d22670503fac87ec6", + "vendor/tree-sitter-wat/src/scanner.c": "e6197b66843756e44e67ff6e9f61790b7df44c930ed1ba5b5fc1169874fc6d60", + "vendor/tree-sitter-wat/src/tree_sitter/alloc.h": "b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea", + "vendor/tree-sitter-wat/src/tree_sitter/array.h": "5bdf6ed1a78e3409fd443e085ca967a64c188a5d082aaf7f819bccd53a471c94", + "vendor/tree-sitter-wat/src/tree_sitter/parser.h": "180b893c8734778fd32f372dfbc27bd6ad1cd2221f26150b31256ff6716320d2" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review.md b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review.md new file mode 100644 index 00000000..5522dd84 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review.md @@ -0,0 +1,9 @@ +**The canonical Windows-prefix P1 is corrected at source level. No new confirmed P0-P3 finding. Native Windows qualification remains pending.** + +Independent GPT-6.1 Sol Max reviewer, adversarial-review-engineer. Frozen baseline `88a369f5820c7c21e05a4336f061887cc5f5d52f` plus exact patch SHA256 `c0d22b8bcbbdd6b838dac8506e29cf1dd698b87f924a22b1152df622a806211b`. Five expected file hashes match, including coordinator-owned Cargo.toml Win32_Globalization feature. Own reconstruction: `/private/tmp/cleanup-s2-windowsfix-review-s6o1lmgl/repo`. Historical findings and Darwin verification are preserved unchanged. + +The validator now accepts normal Disk/UNC and canonical VerbatimDisk/VerbatimUNC namespaces. It preserves supported extended spelling exactly and rejects ambiguous extended tails instead of stripping namespace prefixes. One absolute owned path still feeds both guard and native open. Full absolute path encoding is checked before coordination and again immediately before native open: ASCII or effective FileAPI code page 65001. It reads AreFileApisANSI plus GetACP/GetOEMCP without setters; this matches [documented ANSI/OEM FileAPI selection](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-arefileapisansi). Installed windows-sys declarations and manifest feature were inspected. Actual Windows/CRT end-to-end behavior is not established by those source checks. + +Independent execution passed **29/29 serial runtime tests**, **3/3 actual doctests**, and formatting. Unique root override 149 forced test compilation from the reconstruction; 537-case inventory includes the encoding-policy test. Run `853e45ac-213f-49b3-ae26-3fa6d245940b`; JUnit SHA256 `b539f8fa4d2cc86f107177ed8951a5858797c34e70bb17e43dcf260c1b4a6fee`. Binary `codebase_graph-5cc476c357a26360` SHA256 `99e91c39f8ddc1eb49e36b346be65653d8cbf977e5ddb468c346a2e5db8bc78e`. All 424 inputs and test binary unchanged before/after. Full paths and hashes are in `verification-report.json`. + +Limits: cfg(windows) implementation/tests remain uncompiled and unexecuted; Darwin policy tests do not establish native Windows path parsing/encoding. Actual Windows canonical/UNC/long/non-ASCII cases and effective code-page modes remain qualification gates. A Windows fixture that exits after unsupported encoding rejection provides rejection coverage only. Read-only mode checks do not freeze external process-global mode mutation. 75 preexisting warnings remain. The author parallel 29 run's 3 LEAK statuses are preserved and unexplained; both author and independent serial 29 runs are clean, without proving a cause. No whole-S2, collector, automatic-runtime or aggregate closure is asserted. **Cargo released.** diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.log b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.log new file mode 100644 index 00000000..a44b063b --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.log @@ -0,0 +1,544 @@ +warning: method `delete_key` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/artifact_store.rs:324:19 + | +107 | impl ArtifactStore { + | ------------------ method in this implementation +... +324 | pub(crate) fn delete_key(&self, artifact_key: &str) -> Result { + | ^^^^^^^^^^ + | + = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default + +warning: struct `MaintenanceExecutionPermit` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/coordinator.rs:178:8 + | +178 | struct MaintenanceExecutionPermit { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: method `try_acquire_maintenance` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/coordinator.rs:189:8 + | +188 | impl ExecutionDispatcher { + | ------------------------ method in this implementation +189 | fn try_acquire_maintenance(self: &Arc) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `lifecycle_empty_with_owned` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/containment.rs:178:15 + | +178 | pub(crate) fn lifecycle_empty_with_owned( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: method `writer_lease` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/direct.rs:48:19 + | +47 | impl DirectWriteSession { + | ----------------------- method in this implementation +48 | pub(crate) fn writer_lease(&self) -> Result<&WriterLease, NativeError> { + | ^^^^^^^^^^^^ + +warning: struct `MaintenanceTarget` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:11:19 + | +11 | pub(crate) struct MaintenanceTarget { + | ^^^^^^^^^^^^^^^^^ + +warning: struct `DirectPair` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:20:19 + | +20 | pub(crate) struct DirectPair { + | ^^^^^^^^^^ + +warning: enum `InventoryStatus` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:26:17 + | +26 | pub(crate) enum InventoryStatus { + | ^^^^^^^^^^^^^^^ + +warning: enum `InventoryCategory` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:35:17 + | +35 | pub(crate) enum InventoryCategory { + | ^^^^^^^^^^^^^^^^^ + +warning: enum `Disposition` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:49:17 + | +49 | pub(crate) enum Disposition { + | ^^^^^^^^^^^ + +warning: struct `InventoryEntry` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:56:19 + | +56 | pub(crate) struct InventoryEntry { + | ^^^^^^^^^^^^^^ + +warning: struct `InventoryTotals` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:69:19 + | +69 | pub(crate) struct InventoryTotals { + | ^^^^^^^^^^^^^^^ + +warning: struct `StorageInventory` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:81:19 + | +81 | pub(crate) struct StorageInventory { + | ^^^^^^^^^^^^^^^^ + +warning: struct `Stamp` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:92:8 + | +92 | struct Stamp { + | ^^^^^ + +warning: function `stamp` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:99:4 + | +99 | fn stamp(m: &Metadata) -> Stamp { + | ^^^^^ + +warning: function `changed` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:115:4 + | +115 | fn changed(m: &Metadata) -> Option<(i64, i64)> { + | ^^^^^^^ + +warning: function `allocation` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:133:4 + | +133 | fn allocation(m: &Metadata) -> Option { + | ^^^^^^^^^^ + +warning: function `real_path` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:143:15 + | +143 | pub(crate) fn real_path(path: &Path) -> bool { + | ^^^^^^^^^ + +warning: function `read_observed` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:165:4 + | +165 | fn read_observed(path: &Path) -> Option { + | ^^^^^^^^^^^^^ + +warning: function `read_json` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:184:4 + | +184 | fn read_json(path: &Path) -> Option { + | ^^^^^^^^^ + +warning: struct `ManifestKeyView` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:188:8 + | +188 | struct ManifestKeyView { + | ^^^^^^^^^^^^^^^ + +warning: struct `ArtifactKeyView` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:192:8 + | +192 | struct ArtifactKeyView { + | ^^^^^^^^^^^^^^^ + +warning: function `read_manifest_keys` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:196:4 + | +196 | fn read_manifest_keys(path: &Path) -> Option> { + | ^^^^^^^^^^^^^^^^^^ + +warning: struct `ObservedBinding` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:210:19 + | +210 | pub(crate) struct ObservedBinding { + | ^^^^^^^^^^^^^^^ + +warning: function `default_direct` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:215:15 + | +215 | pub(crate) fn default_direct(target: &MaintenanceTarget) -> DirectPair { + | ^^^^^^^^^^^^^^ + +warning: function `physically_within` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:233:4 + | +233 | fn physically_within(path: &Path, root: &Path) -> bool { + | ^^^^^^^^^^^^^^^^^ + +warning: function `reconcile_observation_roots` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:237:4 + | +237 | fn reconcile_observation_roots(mut roots: Vec) -> Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `managed_direct_roles_overlap` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:253:4 + | +253 | fn managed_direct_roles_overlap( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `bound_roles_overlap` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:265:15 + | +265 | pub(crate) fn bound_roles_overlap(target: &MaintenanceTarget, binding: &ObservedBinding) -> bool { + | ^^^^^^^^^^^^^^^^^^^ + +warning: function `observed_relative` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:277:4 + | +277 | fn observed_relative(path: &Path, root: &Path) -> Option { + | ^^^^^^^^^^^^^^^^^ + +warning: function `config_raw_path` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:286:4 + | +286 | fn config_raw_path( + | ^^^^^^^^^^^^^^^ + +warning: function `config_value_path` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:311:4 + | +311 | fn config_value_path( + | ^^^^^^^^^^^^^^^^^ + +warning: function `observe_binding` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:331:15 + | +331 | pub(crate) fn observe_binding(target: &MaintenanceTarget) -> Result { + | ^^^^^^^^^^^^^^^ + +warning: function `classify_artifact` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:422:4 + | +422 | fn classify_artifact( + | ^^^^^^^^^^^^^^^^^ + +warning: struct `Scan` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:465:8 + | +465 | struct Scan { + | ^^^^ + +warning: associated items `new` and `walk` are never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:473:8 + | +472 | impl Scan { + | --------- associated items in this implementation +473 | fn new() -> Self { + | ^^^ +... +482 | fn walk(&mut self, path: &Path) { + | ^^^^ + +warning: function `unknown` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:552:4 + | +552 | fn unknown(path: &Path, reason: &str) -> InventoryEntry { + | ^^^^^^^ + +warning: function `inspect_storage` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:567:15 + | +567 | pub(crate) fn inspect_storage(target: &MaintenanceTarget) -> StorageInventory { + | ^^^^^^^^^^^^^^^ + +warning: function `inspect_with_hook` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:570:4 + | +570 | fn inspect_with_hook(target: &MaintenanceTarget, between_scans: impl FnOnce()) -> StorageInventory { + | ^^^^^^^^^^^^^^^^^ + +warning: function `identity_supported` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:894:4 + | +894 | fn identity_supported() -> bool { + | ^^^^^^^^^^^^^^^^^^ + +warning: enum `LeaseObservation` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:897:6 + | +897 | enum LeaseObservation { + | ^^^^^^^^^^^^^^^^ + +warning: function `lease_observation` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:902:4 + | +902 | fn lease_observation(path: &Path) -> LeaseObservation { + | ^^^^^^^^^^^^^^^^^ + +warning: function `existing_shared_lock` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:937:4 + | +937 | fn existing_shared_lock(path: &Path) -> std::io::Result { + | ^^^^^^^^^^^^^^^^^^^^ + +warning: function `ambiguous_file_identity` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:965:4 + | +965 | fn ambiguous_file_identity(metadata: &Metadata) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `repository_name` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:973:4 + | +973 | fn repository_name(root: &Path) -> String { + | ^^^^^^^^^^^^^^^ + +warning: function `valid_key` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:994:4 + | +994 | fn valid_key(key: &str) -> bool { + | ^^^^^^^^^ + +warning: function `classify` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/inventory.rs:1000:4 + | +1000 | fn classify<'a>( + | ^^^^^^^^ + +warning: method `path` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/locks.rs:34:19 + | +20 | impl LockedFile { + | --------------- method in this implementation +... +34 | pub(crate) fn path(&self) -> &Path { + | ^^^^ + +warning: method `artifact_root` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:173:19 + | + 51 | impl ReferenceDomain { + | -------------------- method in this implementation +... +173 | pub(crate) fn artifact_root(&self) -> &Path { + | ^^^^^^^^^^^^^ + +warning: method `counter` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:197:19 + | +196 | impl ReferenceEpoch { + | ------------------- method in this implementation +197 | pub(crate) fn counter(&self) -> u64 { + | ^^^^^^^ + +warning: enum `EpochObservation` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:202:17 + | +202 | pub(crate) enum EpochObservation { + | ^^^^^^^^^^^^^^^^ + +warning: function `observe_reference_epoch` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:341:15 + | +341 | pub(crate) fn observe_reference_epoch( + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: field `reason` is never read + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:385:5 + | +383 | struct ReferenceActivity { + | ----------------- field in this struct +384 | guard: Option, +385 | reason: Option, + | ^^^^^^ + | + = note: `ReferenceActivity` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis + +warning: methods `tracked` and `pending_reason` are never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:565:19 + | +564 | impl ReferenceMutation<'_> { + | -------------------------- methods in this implementation +565 | pub(crate) fn tracked(&self) -> bool { + | ^^^^^^^ +... +568 | pub(crate) fn pending_reason(&self) -> Option<&str> { + | ^^^^^^^^^^^^^^ + +warning: enum `MaintenanceAdmission` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:574:17 + | +574 | pub(crate) enum MaintenanceAdmission<'a> { + | ^^^^^^^^^^^^^^^^^^^^ + +warning: enum `DestinationWriter` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:581:6 + | +581 | enum DestinationWriter<'a> { + | ^^^^^^^^^^^^^^^^^ + +warning: method `lease` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:586:8 + | +585 | impl DestinationWriter<'_> { + | -------------------------- method in this implementation +586 | fn lease(&self) -> &WriterLease { + | ^^^^^ + +warning: struct `MaintenanceLease` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:599:19 + | +599 | pub(crate) struct MaintenanceLease<'a> { + | ^^^^^^^^^^^^^^^^ + +warning: multiple methods are never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:613:19 + | +610 | impl MaintenanceLease<'_> { + | ------------------------- methods in this implementation +... +613 | pub(crate) fn begin_reference_mutation(&self) -> Result, NativeError> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +... +624 | pub(crate) fn target(&self) -> &MaintenanceTarget { + | ^^^^^^ +... +628 | pub(crate) fn revalidate(&self) -> Result<(), NativeError> { + | ^^^^^^^^^^ +... +654 | pub(crate) fn artifact_cohort_verified(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +... +659 | pub(crate) fn cohort_verified(&self) -> Result { + | ^^^^^^^^^^^^^^^ +... +681 | pub(crate) fn with_state_lock( + | ^^^^^^^^^^^^^^^ +... +699 | pub(crate) fn cohort_record(&self) -> Result { + | ^^^^^^^^^^^^^ +... +740 | pub(crate) fn publication_cohort_record( + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +806 | pub(crate) fn direct_publication_cohort_record( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +820 | pub(crate) fn publication_cohort_verified( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +829 | pub(crate) fn direct_publication_cohort_verified( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +836 | pub(crate) fn cohort_record_path(&self) -> PathBuf { + | ^^^^^^^^^^^^^^^^^^ + +warning: struct `PublicationCohortRecord` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:842:19 + | +842 | pub(crate) struct PublicationCohortRecord { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: enum `PublicationCohortOrigin` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:856:6 + | +856 | enum PublicationCohortOrigin { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: enum `PublicationScope` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:861:6 + | +861 | enum PublicationScope { + | ^^^^^^^^^^^^^^^^ + +warning: struct `DestinationCohortRecord` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:874:19 + | +874 | pub(crate) struct DestinationCohortRecord { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: enum `DestinationCohortOrigin` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:890:6 + | +890 | enum DestinationCohortOrigin { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: method `cohort_verified` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1063:8 + | + 930 | impl ArtifactUseLease { + | --------------------- method in this implementation +... +1063 | fn cohort_verified(&self) -> Result { + | ^^^^^^^^^^^^^^^ + +warning: associated items `try_exclusive`, `try_exclusive_many`, and `exclusive_proof` are never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1126:19 + | +1107 | impl DatabaseUseLease { + | --------------------- associated items in this implementation +... +1126 | pub(crate) fn try_exclusive(database: &Path) -> Result, NativeError> { + | ^^^^^^^^^^^^^ +... +1133 | pub(crate) fn try_exclusive_many( + | ^^^^^^^^^^^^^^^^^^ +... +1208 | pub(crate) fn exclusive_proof(&self) -> Result { + | ^^^^^^^^^^^^^^^ + +warning: struct `DatabaseUseProof` is never constructed + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1237:19 + | +1237 | pub(crate) struct DatabaseUseProof { + | ^^^^^^^^^^^^^^^^ + +warning: function `observe_artifact_cohort` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1307:15 + | +1307 | pub(crate) fn observe_artifact_cohort( + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `claim_maintenance` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1328:15 + | +1328 | pub(crate) fn claim_maintenance( + | ^^^^^^^^^^^^^^^^^ + +warning: function `claim_maintenance_with_owned` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1333:15 + | +1333 | pub(crate) fn claim_maintenance_with_owned<'a>( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `claim` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1340:4 + | +1340 | fn claim<'a>( + | ^^^^^ + +warning: function `guard_registry` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1525:4 + | +1525 | fn guard_registry(root: &Path, guards: &mut Vec) -> Result<(), String> { + | ^^^^^^^^^^^^^^ + +warning: function `overlapping_pairs` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1609:4 + | +1609 | fn overlapping_pairs( + | ^^^^^^^^^^^^^^^^^ + +warning: function `resources_overlap` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/maintenance_ownership.rs:1654:4 + | +1654 | fn resources_overlap(a: &Path, b: &Path) -> bool { + | ^^^^^^^^^^^^^^^^^ + +warning: method `writer_lease` is never used + --> /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo/src/storage/managed.rs:155:19 + | +154 | impl ManagedWriteSession { + | ------------------------ method in this implementation +155 | pub(crate) fn writer_lease(&self) -> Result<&WriterLease, NativeError> { + | ^^^^^^^^^^^^ + +warning: `codebase-graph` (lib) generated 75 warnings + Compiling guarded-wrapper-review-probe v0.0.0 (/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/probe) + Finished `dev` profile [unoptimized] target(s) in 1.80s + Running `/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/guarded-wrapper-review-probe` +addressed=~/../../private/tmp/cleanup-s2-wrapper-review-ccjn2do9/tilde-native/graph.lbug +guarded_parent=/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/tilde-probe-cwd/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/tilde-native +native_parent=/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/tilde-native +addressed_database_exists=false +native_database_exists=true +actual_native_family_exclusive_available=true diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.rs b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.rs new file mode 100644 index 00000000..2cf330da --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.rs @@ -0,0 +1,38 @@ +fn main() { + use std::{fs, path::{Component, PathBuf}}; + use codebase_graph::db_writer::{connect_ladybug_database, open_ladybug_database}; + use fs2::FileExt; + let root = PathBuf::from(std::env::var_os("PROBE_ROOT").unwrap()); + let native_parent = root.join("tilde-native"); + let cwd = root.join("tilde-probe-cwd/deep"); + fs::create_dir_all(&native_parent).unwrap(); + fs::create_dir_all(&cwd).unwrap(); + std::env::set_current_dir(&cwd).unwrap(); + let native_path = native_parent.join("graph.lbug"); + drop(open_ladybug_database(&native_path, false).unwrap()); + let actual_user_home = PathBuf::from(std::env::var_os("HOME").unwrap()); + assert!(actual_user_home.is_absolute()); + let mut addressed = PathBuf::from("~"); + for component in actual_user_home.components() { + if matches!(component, Component::Normal(_)) { addressed.push(".."); } + } + addressed.push(native_path.strip_prefix("/").unwrap()); + let guarded = open_ladybug_database(&addressed, true).unwrap(); + let connection = connect_ladybug_database(&guarded).unwrap(); + connection.query("RETURN 1").unwrap(); + let family = fs::OpenOptions::new().read(true).write(true) + .open(native_parent.join(".database-use-family.lock")).unwrap(); + let actual_exclusive = family.try_lock_exclusive(); + println!("addressed={}", addressed.display()); + println!("guarded_parent={}", fs::canonicalize(addressed.parent().unwrap()).unwrap().display()); + println!("native_parent={}", fs::canonicalize(&native_parent).unwrap().display()); + println!("addressed_database_exists={}", addressed.exists()); + println!("native_database_exists={}", native_path.exists()); + println!("actual_native_family_exclusive_available={}", actual_exclusive.is_ok()); + assert!(actual_exclusive.is_ok(), "guard unexpectedly covers actual native parent"); + assert!(!addressed.exists()); + connection.query("RETURN 2").unwrap(); + drop(connection); + drop(guarded); + drop(family); +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.json new file mode 100644 index 00000000..f1e0f3ef --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.json @@ -0,0 +1,97 @@ +{ + "status": "confirmed finding; correction and focused verification pending", + "reviewer": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max", + "independent": true, + "scope": "DC-S2 wrapper foundation only; read-only reviewed source" + }, + "snapshot": { + "root": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9", + "repo": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo", + "base": "88a369f5820c7c21e05a4336f061887cc5f5d52f", + "patch": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/s2-wrapper-evidence/wrapper.patch", + "patch_sha256": "b45624c7390ada1316cee51c18720fa908ea2bf2502a0baa67aeaabb8374092b", + "files": { + "src/db_writer/access.rs": "7d1e60ac9d84d07ab9eb210189990fe6ad0e0cbd8972414367ee39c11eadb909", + "src/db_writer/mod.rs": "519f8c9c5f0482d0ca4d9cea6430fe831ea753b3a13b7ba5af06499f91a0f81e", + "src/db_writer/tests.rs": "232f87431b7e7657f300ef98440158b0c6b390214a48c6e947378c27a53aae85", + "src/api/graph_read.rs": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b" + } + }, + "last_code_commit": "077a8d8e1e75dbca553a4d9b0f65412ca9c5def1", + "main_baseline": "018df35e72447fc3e014e950d1d3c6648bbd4820", + "findings": [ + { + "id": "WRAPPER-PATH-1", + "priority": "P1", + "title": "Native path interpretation can open a database outside the retained use lease", + "location": { + "path": "src/db_writer/access.rs", + "lines": [ + 141, + 142 + ] + }, + "contract": "GuardedDatabase must retain exact physical-resource DatabaseUseLease for the full native handle lifetime.", + "trigger": "Call open_ladybug_database with a leading-tilde path. The lease interprets tilde as a literal directory, but lbug expands it before opening.", + "evidence": "Isolated Darwin public-API probe successfully queried RETURN 1 and RETURN 2 on the native database while its actual parent-family coordination file accepted exclusive fs2 ownership. The addressed literal database did not exist. The distinct actual and guarded parents are printed in tilde-probe.log. The probe exited0.", + "consequence": "A cleanup caller can obtain physical-family exclusivity over the database actually opened while the guarded native handle remains live; the new lifetime exclusion contract is therefore false.", + "baseline_distinction": "Native path expansion predates the patch. The introduced wrapper acquires exclusion for the untransformed input, so its newly promised lifetime protection is incomplete.", + "remedy": "Validate native path interpretation before acquisition and native open. Use the same supported, absolute, lossless, consistently normalized path for both lease and Database::new, or explicitly reject unsupported forms before side effects. Do not silently reinterpret a formerly expanded tilde as a different literal cwd/tilde database. Preserve raw Direct address-derived writer/journal keys and caller SystemConfig flags.", + "verification": "Add regressions for native path transforms and prove that the actual native physical-family lock cannot be obtained exclusively while a wrapper/connection is live; alternatively prove rejected forms fail before creating native files or coordination state." + } + ], + "native_path_trace": { + "dependency": "lbug 0.19.0, inspected local crate source", + "rust": "src/database.rs::Database::new passes path.display().to_string() to native FFI.", + "cpp": "lbug-src/src/main/database.cpp::Database::initMembers calls StorageUtils::expandPath.", + "expansion": "lbug-src/src/storage/storage_utils.cpp:72-107 preserves empty/:memory: mode, expands leading tilde, then makes relative paths absolute and lexically normalizes dot and dot-dot. Windows drive/UNC special handling avoids absolute where appropriate.", + "cwd": "Static interleaving: lease resolves a relative path, another thread changes process CWD, and the unchanged relative path passed to native is resolved again. No deterministic CWD-race probe was executed.", + "symlink_dotdot": "Static consideration: Rust canonicalize follows an intermediate symlink before dot-dot, whereas native lexically_normal cancels components before filesystem lookup. Absolute conversion alone does not address that distinction. No symlink/dot-dot native probe was executed.", + "non_utf8": "Static Linux consideration: lease validates UTF-8 basename but not all parent bytes; native display conversion may open a U+FFFD parent while the lease protects original bytes. The attempted Darwin raw-parent fixture failed at directory creation with EILSEQ; it does not establish native Linux qualification.", + "memory_mode": "Empty paths and :memory: are native in-memory modes; a correction must not silently convert these into on-disk resources." + }, + "checks": { + "nextest": { + "result": "24 passed; 0 failures; 0 errors; 508 unselected", + "junit": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/junit.xml", + "log": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/run.log", + "inventory": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/inventory.json", + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-cafb7613f0ed9fdf", + "binary_sha256": "4af16ffad5ee0bb32c0280d7afd57292e10dc4e2797f5dbb6a8d3e1089eb8cff", + "root_test_codegen_units": 143, + "compile_checkout": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/repo" + }, + "doctests": { + "result": "3 passed: 1 no-run positive compile and 2 compile-fail cases", + "log": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/doctests.log" + }, + "tilde_probe": { + "result": "exit 0; live native query plus actual-family exclusive acquisition", + "source": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/probe/src/main.rs", + "log": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/tilde-probe.log" + }, + "raw_parent_attempt": { + "result": "Darwin EILSEQ at fixture directory creation; not a product defect reproduction", + "log": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/path-probe.log" + }, + "invalid_doc_command": { + "result": "initial invocation combined incompatible --doc/--lib flags; no tests ran; corrected command passed", + "log": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/doctests-invalid-invocation.log" + }, + "shipping_constructor_audit": "Only shipping Database::new is the guarded constructor; three raw native constructor uses are test fixtures.", + "borrow_drop_config": "Static review found private fields, immutable Deref only, no Clone/DerefMut/AsMut or consuming raw escape; declaration order drops native Database before lease; connections borrow through immutable native reference; graph search/context keep SystemConfig::default().read_only(true).", + "warnings": "75 preexisting pending-foundation warnings observed; wrapper does not qualify their final removal." + }, + "limitations": [ + "Graph MCP indexes the older original checkout and did not index new maintenance_ownership symbols. The dependency was read using explicit Scryer/frozen file anchors; configured graph was not rebuilt.", + "Fresh k-wiki read returned approved revision 0.2; Scryer chg-6vx5tj node-9emgn1/resp-2x4van and node-2xwzve governed the review. No wiki or Scryer mutation was made.", + "The discarded corrupt-native-file fixture produced nextest LEAK in worker evidence and is not counted as qualified. Final missing-readonly-native fixture passed. No introduced source defect is inferred from that discarded log.", + "No native Linux/Windows execution; no whole-S2 fast-seal, phase admission, collector, automatic runtime or aggregate qualification.", + "The confirmed finding remains unresolved. A fresh final frozen snapshot and focused independent verification are required.", + "No additional probes were run after coordinator requested historical report. Cargo had already been explicitly released.", + "Durable lesson exists about binding native path interpretation to guarded resource identity; coordinator owns any later wiki/memory recording after fix validation." + ], + "cargo": "RELEASED" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.md b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.md new file mode 100644 index 00000000..699a2e02 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.md @@ -0,0 +1,15 @@ +Confirmed **P1 — native path interpretation can bypass the retained database-use lease** at `src/db_writer/access.rs:141-142`. + +`DatabaseUseLease::acquire_shared` protects the input path's physical parent. The same unmodified input goes to lbug 0.19.0, which expands leading `~` and resolves/normalizes the result. The isolated Darwin probe opened a temporary native database through such a path, executed `RETURN 1` and `RETURN 2`, and acquired exclusive ownership on that actual database's parent-family coordination file while the wrapper and connection remained live. The literal addressed database did not exist. Native expansion predates the patch; the new lifetime guarantee is incomplete because its lease is acquired for a different resource. + +Use one validated, supported absolute path consistently for both guard and native open, handling native lexical normalization explicitly. Reject unsupported/lossy native forms before side effects when necessary; do not silently turn expanded tilde input into a different literal database. Preserve raw Direct writer/journal keys and SystemConfig behavior. A plain absolute conversion addresses process-CWD drift but needs separate treatment of native normalization and in-memory tokens. + +Reviewed as a fresh independent GPT-6.1 Sol Max reviewer. Frozen baseline `88a369f5820c7c21e05a4336f061887cc5f5d52f`, exact patch SHA256 `b45624c7390ada1316cee51c18720fa908ea2bf2502a0baa67aeaabb8374092b`; all four source identities matched at reconstruction. Full identities and source trace are in `review-report.json` / `snapshot.json`. + +Actual checks: a uniquely rebuilt root 143 test binary from this isolated checkout passed 24 focused wrapper/native, physical-family and optional-read-availability tests; separate JUnit is `junit.xml`. Three actual wrapper doctests passed. Test binary `codebase_graph-cafb7613f0ed9fdf`, SHA256 `4af16ffad5ee0bb32c0280d7afd57292e10dc4e2797f5dbb6a8d3e1089eb8cff`. Tilde probe source is `probe/src/main.rs`, output `tilde-probe.log`. + +Additional source-based considerations: lbug converts the full Rust Path using display text, so non-UTF8 Linux parents can diverge; the Darwin raw-parent attempt stopped at EILSEQ and is not Linux qualification. Native lexical dot-dot normalization can differ from Rust filesystem resolution across a symlink. Relative inputs can be redirected by a process-CWD change between guard acquisition and native open. These additional cases were not executed as native repros. Empty and `:memory:` inputs have native in-memory semantics and must not silently become on-disk paths. + +No other confirmed P0-P3 finding survived this wrapper review. The borrow/drop structure and caller flags appear correct within the reviewed snapshot. The graph index is older than the new ownership module; explicit Scryer/frozen anchors supplied that dependency. 75 preexisting warnings remain. The worker's discarded corrupt-native-file LEAK result is not counted as qualification. + +The P1 remains unresolved; fix verification and all whole-S2/collector/native-platform/aggregate gates remain pending. No repository/wiki/Scryer/Git mutation occurred. **Cargo is released.** diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.json new file mode 100644 index 00000000..6701604e --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.json @@ -0,0 +1,152 @@ +{ + "reviewer": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max", + "independent": true, + "skill": "adversarial-review-engineer" + }, + "scope": "Read-only Windows-prefix static follow-up on same frozen wrapper fix before commit; no new product/ownership decision.", + "snapshot": { + "baseline": "88a369f5820c7c21e05a4336f061887cc5f5d52f", + "repo": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/repo", + "root": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3", + "patch": "/private/tmp/cleanup-s2-wrapper-pathfix-yowgkj08/wrapper.patch", + "patch_sha256": "2b5b80a40079b6aeda0d640a0a0cff153d76dbbd863749351103cbe1e815a7cb", + "source": { + "src/db_writer/access.rs": "9098e549d34caefff87ad99d214336d63bed67d7c042a1dd50386b20c7a9227d", + "src/db_writer/mod.rs": "519f8c9c5f0482d0ca4d9cea6430fe831ea753b3a13b7ba5af06499f91a0f81e", + "src/db_writer/tests.rs": "19f5a967050d727238b51bf15d537720b18fff154afe08968b98e74d9f8503ce", + "src/api/graph_read.rs": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b" + } + }, + "findings": [ + { + "id": "WRAPPER-WINDOWS-1", + "priority": "P1", + "classification": "Confirmed by complete source trace and primary platform documentation; not runtime-reproduced.", + "title": "Rejecting verbatim prefixes blocks ordinary canonicalized Windows database paths", + "location": { + "path": "src/db_writer/access.rs", + "start": 202, + "end": 213, + "additional_gate": [ + 227, + 232 + ] + }, + "trigger": "A valid Windows managed installation uses the default storage root or a storage/database path relative to its install configuration. Root/config binding canonicalizes those existing paths, producing an extended-length VerbatimDisk or VerbatimUNC prefix, retained into the active generation database path.", + "consequence": "Graph search, context, graph query and health database reads fail with database_path_prefix_unsupported before native opening, despite using ordinary program-generated paths. This affects normal internal resolver output, not merely unsupported caller aliases.", + "trace": [ + { + "path": "src/api/context.rs", + "lines": "421-430,748-758,769-775", + "claim": "bind_repo_selector canonicalizes repository root/config using fs::canonicalize." + }, + { + "external": "https://doc.rust-lang.org/std/fs/fn.canonicalize.html", + "claim": "Rust documents that Windows canonicalize returns extended-length syntax." + }, + { + "path": "src/api/context.rs", + "lines": "231-245,501-507,627-651,797-805", + "claim": "RepoPaths derives state beneath canonical root; default managed storage and relative configured paths inherit the prefix. Runtime clones active snapshot db_path." + }, + { + "path": "src/storage/layout.rs", + "lines": "75-88,128-129,144-153,183-199", + "claim": "ManagedLayout and generation/database joins preserve the storage-root prefix." + }, + { + "path": "src/storage/managed.rs", + "lines": "428-445,1015-1017", + "claim": "GraphStorage preserves supplied ManagedLayout and active snapshot carries generation.db_path." + }, + { + "path": "src/api/core.rs", + "lines": "861-877,908-912,957-958", + "claim": "Search/context/query pass runtime.db_path unchanged to graph_read." + }, + { + "path": "src/api/graph_read.rs", + "lines": "23-25,53-59,227-237,436-443", + "claim": "All native read entry points use the guarded opener." + }, + { + "path": "src/db_writer/access.rs", + "lines": "146-148,194-213,226-232", + "claim": "The Windows allowlist accepts Disk/UNC only; both VerbatimDisk and VerbatimUNC reject before the native constructor." + } + ], + "baseline_distinction": "The baseline and original wrapper did not contain this Windows prefix rejection; this deterministic guard failure is introduced by the path-fix helper.", + "counterevidence_scope": "Explicit ordinary absolute Disk/UNC storage paths may bypass this trigger. That does not cover default root-derived or relative-config paths. The approved strict alias restriction does not establish permission to reject normal resolver-produced Windows paths.", + "sdk_assessment": { + "rust": "lbug0.19.0 database.rs137-154 converts Path display text and forwards the full string.", + "native": "StorageUtils::expandPath72-107 treats leading double backslash as a Windows UNC path for avoiding CWD, and lexically_normalizes without an explicit verbatim-prefix rejection. LocalFileSystem::openFile89-134 passes its resulting string to CreateFileA.", + "support_limit": "The source does not establish that stripping all extended prefixes is lossless. Extended namespace parsing and length behavior differ; the SDK uses a narrow ANSI API, so arbitrary Unicode/code-page equivalence is also not demonstrated. No universal projection or arbitrary verbatim acceptance is asserted.", + "correction_target": "Preserve ordinary program-generated canonicalized Windows disk/UNC paths using a demonstrably exact supported representation consistently for both guard and native open. Keep unsupported/device/ambiguous forms strict and preserve raw Direct writer/journal address keys. Do not change those persisted identities as a prefix repair.", + "verification_needed": "Run Windows-native public runtime search/query/materialization through fs::canonicalize-derived disk paths, with appropriate UNC coverage where available, and verify actual resource-family exclusion for the chosen representation. Include relevant normalization, long-path and encoding boundaries. No Windows support conclusion should be drawn from Darwin-only cfg exclusion." + }, + "documentation": [ + "https://doc.rust-lang.org/std/fs/fn.canonicalize.html", + "https://doc.rust-lang.org/std/path/enum.Prefix.html", + "https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea", + "https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file" + ], + "status": "Open; correction and focused qualification pending." + } + ], + "source_identities": { + "src/db_writer/access.rs": { + "path": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/repo/src/db_writer/access.rs", + "sha256": "9098e549d34caefff87ad99d214336d63bed67d7c042a1dd50386b20c7a9227d" + }, + "src/db_writer/tests.rs": { + "path": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/repo/src/db_writer/tests.rs", + "sha256": "19f5a967050d727238b51bf15d537720b18fff154afe08968b98e74d9f8503ce" + }, + "src/api/context.rs": { + "path": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/repo/src/api/context.rs", + "sha256": "b038192e4cc8258c94c7c524f9ed565017e45d7e28092b09e5490d4836db0eed" + }, + "src/api/core.rs": { + "path": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/repo/src/api/core.rs", + "sha256": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462" + }, + "src/api/graph_read.rs": { + "path": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/repo/src/api/graph_read.rs", + "sha256": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b" + }, + "src/storage/layout.rs": { + "path": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/repo/src/storage/layout.rs", + "sha256": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987" + }, + "src/storage/managed.rs": { + "path": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/repo/src/storage/managed.rs", + "sha256": "aee08a9c6f09640bde3d2d0a3b50690391e294d308561544e94079c9abe2041d" + }, + "lbug/src/database.rs": { + "path": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/src/database.rs", + "sha256": "f44a1eae3231774d632b39d895b324982670d4f548c1cb5b43aa73c0f5df1766" + }, + "lbug/lbug-src/src/storage/storage_utils.cpp": { + "path": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/lbug-src/src/storage/storage_utils.cpp", + "sha256": "d1505503398f15c1bc94b01afdc3351fbef44053bfaed9aa66b5cfc7d8ab33c5" + }, + "lbug/lbug-src/src/common/file_system/local_file_system.cpp": { + "path": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/lbug-src/src/common/file_system/local_file_system.cpp", + "sha256": "dd02f6018a2065d67d8c637cc2a5fb19b0c8c57998488d0a8bdad2a210d752ad" + } + }, + "preserved_evidence": { + "darwin_verification": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/verification-report.json", + "darwin_status": "Existing 28-runtime-test/3-doctest/format results and original P1 correction remain valid for their exact Darwin scope; no report was overwritten." + }, + "limitations": [ + "No Cargo invoked; no native Windows compilation or execution; no Linux runtime execution.", + "Configured graph indexes older checkout; explicit frozen files and fetched existing architecture intent supplied newer ownership context.", + "Safe projection or general native handling of all verbatim/Unicode/long paths is not proved by this static trace.", + "Full wrapper acceptance is pending the new Windows finding; whole-S2 and collector/native-platform/aggregate gates remain pending." + ], + "mutations": "Only this isolated report and source-hash evidence were written; no source/test/wiki/Scryer/Git mutation.", + "cargo": "Not acquired or used; S3 retains ownership." +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.md b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.md new file mode 100644 index 00000000..77877ee8 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.md @@ -0,0 +1,9 @@ +**P1 — ordinary canonicalized Windows database paths are rejected.** Confirmed by a complete source trace plus Rust platform documentation; no Windows execution is claimed. + +At `src/db_writer/access.rs:202-213` (and the second gate at 227-232), `guarded_native_path` allows only `Prefix::Disk` and `Prefix::UNC`, rejecting `VerbatimDisk` and `VerbatimUNC`. `bind_repo_selector` canonicalizes roots/config paths (`context.rs421-430,748-775`); [Rust documents Windows canonicalize returning extended-length syntax](https://doc.rust-lang.org/std/fs/fn.canonicalize.html). `RepoPaths::derive` and the default managed layout join beneath that canonical root, while relative configured storage joins beneath the canonical config parent (`context.rs231-245,627-651,797-805`). Generation layout and snapshot retain the prefix (`layout.rs75-88,128-153,183-199`; `managed.rs428-445`). Search/context/query pass runtime.db_path unchanged to the guarded opener (`core.rs876,911,957-958`; `graph_read.rs24,58,236,442`). Thus normal root-derived/relative-config Windows graph reads deterministically return `database_path_prefix_unsupported` before native open. Plain absolute configured Disk/UNC destinations can avoid the trigger; they do not protect the standard root-derived route. + +This rejection is introduced by the path fix. It is a normal internal-path compatibility regression, beyond a restriction on ambiguous caller aliases. Resolve it before accepting the full wrapper for supported Windows use. + +Pinned lbug 0.19.0 does not itself impose this blanket prefix rejection: Rust passes Path display text; native StorageUtils treats leading double-backslash forms as Windows UNC for CWD avoidance and calls `lexically_normal`; LocalFileSystem passes the result to `CreateFileA`. [CreateFileA documentation discusses extended prefixes](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea), but [Windows namespace documentation explains their parsing/length differences](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file). Therefore blindly stripping the prefix is not established as a universally lossless repair. The narrow ANSI SDK call also leaves arbitrary Unicode/code-page equivalence unqualified. Preserve an exact supported representation for BOTH guard and native; preserve raw Direct writer/journal keys. Demonstrate the selected representation with Windows-native canonicalized-disk runtime and guard-exclusion checks, plus applicable UNC/normalization/length/encoding cases. + +Reviewed as independent GPT-6.1 Sol Max on the same baseline 88a369f / patch 2b5b80a40079b6aeda0d640a0a0cff153d76dbbd863749351103cbe1e815a7cb frozen snapshot. Exact source identities and full trace: `windows-prefix-followup.json`. Prior Darwin verification and historical tilde evidence are preserved unchanged. No Cargo, Windows compilation, native Windows execution or repository mutation occurred. S3 retains Cargo ownership. Whole-S2/collector/aggregate closure remains pending. diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-scope.json b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-scope.json new file mode 100644 index 00000000..9e29bcbc --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-scope.json @@ -0,0 +1,33 @@ +{ + "primary_sources": { + "file_api_mode": "https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-arefileapisansi", + "ansi_codepage": "https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getacp", + "utf8_process_codepage": "https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page", + "create_file_a": "https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea", + "namespace_semantics": "https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file", + "msvc_filesystem_conversion": "https://github.com/microsoft/STL/blob/main/stl/src/filesystem.cpp" + }, + "policy": "Preserve exact canonical Disk/UNC/VerbatimDisk/VerbatimUNC namespaces; nonASCII only with effective ANSI/OEM FileAPI codepage65001; no prefix stripping or process-global setter.", + "prior_windows_p1_report": { + "path": "/private/tmp/cleanup-s2-pathfix-review-wt1kmbw3/windows-prefix-followup.md", + "sha256": "583c1db9663998b036ed62b31c4e8fee9269b82d5b0eede713492f52671f0220" + }, + "prior_tilde_failure": { + "path": "/private/tmp/cleanup-s2-wrapper-review-ccjn2do9/tilde-probe.log", + "sha256": "417acd28bdc8143feeda839a24ffe0d01cbb1cbe3ef3f125a16dc1d370c06bb6" + }, + "prior_corrupt_native_fixture_leak": { + "path": "/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/s2-wrapper-evidence/corrupt-native-fixture-leak.log", + "sha256": "387544134e47bbe5f63d1601f8279dee31b30a8741fa71ec5ad8bf038d0c2ee3" + }, + "previous_snapshot": { + "path": "/private/tmp/cleanup-s2-wrapper-pathfix-yowgkj08/snapshot.json", + "sha256": "530429c9426c680a5260f2ad55139c2f5ad85442347bd3bd4f92882419fd219a" + }, + "pinned_createfilea": { + "path": "/Users/rabii/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lbug-0.19.0/lbug-src/src/common/file_system/local_file_system.cpp", + "sha256": "dd02f6018a2065d67d8c637cc2a5fb19b0c8c57998488d0a8bdad2a210d752ad", + "lines": "60-118", + "excerpt": "std::unique_ptr LocalFileSystem::openFile(const std::string& path, FileOpenFlags flags,\n main::ClientContext* context) {\n auto fullPath = expandPath(context, path);\n auto fileFlags = flags.flags;\n validateFileFlags(fileFlags);\n\n int openFlags = 0;\n bool readMode = fileFlags & FileFlags::READ_ONLY;\n bool writeMode = fileFlags & FileFlags::WRITE;\n if (readMode && writeMode) {\n openFlags = O_RDWR;\n } else if (readMode) {\n openFlags = O_RDONLY;\n } else if (writeMode) {\n openFlags = O_WRONLY;\n } else {\n // LCOV_EXCL_START\n throw InternalException(\"READ, WRITE or both should be specified when opening a file.\");\n // LCOV_EXCL_STOP\n }\n if (writeMode) {\n DASSERT(fileFlags & FileFlags::WRITE);\n if (fileFlags & FileFlags::CREATE_IF_NOT_EXISTS) {\n openFlags |= O_CREAT;\n } else if (fileFlags & FileFlags::CREATE_AND_TRUNCATE_IF_EXISTS) {\n openFlags |= O_CREAT | O_TRUNC;\n }\n }\n\n#if defined(_WIN32)\n auto dwDesiredAccess = 0ul;\n int dwCreationDisposition;\n if (fileFlags & FileFlags::CREATE_IF_NOT_EXISTS) {\n dwCreationDisposition = OPEN_ALWAYS;\n } else if (fileFlags & FileFlags::CREATE_AND_TRUNCATE_IF_EXISTS) {\n dwCreationDisposition = CREATE_ALWAYS;\n } else {\n dwCreationDisposition = OPEN_EXISTING;\n }\n auto dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;\n if (openFlags & (O_CREAT | O_WRONLY | O_RDWR)) {\n dwDesiredAccess |= GENERIC_WRITE;\n }\n // O_RDONLY is 0 in practice, so openFlags & (O_RDONLY | O_RDWR) doesn't work.\n if (!(openFlags & O_WRONLY)) {\n dwDesiredAccess |= GENERIC_READ;\n }\n if (openFlags & FileFlags::BINARY) {\n dwDesiredAccess |= _O_BINARY;\n }\n\n HANDLE handle = CreateFileA(fullPath.c_str(), dwDesiredAccess, dwShareMode, nullptr,\n dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);\n if (handle == INVALID_HANDLE_VALUE) {\n throw IOException(std::format(\"Cannot open file. path: {} - Error {}: {}\", fullPath,\n GetLastError(), std::system_category().message(GetLastError())));\n }\n if (flags.lockType != FileLockType::NO_LOCK) {\n DWORD dwFlags = flags.lockType == FileLockType::READ_LOCK ?" + } +} \ No newline at end of file diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-evidence.json b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-evidence.json new file mode 100644 index 00000000..7191dd47 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-evidence.json @@ -0,0 +1,30 @@ +{ + "baseline": "88a369f5820c7c21e05a4336f061887cc5f5d52f", + "source": "/private/tmp/dc-s3-facts-final-lm75n3xh/repo", + "source_inputs": 427, + "inputs_unchanged": true, + "host": "aarch64-apple-darwin", + "rustc": "1.96.0", + "cargo_root_override": "profile.test.package.codebase-graph.codegen-units=146", + "build": "visible root rebuild,26.80s,no compiler warnings", + "selection": "test(storage::maintenance)", + "selected": 66, + "new_facts_tests": 14, + "existing_ownership_tests": 52, + "result": "66 passed,0 failed,474 skipped,retries0", + "fmt": "cargo fmt --all --check passed", + "negative_traits": "rustc expected exit1; exactly two E0277 errors for MutationGrant Send and Sync", + "limitations": [ + "Facts-only; no current references/origin/schema/backup-usability/deletion/retirement authority.", + "Memory account covers requested owned layouts and explicit stack floor, not allocator slack/RSS/OS buffers. Caller must account hidden allocations of generic Read inputs and other controller values.", + "Unsafe intrusive account requires independent alloc/refcount/Send/Sync/drop audit; no Miri/sanitizer or native Windows/Linux qualification.", + "Json lexical range intentionally differs from typed numeric loader; duplicate-key/default/schema meaning is not certified.", + "Pure cursor is bounded stack facts only; caller must charge controller floor and budget durable persistence.", + "Production uses no new foundation APIs yet; independent baseline excludes concurrent S2 wrapper, integration remains coordinator-owned." + ], + "prior_attempts": [ + "Initial archive extraction Python3.9 filter argument failed before extracting; successful initial freeze preserved.", + "Initial standalone rustc budget tests:5 pass,one unused limit warning in initial frozen version.", + "Initial JUnit copy assumed shared target/nextest; actual nextest store is frozen source target/nextest, subsequently copied unchanged." + ] +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-junit.xml new file mode 100644 index 00000000..71575262 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-junit.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-source-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-source-manifest.json new file mode 100644 index 00000000..3cf763ed --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-source-manifest.json @@ -0,0 +1,28 @@ +{ + "baseline": "88a369f5820c7c21e05a4336f061887cc5f5d52f", + "overlay": [ + { + "path": "src/storage/maintenance_budget.rs", + "sha256": "3288f29d3133f7f403afcf275d35e8c55fb53cc487b0251b245070e19daa27e0", + "bytes": 12186 + }, + { + "path": "src/storage/maintenance_json.rs", + "sha256": "d6c2162b2aa37d77667900f5491a5e09fb6a66e1c4ff7ba9d93d056794e45d9e", + "bytes": 19665 + }, + { + "path": "src/storage/maintenance.rs", + "sha256": "c5d94f0ac68d9ed81f8dfb060928887f1c2641798236ba60ecb15f9cc9c970a7", + "bytes": 7896 + }, + { + "path": "src/storage/mod.rs", + "sha256": "9ac2905364228f9376c69fbc6347fe84b043b5ca6e63e27046bb5eeedaac6081", + "bytes": 333 + } + ], + "source": "/private/tmp/dc-s3-facts-final-lm75n3xh/repo", + "prior_snapshot": "/private/tmp/dc-s3-facts-snapshot-hy04bi67", + "qualification": "facts-only; no storage authority; source frozen before cargo" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.rs b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.rs new file mode 100644 index 00000000..68aae9e3 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.rs @@ -0,0 +1,9 @@ +#![allow(dead_code)] +#[path = "/private/tmp/cleanup-v2-s3-review-_098ctam/repo/src/storage/maintenance_budget.rs"] +mod maintenance_budget; +fn needs_send() {} +fn needs_sync() {} +fn main() { + needs_send::(); + needs_sync::(); +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.stderr b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.stderr new file mode 100644 index 00000000..89a12a04 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.stderr @@ -0,0 +1,49 @@ +error[E0277]: `Rc<()>` cannot be sent between threads safely + --> /private/tmp/cleanup-v2-s3-review-_098ctam/negative-traits.rs:7:18 + | + 7 | needs_send::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc<()>` cannot be sent between threads safely + | + = help: within `MutationGrant`, the trait `Send` is not implemented for `Rc<()>` +note: required because it appears within the type `PhantomData>` + --> /Users/rabii/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/marker.rs:811:12 + | +811 | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `MutationGrant` + --> /private/tmp/cleanup-v2-s3-review-_098ctam/repo/src/storage/maintenance_budget.rs:208:19 + | +208 | pub(crate) struct MutationGrant { + | ^^^^^^^^^^^^^ +note: required by a bound in `needs_send` + --> /private/tmp/cleanup-v2-s3-review-_098ctam/negative-traits.rs:4:18 + | + 4 | fn needs_send() {} + | ^^^^ required by this bound in `needs_send` + +error[E0277]: `Rc<()>` cannot be shared between threads safely + --> /private/tmp/cleanup-v2-s3-review-_098ctam/negative-traits.rs:8:18 + | + 8 | needs_sync::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc<()>` cannot be shared between threads safely + | + = help: within `MutationGrant`, the trait `Sync` is not implemented for `Rc<()>` +note: required because it appears within the type `PhantomData>` + --> /Users/rabii/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/marker.rs:811:12 + | +811 | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `MutationGrant` + --> /private/tmp/cleanup-v2-s3-review-_098ctam/repo/src/storage/maintenance_budget.rs:208:19 + | +208 | pub(crate) struct MutationGrant { + | ^^^^^^^^^^^^^ +note: required by a bound in `needs_sync` + --> /private/tmp/cleanup-v2-s3-review-_098ctam/negative-traits.rs:5:18 + | + 5 | fn needs_sync() {} + | ^^^^ required by this bound in `needs_sync` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe-result.json b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe-result.json new file mode 100644 index 00000000..1899bafd --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe-result.json @@ -0,0 +1 @@ +{"allocation_failure_and_lifetime":"passed","exclusive_ceiling_races":16,"json_mutation_differentials":7559,"json_boundaries":428,"cursor_restart_selections":32,"cursor_bit_corruptions":593,"entry_101":"rejected"} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe.rs b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe.rs new file mode 100644 index 00000000..1a3bb082 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe.rs @@ -0,0 +1,243 @@ +#![allow(dead_code)] +#[path = "/private/tmp/cleanup-v2-s3-review-_098ctam/repo/src/storage/maintenance_budget.rs"] +mod maintenance_budget; +#[path = "/private/tmp/cleanup-v2-s3-review-_098ctam/repo/src/storage/maintenance_json.rs"] +mod maintenance_json; +#[path = "/private/tmp/cleanup-v2-s3-review-_098ctam/repo/src/storage/maintenance.rs"] +mod maintenance; + +use maintenance_budget::{CleanupMemory, MutationGrant, ResourceLimit}; +use maintenance_json::{JsonFailure, MaintenanceJsonReader, TokenKind}; +use maintenance::{CursorAddress, MaintenanceCursor, WorkCategory}; +use sha2::{Digest, Sha256}; +use std::alloc::{GlobalAlloc, Layout, System}; +use std::io::{self, Read}; +use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + +struct ObservedAllocator; +static FAIL_NEXT: AtomicBool = AtomicBool::new(false); +static WATCH_MEMORY: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); +static WATCH_CHARGE: AtomicUsize = AtomicUsize::new(0); +static WATCH_BYTES: AtomicUsize = AtomicUsize::new(0); +static OBSERVED_ALLOC: AtomicUsize = AtomicUsize::new(0); +static OBSERVED_FREE: AtomicUsize = AtomicUsize::new(0); +static WRONG_CHARGE: AtomicBool = AtomicBool::new(false); +unsafe impl GlobalAlloc for ObservedAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if FAIL_NEXT.swap(false, Ordering::SeqCst) { + return std::ptr::null_mut(); + } + inspect_charge(layout, true); + System.alloc(layout) + } + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + if FAIL_NEXT.swap(false, Ordering::SeqCst) { + return std::ptr::null_mut(); + } + inspect_charge(layout, true); + System.alloc_zeroed(layout) + } + unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) { + inspect_charge(layout, false); + System.dealloc(pointer, layout) + } +} +unsafe fn inspect_charge(layout: Layout, allocation: bool) { + let memory = WATCH_MEMORY.load(Ordering::SeqCst); + if !memory.is_null() && layout.size() == WATCH_BYTES.load(Ordering::SeqCst) { + if (*memory).used() != WATCH_CHARGE.load(Ordering::SeqCst) { + WRONG_CHARGE.store(true, Ordering::SeqCst); + } + if allocation { OBSERVED_ALLOC.fetch_add(1, Ordering::SeqCst); } + else { OBSERVED_FREE.fetch_add(1, Ordering::SeqCst); } + } +} +#[global_allocator] +static ALLOCATOR: ObservedAllocator = ObservedAllocator; + +fn allocation_checks() { + FAIL_NEXT.store(true, Ordering::SeqCst); + assert!(matches!(CleanupMemory::new(4096), Err(ResourceLimit::AllocationFailed))); + assert!(!FAIL_NEXT.load(Ordering::SeqCst)); + let mut memory = CleanupMemory::new(8192).unwrap(); + let floor = memory.used(); + FAIL_NEXT.store(true, Ordering::SeqCst); + assert!(matches!(memory.reserve(513).unwrap().bytes(513), Err(ResourceLimit::AllocationFailed))); + assert!(!FAIL_NEXT.load(Ordering::SeqCst)); + assert_eq!(memory.used(), floor); + WATCH_MEMORY.store(&mut memory, Ordering::SeqCst); + WATCH_BYTES.store(512, Ordering::SeqCst); + WATCH_CHARGE.store(floor + 512, Ordering::SeqCst); + let mut bytes = memory.reserve(512).unwrap().bytes(512).unwrap(); + assert!(bytes.as_slice().iter().all(|b| *b == 0)); + bytes.as_mut_slice().fill(42); + assert!(bytes.as_slice().iter().all(|b| *b == 42)); + drop(bytes); + WATCH_MEMORY.store(std::ptr::null_mut(), Ordering::SeqCst); + assert_eq!(OBSERVED_ALLOC.load(Ordering::SeqCst), 1); + assert_eq!(OBSERVED_FREE.load(Ordering::SeqCst), 1); + assert!(!WRONG_CHARGE.load(Ordering::SeqCst)); + assert_eq!(memory.used(), floor); + let zero = memory.reserve(0).unwrap().bytes(0).unwrap(); + assert!(zero.as_slice().is_empty()); + drop(zero); + let memory = CleanupMemory::new(usize::MAX).unwrap(); + let floor = memory.used(); + assert!(matches!(memory.reserve(isize::MAX as usize + 1).unwrap().bytes(isize::MAX as usize + 1), Err(ResourceLimit::ArithmeticOverflow))); + assert_eq!(memory.used(), floor); + assert!(matches!(memory.reserve(usize::MAX), Err(ResourceLimit::ArithmeticOverflow))); + let mut reservation = memory.reserve(100).unwrap(); + let zero = reservation.split(0).unwrap(); + let child = reservation.split(100).unwrap(); + assert_eq!(reservation.remaining(), 0); + assert_eq!(child.remaining(), 100); + drop(memory); + drop(zero); + drop(reservation); + let buffer = child.bytes(100).unwrap(); + std::thread::spawn(move || { assert_eq!(buffer.as_slice().len(), 100); drop(buffer); }).join().unwrap(); +} + +fn concurrent_checks() { + use std::sync::{Arc, Barrier}; + for _ in 0..16 { + let discovery=CleanupMemory::new(4096).unwrap(); + let floor=discovery.used(); drop(discovery); + let memory=CleanupMemory::new(floor+257).unwrap(); + let start=Arc::new(Barrier::new(3)); + let held=Arc::new(Barrier::new(3)); + let release=Arc::new(Barrier::new(3)); + let successes=AtomicUsize::new(0); + std::thread::scope(|scope| { + for _ in 0..2 { + let memory=memory.clone(); let start=start.clone(); let held=held.clone(); let release=release.clone(); let successes=&successes; + scope.spawn(move || { + start.wait(); + let reservation=memory.reserve(257).ok(); + if reservation.is_some() { successes.fetch_add(1,Ordering::SeqCst); } + held.wait(); release.wait(); drop(reservation); + }); + } + start.wait(); held.wait(); + assert_eq!(successes.load(Ordering::SeqCst),1); + assert_eq!(memory.used(),floor+257); + release.wait(); + }); + assert_eq!(memory.used(),floor); + assert_eq!(memory.peak(),floor+257); + } +} + +struct Chunked<'a> { bytes: &'a [u8], chunk: usize, interrupts: usize } +impl Read for Chunked<'_> { + fn read(&mut self, output: &mut [u8]) -> io::Result { + if self.interrupts > 0 { self.interrupts -= 1; return Err(io::ErrorKind::Interrupted.into()); } + let n = output.len().min(self.chunk).min(self.bytes.len()); + output[..n].copy_from_slice(&self.bytes[..n]); + self.bytes = &self.bytes[n..]; + Ok(n) + } +} +fn parsed(bytes: &[u8], chunk: usize) -> Result<([u8;32], [u8;32]), JsonFailure> { + let memory = CleanupMemory::new(16384).unwrap(); + MaintenanceJsonReader::new(Chunked { bytes, chunk, interrupts: 2 }, &memory)?.finish() +} +fn check_tokens(bytes: &[u8], chunk: usize) { + let memory = CleanupMemory::new(16384).unwrap(); + let mut reader = MaintenanceJsonReader::new(Chunked { bytes, chunk, interrupts: 1 }, &memory).unwrap(); + let mut last = 0; + while let Some(token) = reader.next_token().unwrap() { + assert!(token.start >= last && token.start < token.end && token.end <= bytes.len() as u64); + last = token.end; + let span = &bytes[token.start as usize..token.end as usize]; + match token.kind { + TokenKind::String { decoded_len, decoded_sha256, .. } => { + let value: String = serde_json::from_slice(span).unwrap(); + assert_eq!(decoded_len, value.len() as u64); + assert_eq!(decoded_sha256, <[u8;32]>::from(Sha256::digest(value.as_bytes()))); + } + TokenKind::ObjectStart => assert_eq!(span, b"{"), + TokenKind::ObjectEnd => assert_eq!(span, b"}"), + TokenKind::ArrayStart => assert_eq!(span, b"["), + TokenKind::ArrayEnd => assert_eq!(span, b"]"), + TokenKind::Bool(true) => assert_eq!(span, b"true"), + TokenKind::Bool(false) => assert_eq!(span, b"false"), + TokenKind::Null => assert_eq!(span, b"null"), + TokenKind::Number => { serde_json::from_slice::(span).unwrap(); } + } + } + assert_eq!(reader.finish().unwrap().0, <[u8;32]>::from(Sha256::digest(bytes))); +} +fn json_checks() -> (usize, usize) { + let seeds: &[&[u8]] = &[ + b"0", b"-0", b"1.25e-3", b"true", b"false", b"null", b"[]", b"{}", + br#"{"z":[0,true,null,"space \n\t\u0000\u001f\uD83D\uDE00"],"a":{"x":3}}"#, + br#"["\"\\\/\b\f\n\r\t","\u007f\u0080\u07ff\u0800\ud7ff\ue000\udbff\udfff"]"#, + br#"{"x":1,"x":2}"#, + ]; + let mut changed = 0; + let mutations = b"[]{}:,\"\\0ntf.e-+ \n\r\t"; + for seed in seeds { + assert!(serde_json::from_slice::(seed).is_ok()); + for chunk in [1,2,3,7,4095,4096,4097] { check_tokens(seed, chunk); } + for index in 0..=seed.len() { + let mut candidates = Vec::new(); + if index < seed.len() { + let mut missing=seed.to_vec(); missing.remove(index); candidates.push(missing); + for byte in mutations { let mut swapped=seed.to_vec(); swapped[index]=*byte; candidates.push(swapped); } + } + for byte in mutations { let mut inserted=seed.to_vec(); inserted.insert(index,*byte); candidates.push(inserted); } + for candidate in candidates { + let expected=serde_json::from_slice::(&candidate).is_ok(); + assert_eq!(parsed(&candidate, 3).is_ok(), expected, "candidate={candidate:?}"); + changed += 1; + } + } + } + let mut boundaries = 0; + for prefix in [4092,4093,4094,4095,4096,4097] { + for value in ["é", "😀", "\u{10ffff}", "\0\u{1f}\n"] { + let mut input=vec![b' ';prefix]; input.extend(serde_json::to_vec(value).unwrap()); + for chunk in [1,2,3,7,4095,4096,4097] { check_tokens(&input,chunk); boundaries+=1; } + } + } + for depth in 1..=130 { + for kind in [0,1] { + let input=if kind==0 { format!("{}0{}", "[".repeat(depth), "]".repeat(depth)) } + else { format!("{}0{}", "{\"x\":".repeat(depth), "}".repeat(depth)) }; + assert_eq!(parsed(input.as_bytes(),1).is_ok(),serde_json::from_str::(&input).is_ok(),"depth={depth},kind={kind}"); + boundaries+=1; + } + } + (changed,boundaries) +} + +fn cursor_checks() { + let categories=[WorkCategory::Canonical,WorkCategory::TemporaryOrBackup,WorkCategory::Run,WorkCategory::Phase,WorkCategory::Generation,WorkCategory::LegacyNative,WorkCategory::LegacySnapshot,WorkCategory::DefaultDirect]; + let address=|n:u8| { let mut bytes=[0u8;64]; bytes[63]=n; CursorAddress(bytes) }; + let mut cursor=MaintenanceCursor::new([3;32]); + let mut seen=[[false;4];8]; + for _ in 0..32 { + cursor=MaintenanceCursor::decode(&cursor.encode(),[3;32]).unwrap(); + let mut selection=cursor.selection(); + for category in categories.iter().rev() { for n in (0..4).rev() { selection.observe(*category,address(n)); } } + let (category,candidate)=selection.selected().unwrap(); + let index=category as usize; let n=candidate.0[63] as usize; + assert!(!seen[index][n]); seen[index][n]=true; + cursor.inspected(category,candidate); + } + assert!(seen.iter().flatten().all(|x|*x)); + let bytes=cursor.encode(); + for index in 0..bytes.len() { let mut invalid=bytes;invalid[index]^=1;assert!(MaintenanceCursor::decode(&invalid,[3;32]).is_err()); } +} +fn main() { + allocation_checks(); + concurrent_checks(); + let (differentials,boundaries)=json_checks(); + cursor_checks(); + let mut grant=MutationGrant::start(); + grant.take_entries(100).unwrap(); + assert!(grant.check_time().is_ok()); + assert!(grant.take_entry().is_err()); + println!("{{\"allocation_failure_and_lifetime\":\"passed\",\"exclusive_ceiling_races\":16,\"json_mutation_differentials\":{differentials},\"json_boundaries\":{boundaries},\"cursor_restart_selections\":32,\"cursor_bit_corruptions\":593,\"entry_101\":\"rejected\"}}"); +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-build.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-build.log.gz new file mode 100644 index 00000000..df1e8707 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-build.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-evidence.json b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-evidence.json new file mode 100644 index 00000000..77b79b84 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-evidence.json @@ -0,0 +1,91 @@ +{ + "reviewer": { + "model": "gpt-6.1-sol", + "reasoning_effort": "max", + "fresh_independent": true, + "settings_source": "coordinator dispatch" + }, + "snapshot": { + "base": "88a369f5820c7c21e05a4336f061887cc5f5d52f", + "author_source": "/private/tmp/dc-s3-facts-final-lm75n3xh/repo", + "review_source": "/private/tmp/cleanup-v2-s3-review-_098ctam/repo", + "input_count": 427, + "all_input_hashes_match_after_checks": true, + "overlay": [ + { + "path": "src/storage/maintenance_budget.rs", + "sha256": "3288f29d3133f7f403afcf275d35e8c55fb53cc487b0251b245070e19daa27e0", + "bytes": 12186 + }, + { + "path": "src/storage/maintenance_json.rs", + "sha256": "d6c2162b2aa37d77667900f5491a5e09fb6a66e1c4ff7ba9d93d056794e45d9e", + "bytes": 19665 + }, + { + "path": "src/storage/maintenance.rs", + "sha256": "c5d94f0ac68d9ed81f8dfb060928887f1c2641798236ba60ecb15f9cc9c970a7", + "bytes": 7896 + }, + { + "path": "src/storage/mod.rs", + "sha256": "9ac2905364228f9376c69fbc6347fe84b043b5ca6e63e27046bb5eeedaac6081", + "bytes": 333 + } + ], + "diff_sha256": "016bac264996d0e6e1c210b75327b899a4166ce0d44c1805caf35427f9603676" + }, + "environment": { + "platform": "macOS-26.6.2-arm64-arm-64bit", + "rustc": "rustc 1.96.0 (ac68faa20 2026-05-25)" + }, + "nextest": { + "command": "cargo nextest run --lib --locked --offline --config 'profile.test.package.codebase-graph.codegen-units=147' --target-dir /Users/rabii/Projects/Repositories/codebaseGraph/target --config-file /private/tmp/codebasegraph-disk-cleanup-7t9af0a6/nextest.toml --profile cleanup-s3 --filterset 'test(storage::maintenance)'", + "build_seconds": 17.95, + "selected": 66, + "foundation_tests": 14, + "baseline_ownership_tests": 52, + "skipped": 474, + "retries": 0, + "junit": { + "name": "nextest-run", + "tests": "66", + "failures": "0", + "errors": "0", + "uuid": "06c3bb2f-6268-4a68-b0a6-342ade4215a9", + "timestamp": "2026-10-21T10:48:52.467+09:30", + "time": "2.350" + }, + "junit_sha256": "e25b7e41502e1a46fabfa82fa8c04edd67802310dbf393e9ee0298e517d1b8d8", + "binary": { + "suite": "codebase-graph", + "binary": "/Users/rabii/Projects/Repositories/codebaseGraph/target/debug/deps/codebase_graph-880b2cd34b390f45", + "sha256": "0bb7caad579500d38248ce8b0307b3c4d25407deb9d1ea645959dc2e22c71c61", + "total": 540 + } + }, + "probe": { + "allocation_failure_and_lifetime": "passed", + "exclusive_ceiling_races": 16, + "json_mutation_differentials": 7559, + "json_boundaries": 428, + "cursor_restart_selections": 32, + "cursor_bit_corruptions": 593, + "entry_101": "rejected" + }, + "negative_traits": { + "command": "rustc --edition=2021 negative-traits.rs", + "expected_exit": 1, + "E0277_count": 2 + }, + "msrv_budget_only": { + "rustc": "1.82.0", + "passed": 5, + "full_crate_msrv_claim": false + }, + "format": { + "four_owned_files": "passed" + }, + "confirmed_findings": [], + "cargo_reservation": "released to coordinator after shared-target work" +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-junit.xml b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-junit.xml new file mode 100644 index 00000000..63c76cd6 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-junit.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-nextest.log.gz b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-nextest.log.gz new file mode 100644 index 00000000..0dbd6276 Binary files /dev/null and b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-nextest.log.gz differ diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-source-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-source-manifest.json new file mode 100644 index 00000000..66f9c7ca --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-source-manifest.json @@ -0,0 +1,1710 @@ +[ + { + "path": ".cargo/config.toml", + "sha256": "388ad5407d7e78cae8dacd1c3c556f8c97f43d998c4e09273dcf8fca00eb0f82" + }, + { + "path": ".github/actions/setup-native/action.yml", + "sha256": "76c67a676d283d59c8fed8b1f0db71df540ed0a7ad4b48b976ed9cf161d91a08" + }, + { + "path": ".github/actions/verified-release/action.yml", + "sha256": "b69e532e353090199ff7c8c98e1c71dc1d060cf7207af00c10e65cd8e7ebd230" + }, + { + "path": ".github/dependabot.yml", + "sha256": "960870ef6e187996b45b672a2590490efd9558d6103235234834d34a697318dd" + }, + { + "path": ".github/workflows/ci.yml", + "sha256": "28a2ad81d82476a7d5faa8234b30b4fead29130b4eda480bf046368b454fc9de" + }, + { + "path": ".github/workflows/native.yml", + "sha256": "c707e4eabcef7886436f426cf13bc627082ede7409f0ecc061b5c631fd784a92" + }, + { + "path": ".github/workflows/release.yml", + "sha256": "a5b43d36f805848793c9bcd2c03b456a4c8779dd2d318fcb8be6af081fd32f30" + }, + { + "path": ".gitignore", + "sha256": "c1539fae5f820edf177fb1f3f695d63fcb2e375654ea09bfbd8675d3419f8e47" + }, + { + "path": ".release-please-manifest.json", + "sha256": "cd76ee03fb0ca1f78049405025ad82d24d8c13a7c2a11e2db4556456939b963f" + }, + { + "path": "CHANGELOG.md", + "sha256": "cfde98196d430a4077eff9bff66c7575ff05095f38dc4b58c740eb08bd39084d" + }, + { + "path": "Cargo.lock", + "sha256": "17e5add36d90045704049e45154977fd4d569d5968989e1d7047dea3e965a4a6" + }, + { + "path": "Cargo.toml", + "sha256": "f267cf8583c7eb20a70d381b461ad0e20ecf52d7e1432ba204ae3664add86a1f" + }, + { + "path": "LICENSE", + "sha256": "d6a96f62ba19e3c410e26b40970ff842fb9d29afacaaca1e3b8cab33eb46eb61" + }, + { + "path": "README.md", + "sha256": "79ad620f0a8c4fe84425faefa5406e09ba04f14ae83410f62762cca30163b1b5" + }, + { + "path": "SECURITY.md", + "sha256": "a838500eac12bd53304f485c21052df40435b4ed37cb2823fe63f3ae225e44e5" + }, + { + "path": "assets/architecture_queries.json", + "sha256": "f14a2f43e0f1d1748f39aa0f7a92791e832955cf25a2435357d25bfa1d33a41c" + }, + { + "path": "assets/graph_schema.json", + "sha256": "244fbce950f51ed5db6b52fa8aadb418fdd96d73bc475b4a1a0d1fb66e34d3bd" + }, + { + "path": "assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz", + "sha256": "0954cd079bb3f24c633242e4cf31dfe949c54a5f575cd983edf45fb44b469e7f" + }, + { + "path": "assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz", + "sha256": "f4349356314482b3c802017b56fb7b78efa31b81a85f9804c6f4273be15e6a58" + }, + { + "path": "assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz", + "sha256": "0494bdab7d96d07bc6693a8a62a6b852f64bab2670adbf6bd3c3c6fae57a8f8a" + }, + { + "path": "assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz", + "sha256": "d649d27b7383277cc4764592fd2dd99a9dfd16d91f167da52dc71ed2fe75e5ba" + }, + { + "path": "assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz", + "sha256": "8156ebd29b5bb2d0742994a9a738afefd807740d5ef74f5ab1d15cb2d10a8e03" + }, + { + "path": "assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz", + "sha256": "ff32b9298a5b271fffb8b10f48ef5425da3fa1f2a453c2522d894de7acbab753" + }, + { + "path": "assets/ladybug-extensions/0.19.0/osx_arm64/fts/libfts.lbug_extension.xz", + "sha256": "ac6faf62a5a6bd236ee27899327abcdc56a7bc2fd8267619f767ffe4e7254f93" + }, + { + "path": "assets/ladybug-extensions/0.19.0/osx_arm64/json/libjson.lbug_extension.xz", + "sha256": "b36f55d57125ecf05b8ba6d9c53e4d6d2532d81f09e9a37b8294d8b46e01c957" + }, + { + "path": "assets/ladybug-extensions/0.19.0/win_amd64/fts/libfts.lbug_extension.xz", + "sha256": "ffc724819419c3c9e93e810cde560acea93a445c9d260f1041c3c99aaa75c8df" + }, + { + "path": "assets/ladybug-extensions/0.19.0/win_amd64/json/libjson.lbug_extension.xz", + "sha256": "07c20799a0ba85e43f5ad09fd599b708653b614f07ccd5b5743d7432438ceafd" + }, + { + "path": "assets/mcp_tool_specs.json", + "sha256": "b7fd145e4885b4009e6d8ccecb0300eb6286ca7d117a915934478c64c72ab060" + }, + { + "path": "assets/query_helpers.json", + "sha256": "c91ab54b35de0c26a14cffbb531e0a4374763f4ccb723e6d8d3bac8f2a91b689" + }, + { + "path": "build.rs", + "sha256": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2" + }, + { + "path": "conda-forge/recipe/meta.yaml", + "sha256": "47a6b0c02a84fbfada104cc5806a3629040fd6db83651957a66a8e2ff396590d" + }, + { + "path": "crates/k-wiki/Cargo.toml", + "sha256": "f6b8c6e0814637c4f9def32a93ffbb40eda34cac385399eb3a4675cb945c8a46" + }, + { + "path": "crates/k-wiki/assets/wiki.css", + "sha256": "0c8c4a8b828c92aa838fcbe4d5cb823630aa1d3f9280befc479e6f89cc9cd775" + }, + { + "path": "crates/k-wiki/src/adapters/cli.rs", + "sha256": "b5d5d054a2a063f5b8f881547aa3d34c75aae74dbf94465a0a5ad353c1926bf4" + }, + { + "path": "crates/k-wiki/src/adapters/http.rs", + "sha256": "8ae80aa4601a8776369cbf6426f23ab986c51626d95673561673fb29663f2aa4" + }, + { + "path": "crates/k-wiki/src/adapters/install.rs", + "sha256": "7be961ca1516892e4b63f4c32a8536996bdca1e1f5447d294028972b20c5e4bf" + }, + { + "path": "crates/k-wiki/src/adapters/mcp.rs", + "sha256": "4baad85ade6fcf8f2eb7a69b5c07929bacc783e65d2bb8c3192495604f2b804d" + }, + { + "path": "crates/k-wiki/src/adapters/mod.rs", + "sha256": "9fba7cf7de016731656f301a2ff86a7334ed898bced55df89650511422e5ef84" + }, + { + "path": "crates/k-wiki/src/api/contracts.rs", + "sha256": "019f08b4dad8f1c250b0b01de71f16d262056970979e84dc1c0ae23f45f553ea" + }, + { + "path": "crates/k-wiki/src/api/facade.rs", + "sha256": "559933fe8aa20ac49582617993a31df620f0e89360151fc3ddc0d53ac0a07bdf" + }, + { + "path": "crates/k-wiki/src/api/mod.rs", + "sha256": "cecdbbe2ddda8302be2e99e8e95afe32910df6aa367dc3065270d91cb7bc5145" + }, + { + "path": "crates/k-wiki/src/api/registry.rs", + "sha256": "596fbe985b6e2af9e5cd0b5653baca585572cf9904f9732c7eda9464188ad0d1" + }, + { + "path": "crates/k-wiki/src/authoring/bundle.rs", + "sha256": "4cc974d441227b576dc0ccbb873ea7fe232e1f87f4ecf01b52fc927357e41e32" + }, + { + "path": "crates/k-wiki/src/authoring/mod.rs", + "sha256": "739484eaf8e3e02614c9ef74c36508fb6751aafcb3152ecbe68f9bd73862d533" + }, + { + "path": "crates/k-wiki/src/authoring/page.rs", + "sha256": "4ac9552f9407cc8511c874d1d12e4c290eec933345c51045b0c179a5fe40aaea" + }, + { + "path": "crates/k-wiki/src/authoring/write.rs", + "sha256": "f5b6822859ca46e37c7121420de749eacdcc454863f12261b48e5fd345821dbf" + }, + { + "path": "crates/k-wiki/src/bin/k-wiki.rs", + "sha256": "7688bb538d30ed6d3c1aa5147f52b604f364efd52104466233ba71b88ccfafba" + }, + { + "path": "crates/k-wiki/src/bundle/mod.rs", + "sha256": "89648f228ce2bb1f60106ac451c68c0cfae17d407a7898c4e82e7daa2230614f" + }, + { + "path": "crates/k-wiki/src/compiler/mod.rs", + "sha256": "ba3be8236266cb742b5432484cc8ec4042e3da47dd74259329008ae25a876365" + }, + { + "path": "crates/k-wiki/src/conformance.rs", + "sha256": "d70c4149a337268e82d4cb1bbd9cb069c423c3b59f452de9a19f03eab71bb108" + }, + { + "path": "crates/k-wiki/src/diagnostic.rs", + "sha256": "05d96b05a6c569e7378e6d61ee7ad73e524e60ee0cf29d02537a6be75e2a1204" + }, + { + "path": "crates/k-wiki/src/graph_context.rs", + "sha256": "23fba237d4b6117ac914965a87504a7ffec4c02b958b887f536f7d86f2293e1c" + }, + { + "path": "crates/k-wiki/src/lib.rs", + "sha256": "b38e9b31eabda34565ab5be588cdc291d67006204ba27385b02634be1be24c8c" + }, + { + "path": "crates/k-wiki/src/memory.rs", + "sha256": "8e6402586f2190e536ecb1d6144735a77b08640ef85e554c148d0049cbfbaeea" + }, + { + "path": "crates/k-wiki/src/model.rs", + "sha256": "37495fe19bdb12bcf4247b06b1cb534891e834cf1b13becade4f917e8bdaef57" + }, + { + "path": "crates/k-wiki/src/projection/mod.rs", + "sha256": "91b213bc252e2b895f1fef6e70cde41f93f2989bc7a98712d13a6b99b9845594" + }, + { + "path": "crates/k-wiki/src/refresh.rs", + "sha256": "fa3c1e2199b8ef6ed0a50e8c59d3e5b0819e896ba262673d071f91175d1b1b5d" + }, + { + "path": "crates/k-wiki/src/render/mod.rs", + "sha256": "72d933e017228db06805568232c3dab45ad9bb2caf9336e954b7442f5ed6e8dd" + }, + { + "path": "crates/k-wiki/src/search/mod.rs", + "sha256": "09b2dc7d91aee673ff137f1d09ba0a4320a42f39972ea0ebc45b3f6a723914b3" + }, + { + "path": "crates/k-wiki/src/service.rs", + "sha256": "9aee89282f778101705752e4f597f3b1642b0d92cb464372ab41cc169833a9e4" + }, + { + "path": "crates/k-wiki/templates/page.html", + "sha256": "9322ef692171d3a94683105d0fdf641caa2e8a597b7c5e8d47ff39906f4c9c3e" + }, + { + "path": "crates/k-wiki/tests/api_integration.rs", + "sha256": "1bbfa6812a737c07d7f3b566e82125e24aefdd54409eaf83faa1877b0cc04c00" + }, + { + "path": "crates/k-wiki/tests/authoring.rs", + "sha256": "ccf184723141f0a80c5015d18e467deb70480a57a3da099ffc34207997414f5c" + }, + { + "path": "crates/k-wiki/tests/bundle_reader.rs", + "sha256": "e823f28854b532e109525c961151164cec00c4f24d391249daa428bd18c44831" + }, + { + "path": "crates/k-wiki/tests/compilation.rs", + "sha256": "036e0c6ad7a6ecca6158d3b04a84032b1ddf83aec7f9519a0094ddfc91ec7a6d" + }, + { + "path": "crates/k-wiki/tests/conformance.rs", + "sha256": "2110c007ca8bcb2c77be5f5b2592ab8865d26b422bb4ec2cf0501243f0c27f4d" + }, + { + "path": "crates/k-wiki/tests/diagnostic_transports.rs", + "sha256": "6340ce2a5d43ac63d947ac44909b69bd9cb5129fd2e3e80600e623eb89520a59" + }, + { + "path": "crates/k-wiki/tests/diagnostics.rs", + "sha256": "7b6e8cc14482d99a004f2115607da0ad675d9b113b467d194b97ed3081abca54" + }, + { + "path": "crates/k-wiki/tests/fixtures/comprehensive/decisions/adr-002.md", + "sha256": "ee5c69e25b8f92d32378116a2e42ade139cd4de9f27b8f80c870fd9780c1862d" + }, + { + "path": "crates/k-wiki/tests/fixtures/comprehensive/guides/index.md", + "sha256": "74050508a412220f57386f01eb0b4371f122cf47b8b5a652a39990fe0a4782fe" + }, + { + "path": "crates/k-wiki/tests/fixtures/comprehensive/guides/log.md", + "sha256": "4e0876ba1910e475f81c85fe0f821d31e37860edae1d81a72c740b6ce7f978cf" + }, + { + "path": "crates/k-wiki/tests/fixtures/comprehensive/guides/overview.md", + "sha256": "e43357817795d828af91dee0398c4ecf8ede018147319dfc965e7d5b38638c4a" + }, + { + "path": "crates/k-wiki/tests/fixtures/comprehensive/index.md", + "sha256": "ec44e48209becae3111c1bd883f218923c6b2d3cc885c017824ba26a460fb5d6" + }, + { + "path": "crates/k-wiki/tests/fixtures/malformed/invalid-frontmatter.md", + "sha256": "08243906665df2b1bb64ab1beefe56662b1ca220a89711c0b5899ad0ee572987" + }, + { + "path": "crates/k-wiki/tests/fixtures/malformed/log.md", + "sha256": "d26bf0adce832f845e82a01e6e2d84c00c35383d7e04e86f1ac25ff76f2eda34" + }, + { + "path": "crates/k-wiki/tests/fixtures/malformed/missing-type.md", + "sha256": "8616868d3786290e5241ae7af997745318573f6dc623163d721708eede778174" + }, + { + "path": "crates/k-wiki/tests/fixtures/malformed/nested/index.md", + "sha256": "c9d1642dafd370c907cf06ae498162b9447f3648eea2729e862eeb249137a3f6" + }, + { + "path": "crates/k-wiki/tests/fixtures/malformed/no-frontmatter.md", + "sha256": "9975ae24341afd498f46f74e0e6d03de80b33a857322e4f4f3041a1d717dfd18" + }, + { + "path": "crates/k-wiki/tests/fixtures/malformed/stray-version.md", + "sha256": "d949cba772124200cb4baa256980447f30189eb65cf46c4f0de8a3fc404753a1" + }, + { + "path": "crates/k-wiki/tests/fixtures/malformed/tagged-extension.md", + "sha256": "d2cd20fb5604c48279c8d88a5f52e7d83e7ceb5115cf26e5345d8f79badbbfa0" + }, + { + "path": "crates/k-wiki/tests/fixtures/minimal/decisions/adr-001.md", + "sha256": "cf2c2dcb1b5f1ed96212aec1a1f3f463d9be5b0bcf11639221ced40edb2cc63e" + }, + { + "path": "crates/k-wiki/tests/fixtures/minimal/index.md", + "sha256": "57294be90498ad361c2156ac3f4986572b672713299597587d7612254819cd63" + }, + { + "path": "crates/k-wiki/tests/fixtures/multi_bundle/alpha/index.md", + "sha256": "e91e48f91cff58e2249470c36da84313fc043b8b32ba0367d525be78dc7d7124" + }, + { + "path": "crates/k-wiki/tests/fixtures/multi_bundle/alpha/page.md", + "sha256": "893778b136b09da256a4801c71ada991ba7806e2b1dc3098f5d56e0556cf5758" + }, + { + "path": "crates/k-wiki/tests/fixtures/multi_bundle/beta/index.md", + "sha256": "cf2d6b5e006222ca29ce1fc532f0331d7d51d22282abb605cd26fb9c8119aaa0" + }, + { + "path": "crates/k-wiki/tests/fixtures/multi_bundle/beta/page.md", + "sha256": "4b2e49cd21250f1efdba35b787578f147779e9abb3871c1d45c5298133ab83c8" + }, + { + "path": "crates/k-wiki/tests/memory.rs", + "sha256": "a4b8b4e03c2ed19d6169bab98291f89d81108736d642fb3aec3657fa08949262" + }, + { + "path": "crates/k-wiki/tests/projection.rs", + "sha256": "6add0a4bbc02401cb5ebd733ebba64d7e47e3c6f97f60d842f6cbc40df2da314" + }, + { + "path": "crates/k-wiki/tests/refresh.rs", + "sha256": "a8b90c02df7a6ced0ef78b9055ba25fc92cef0f2af2b00d9f1383971114ef82d" + }, + { + "path": "crates/k-wiki/tests/rendering.rs", + "sha256": "ea6328e283ec169824fad347d446a79e9ef0e4f5d10fcad8664bc862b991b014" + }, + { + "path": "crates/k-wiki/tests/search.rs", + "sha256": "73cf862aa1e154bb830f43d6d64d237ecd0e7f9924af7d3add37d4a93f58e002" + }, + { + "path": "crates/k-wiki/tests/transports.rs", + "sha256": "4908e8f16af4e57fbfe56158986d7c4cf807f5bbfdf4ca85203f48ada1959830" + }, + { + "path": "crates/xtask/Cargo.toml", + "sha256": "33982fd763e2b03e72ae459e516bd76925c7f9a033b539be7b6ec6ce13c9edef" + }, + { + "path": "crates/xtask/src/main.rs", + "sha256": "a4eb81730661eddb10a133c3c106c492cd611b5d7b827996edcd81a797e713e9" + }, + { + "path": "crates/xtask/src/release/workflow_tests.rs", + "sha256": "549e2b6cfabfe0e60717dc186ecf94b1a3e598d7ee670857460f1f3bb1b59861" + }, + { + "path": "crates/xtask/src/release.rs", + "sha256": "dc74317037a68df27c98c24678d2b46589ef64fc1ef0d484b415cae33435ebd7" + }, + { + "path": "docs/assets/codebasegraph-map.png", + "sha256": "fc11a221d1a64ee9fd7c82d1436a11516aff9b6c0a7f4d0c8cb516417b5de328" + }, + { + "path": "docs/graph_output_token_comparison.md", + "sha256": "da24d64e7e78edcd74ff0d8c76498af037740e3caf8c71fab092127fac08d332" + }, + { + "path": "docs/k-wiki.md", + "sha256": "a88834489a890251912da9153df030eed7577cd71382c8d1af15b85d8e377a8a" + }, + { + "path": "docs/mcp.md", + "sha256": "6267772875d91ce2c63840248933b1056ad0f175d91351e7e057e07ea05ceb83" + }, + { + "path": "docs/readme-ux-rewrite-proposal.md", + "sha256": "cb6b3d736a571d3609e87b0bdad3f935afa7ba7355c743a1cb0bac43059a99ed" + }, + { + "path": "docs/release.md", + "sha256": "e7456beda70e58b2049ed13f1484731544a9fb89cf49d8bf0f7d2435723d02bf" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json", + "sha256": "c750c93245f79df2379200a4a43fb8b5b94c48a7d351b22da1eefb8e05bcc91d" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr", + "sha256": "37deacba5b2b624d0962949aa5cb60ce368a873c072cfff764cf836bb098a66b" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/audit-regressions.stdout", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/audit.py", + "sha256": "316630c38e1638370b8c1873174d4311cc32ae4d0d9bcbbf0045e1083ccfb518" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/foundation-reports.json", + "sha256": "88eff4652ee0d4914980912c2a9600481bb891bc539706fea1677062133b3bb8" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regression-execution.json", + "sha256": "55763bb75fe573cf97fbe8878687d8e12c30836fa6ee991ee7b7a71451779611" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stderr", + "sha256": "e4ed00080d9030eba59f6c9af6ac3d6b784e69ad1e9dcb2ce9107c19fb07be0d" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stdout", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json", + "sha256": "0dc7bd221b6d210f260677b1c2027b707a3bd0f8a0a314d35eefb64d0402e151" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json", + "sha256": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json", + "sha256": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/review-outcomes.json", + "sha256": "bac2d4ad1d32aa54f709c6ccfd34b4a3b5917f0822d3524e1b8f70378cc5ccbe" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml", + "sha256": "43789b2717a390d201e486c0c1def0e0f7aa8cfe3548a61fe2ee4fc8546e0603" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml", + "sha256": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json", + "sha256": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml", + "sha256": "ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json", + "sha256": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml", + "sha256": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json", + "sha256": "65d9f8a5c1124a215f66e63f5819f60aef52382559cb627d807ce7a7d5bab718" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt", + "sha256": "ffd58ad5367a31890d30ae14515523e74e76c810742d011f028c46a4729ae6ed" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json", + "sha256": "95f89f93b01ad3c9b02cbab6b97278cdbb14d604080963a4a2b8a08deee39c5c" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s3-expanded-zero-budget-probe-output.txt.gz", + "sha256": "9deb649f914ef65bc0ad3dc7632409c2b4d4615091c8ee74781b4e390abf806a" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml", + "sha256": "8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml", + "sha256": "c4d0cbb89af4996d859c7da5b1bed3b04a1a9b90dd44135bc7c9d2d2c9ecfb3f" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml", + "sha256": "3b8c4e663c2c93d3c52f93da6af4b17096b529deff117ce4f9b191ad766e44b7" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "sha256": "c2dd357024899ff4a64e9d4de15f181a4f9b827e460e509d2ed7e8b1a09ba3f3" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml", + "sha256": "8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json", + "sha256": "ea74ad1208875efa0e56d2f168bcec49c409cfe9219387e8e0b3f85a15645f86" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json", + "sha256": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json", + "sha256": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json", + "sha256": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr", + "sha256": "38b1121bacbb008a468e084a6453f3b81fb7972fc81e6670176e212381b8aa3e" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout", + "sha256": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json", + "sha256": "a0dbc8b2f618b3e0c2157cdbbdba9e6a58676e8b02d5210e75263d33cb287651" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.json", + "sha256": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stderr", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stdout", + "sha256": "aa2490c387a1493390b282b9b25b1efab55d8b809c519da8f98d10e76c9eaba3" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr", + "sha256": "df966e27aff804782c1143e8aa9325367cfb66ead53b60fdb86348e6175bea1a" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stdout", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json", + "sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json", + "sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json", + "sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr", + "sha256": "3789ba8ed22ae4a82b1fc7adaea6cd8d86d3c94beb1f30f698bd4afb3f7e6ff0" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr", + "sha256": "d75191f480933414ac1b27956d159e407be16eecac57c0d782ec127207ea333e" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-narrow-audit-fix-checkpoint.json", + "sha256": "330a275563716dcd528c145b9ff785ac7ab686088a86572d1379f11aa9d278be" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-narrow-checkpoint.json", + "sha256": "47dd5518ed1d1cc5dac128acda06e2431ced50586af17fd79bc27245861f1db9" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json", + "sha256": "ff7043693f6cd3bf978921262ae7c9e83ad4772bf760d1db777f169f93694600" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence/test_audit.py", + "sha256": "b317ac2a5f4ab7ec3c52e98169e37090d65c9a7a92d397bd898962516f6f545c" + }, + { + "path": "docs/testing/graph-disk-cleanup-evidence.json", + "sha256": "8cb9670ace63fc10d02447c4f612f306242a060e7d1882e4992cae7ba6347d40" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-admission-reproduction.tar.gz", + "sha256": "0c8f21fb29f1137b5f509aa0a904d2ae441729bb935a1e02aaed8d5c8d80a5b9" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/direct-read-registration-fix-verification.tar.gz", + "sha256": "9141fd2c55d0a8b074f6e0b324c4984c5ba0e429c6dda15a7160af01aef1b722" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-junit.xml", + "sha256": "698d41772c871f1398e5cf926c415a93af41ef65bd73b298220323266a7e761f" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-verification.tar.gz", + "sha256": "3703829e77e3f4d2f47b64b40e187bbda7b3e66b7628b1e73f5f7ab0757b2a74" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/integrated-fix-verification.json", + "sha256": "5fd30a710e42520ee3732b73470870687f8511830cca98ce2e218062abd3279e" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/integrated-foundation-review.json", + "sha256": "f3286fa16726d54717d3fa90d79ed817e86878af3a6bab13f416462d9ffe9549" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/integration-binaries.json.gz", + "sha256": "2a9cf8f1e9abbcb72dd0b84efa3dcc3fc7e670d780dff63603c2b5fcfd0046b0" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/integration-check.log.gz", + "sha256": "d8568eedc674aabe90e1a136722e71275602269e4132c7a90654ccb28c563911" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/integration-junit.xml", + "sha256": "3562f1cb18f27b76ecd94f3b71770d2659b91836e24c37f9bebc7c1c2adb8e76" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/integration-tests.log.gz", + "sha256": "aeb4eca36c07170d46718e713a6b6a568b2786295a3ede992dd802a41d3aa221" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-independent-junit.xml", + "sha256": "640d1dc448e77ea726b0468255a488c9badce39a955176e7fa63158263fcdc76" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-rebuild-provenance.json", + "sha256": "acef411c4c6d5a68565001d23d69da28bd483350e1ea74dfc64a15e98fbbfdc0" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-independent-junit.xml", + "sha256": "3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-review-evidence.json", + "sha256": "6101c50b30b24228d473ff86ab98c4d6d16128fd8e98195705df595ea5d22729" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/s4-reused-target-excluded-junit.xml", + "sha256": "ed70ff0327920ea194f4df6fd76ceac547cd67db2ccdc03b96c06f8c9c4d408c" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-evidence/unfinished-work-and-scryer-source.tar.gz", + "sha256": "b938db71e07857f73dabfc852c94e6006321caf0dc360c3cfbe0250423066294" + }, + { + "path": "docs/testing/graph-disk-cleanup-foundation-integration.json", + "sha256": "f33d2a4410e0c2b6785c14bda9330f878973827751fa65e3f4521c98a46168d0" + }, + { + "path": "docs/testing/worker-recovery-evidence-2026-10-20.json", + "sha256": "a74d172ee20714ab3d61341b04cde56acaee60e84091ef660868ba046f4227d0" + }, + { + "path": "docs/troubleshooting.md", + "sha256": "4ef9e29cb7edac9d1918d9dfe5e7316fda4a4a322c2b4c313c21d6854b74e2cd" + }, + { + "path": "knowledge/architecture/agent-loop-hooks.md", + "sha256": "fa6d63c3202ff1e113028c7a8567293410599ac6b3307bd7a1cd90f2bbe86029" + }, + { + "path": "knowledge/architecture/agent-memory.md", + "sha256": "54c5a3591a4f96c1ad3f100f8dad1529bccebc437fdc6cedb16118363e141527" + }, + { + "path": "knowledge/architecture/graph-disk-cleanup-contract.md", + "sha256": "ee96ef50c37f98126a4e907b70317a413d3ac5a2813f3d60f85d65fc7ad3cdd1" + }, + { + "path": "knowledge/architecture/graph-disk-cleanup-spec-reviews-2026-10-20.md", + "sha256": "c47a3e6be27346ed997cc5b43ef7433f4251ef103005fc06af1f8659a1abc9c7" + }, + { + "path": "knowledge/architecture/graph-disk-retention-review-2026-10-20.md", + "sha256": "a3af205590764a958631ee9bbb27e82a056b11648f7278c1fb45eda785e81bff" + }, + { + "path": "knowledge/architecture/graph-freshness-recovery.md", + "sha256": "08f20247d9c730a247179a66787bebc9efb540feff4cc6cf3e82cbbf631a6872" + }, + { + "path": "knowledge/architecture/graph-runtime.md", + "sha256": "f32d3ac114ee6fbe0f3303ef35ad239d9ee633bbbc8499474a72d4ec65911bd4" + }, + { + "path": "knowledge/architecture/graph-storage-lifecycle.md", + "sha256": "953eebfcdca3fec7e0d520767f0d09ec6c465d6ba4f4411e255f2d0a6c9a8353" + }, + { + "path": "knowledge/architecture/invariants.md", + "sha256": "778c4fe26697b8947f0b08c7ec70f85ba5c1718656002edb9ac8a77d922b015a" + }, + { + "path": "knowledge/architecture/knowledge-wiki.md", + "sha256": "b1738e07955e3529569852c0a7f7ae1014561ca43a39cc04c92cf6d65c74885e" + }, + { + "path": "knowledge/architecture/language-css.md", + "sha256": "da78f20e76ffe519161cd6d8e0c13df113ad9ee7508ab9c6512026bc82d25a15" + }, + { + "path": "knowledge/architecture/language-html.md", + "sha256": "2e2b1a2a1997b81c735361d8acc72027c1fe919f15ffba443bb92e5eb70ed512" + }, + { + "path": "knowledge/architecture/language-javascript.md", + "sha256": "06e7603156ee08348414c0b903dffb000805bb2c868fc3fbb77d974cc33259db" + }, + { + "path": "knowledge/architecture/language-typescript-tsx.md", + "sha256": "d6520c8d3d01e5681c8984f6d610698b4a49c1c88b73b026936ec9dae62bf46c" + }, + { + "path": "knowledge/architecture/language-webassembly.md", + "sha256": "e5f2308d5c7fef6009e335a7b9b3cfa9b77bc8ef8dbb61760d0dedfd5a55b3ca" + }, + { + "path": "knowledge/architecture/materialization-pipeline.md", + "sha256": "99a5056c059cd9c499897845c26a15a4dbc460729cebed0b0b22f31b22f4ade9" + }, + { + "path": "knowledge/architecture/operation-paths.md", + "sha256": "f700053df572091aed938c64bfb63c3050f99d312843d5908c22f5518b283657" + }, + { + "path": "knowledge/architecture/overview.md", + "sha256": "3710c89eae9a6d3f30899b0f1b9e4948c4b9fb09bae34962f507ef9a85dd7e78" + }, + { + "path": "knowledge/architecture/release-verification.md", + "sha256": "099f8dee41675237965ca1d54d61a506de96fc3ffbcd674dfa97a4552d32b649" + }, + { + "path": "knowledge/architecture/repository-map.md", + "sha256": "ad0273bd362d37346e16b8fd446a65d62462f3ae7b5ce1e502f5e44961d1c0f1" + }, + { + "path": "knowledge/architecture/worker-recovery-regression-contract.md", + "sha256": "ade6d75a85a272e993b3e5752def9307782915a9fd40843f82eee6061f49632c" + }, + { + "path": "knowledge/index.md", + "sha256": "87aece4723c5682ea96f4569b0b1813d48ae6c59d285ebd9c8a42c9be211a474" + }, + { + "path": "knowledge/memory/episodic/agent-hook-live-mcp-budget-eagain-2026-09-23.md", + "sha256": "e1bfe57c3611584a4355357ebf9836b3b16a07b3cbfc1b39cc725c75eeaf2cb3" + }, + { + "path": "knowledge/memory/episodic/atomic-temp-path-process-scope-2026-08-21.md", + "sha256": "873044c1ded70c01d5e048de1d234a37d391a2927e18136da2ac6216be535027" + }, + { + "path": "knowledge/memory/episodic/ci-release-artifact-recipe-drift-2026-08-13.md", + "sha256": "8abc08782c4de0b0732d3e17d83389f936b161cb72ed9071f4424a4708491811" + }, + { + "path": "knowledge/memory/episodic/config-root-loss-stops-refresher-2026-09-08.md", + "sha256": "25749a43e8d29514164f3a0b9ea3a0aaa0873a02f858a3b29dee6b22f81da84d" + }, + { + "path": "knowledge/memory/episodic/containment-proof-must-survive-candidate-cleanup-2026-10-15.md", + "sha256": "34c176cc6f641df51959f04d0694cf7ead4abbcefb218cac533bb4796d1e80ae" + }, + { + "path": "knowledge/memory/episodic/coordinator-frame-timeout-retry-2026-08-21.md", + "sha256": "80bf6d574cda7e7725b517fcd5d2d9d48e48fa79bc72d050bfa53e456d5e1ecf" + }, + { + "path": "knowledge/memory/episodic/coordinator-stale-route-authentication-recovery-2026-08-20.md", + "sha256": "cca5202d3efd03cedef7fa09ee3e502bbf78e224cc101303042b99c4cd2455ba" + }, + { + "path": "knowledge/memory/episodic/coordinator-watcher-readiness-race-2026-08-21.md", + "sha256": "19825664cf3499746857f6f02edcced26605a03755b054e09ef5ca048cb8b41f" + }, + { + "path": "knowledge/memory/episodic/copilot-cli-native-hook-payload-omits-event-2026-09-22.md", + "sha256": "32345f16b266bb7f2cb9f2c36e908888fea928837d2f8d216b9379c15e3fea58" + }, + { + "path": "knowledge/memory/episodic/database-use-unicode-alias-parent-family-2026-10-21.md", + "sha256": "01d6d14432a32b2bf3f43244a5b8811426e1250df7d93c65837543acfb50a84d" + }, + { + "path": "knowledge/memory/episodic/direct-destination-spelling-recovery-identity-2026-09-08.md", + "sha256": "ed15cd682e7725d2a0366ecc883ea889fc688b9516c2b4d5ba051186759b7d95" + }, + { + "path": "knowledge/memory/episodic/direct-prepared-replay-deletes-promoted-sidecar-2026-09-23.md", + "sha256": "b792bbcc60b629ce871d350b77455173bf147ac1e84d6c9bcf1bcac428d279dc" + }, + { + "path": "knowledge/memory/episodic/index.md", + "sha256": "98d6b26ae479e29b0253cf2de40ac081bb4fe08f27ea07efd64fd76a8c2e1308" + }, + { + "path": "knowledge/memory/episodic/k-wiki-domain-errors-need-wire-boundary-regressions-2026-10-16.md", + "sha256": "86a67e567ff6299810850668095411b5ca671ab4050f1e5393af1d821b2e4356" + }, + { + "path": "knowledge/memory/episodic/k-wiki-yaml-errors-need-origin-redaction-2026-10-16.md", + "sha256": "dd36fbe95e9edade8fdaf983549f121dbb4d3a3c489d7547b75b3025fc1b9db1" + }, + { + "path": "knowledge/memory/episodic/lifecycle-fixtures-must-recognize-retired-startups-2026-10-16.md", + "sha256": "a9a22fc2bb20309451aac96df351748b4c6226bec7c503b0ab7fd18364391b3d" + }, + { + "path": "knowledge/memory/episodic/macos-current-rss-materialization-supervision-2026-08-18.md", + "sha256": "96013fdf89242f107a8b506e883dff0ae037d0b5d3294e461857b8cd59a50267" + }, + { + "path": "knowledge/memory/episodic/materialization-reference-memory-gates-2026-08-18.md", + "sha256": "de4ef170b04a3e979f37c5242f39e74d549b3916876d97bd0069ad0facef75ef" + }, + { + "path": "knowledge/memory/episodic/mcp-daemon-client-disconnect-broken-pipe-2026-09-22.md", + "sha256": "c3c46bbb6a371af191fa82a7f2b113c9dec73fd90a20fa96b75cfce604c48fb3" + }, + { + "path": "knowledge/memory/episodic/mcp-refresh-rebuild-convoy-memory-overload-2026-08-14.md", + "sha256": "b6d8d43d95a6a106ed8d171522593bbb8c3cb265323d7d00ab5f604e3de01ff0" + }, + { + "path": "knowledge/memory/episodic/native-startup-backend-label-ci-2026-09-08.md", + "sha256": "6f51c9792ca69a08f4ae5b0b0026d083da270740c35e843a8aa0904490d1da03" + }, + { + "path": "knowledge/memory/episodic/native-wiki-smoke-needs-recommended-metadata-2026-10-16.md", + "sha256": "19616adf6edc905ab2ef5c1946d6435cc76205adaefdc41831eaabe6281fb056" + }, + { + "path": "knowledge/memory/episodic/recovery-fixture-caller-and-handoff-preconditions-2026-10-20.md", + "sha256": "1b1d95b8a78540f0d7cfc017a6eeec9184136b3e25d23c15181ef2c89626dd1f" + }, + { + "path": "knowledge/memory/episodic/recovery-review-branch-and-live-version-alignment-2026-10-20.md", + "sha256": "d44b6c2c037b34f29903b62d907e7ba55e34d627a4430bd5f9405c941b15d681" + }, + { + "path": "knowledge/memory/episodic/slow-reader-test-waits-for-response-transmission-2026-09-24.md", + "sha256": "16992cc061c05a114d0fab558ed0b86283767b0611d2903cf4d0281417945e26" + }, + { + "path": "knowledge/memory/episodic/stopped-http-daemon-transport-send-error-2026-08-24.md", + "sha256": "0ec06e9cfc462cf20f6c264905db3943cfb8ea4635d57c29feee8043f97792a8" + }, + { + "path": "knowledge/memory/episodic/successful-release-merge-starved-by-main-tip-guard-2026-09-23.md", + "sha256": "2af2037c6f0ff099b602fe17193db91233e24d0bc6ed15975879b97a730d4f02" + }, + { + "path": "knowledge/memory/episodic/untagged-release-pr-blocks-later-proposals-2026-09-24.md", + "sha256": "efdf88a699dc3543ba8acae4c2912d1227cbf978c56f451f158daf6ce705aa24" + }, + { + "path": "knowledge/memory/episodic/wiki-recall-utf8-snippet-panic-2026-10-01.md", + "sha256": "c323039532274caf784ffa6c0eaffc8f15cd60386427cf0d8ee3427d0961dee3" + }, + { + "path": "knowledge/memory/episodic/windows-direct-materialization-path-limit-2026-08-13.md", + "sha256": "689e41a8df9ae3d8d8eb1e2afdc5a89a1a8116f071a2b7670a2dd317e80d93af" + }, + { + "path": "knowledge/memory/episodic/windows-emptiness-probe-must-not-terminate-jobs-2026-10-15.md", + "sha256": "fb8f2f903d100211666c0850a600d6b3e8286c369dffbdfe936a5af69aeff94b" + }, + { + "path": "knowledge/memory/episodic/windows-mcp-daemon-state-publishes-before-api-readiness-2026-09-21.md", + "sha256": "e7db20c695cf90435fde23e54f4c113bb0ff38c8097154e5cc95926e8d9d2148" + }, + { + "path": "knowledge/memory/episodic/windows-notify-verbatim-watch-paths-2026-08-21.md", + "sha256": "fc1c15f94a6beac896dbe4fbc0c7851172c7307435c0bd2c0383d5f54c00cc78" + }, + { + "path": "knowledge/memory/episodic/windows-watch-test-marker-lock-2026-08-20.md", + "sha256": "5a7c02c054f3c6b1700e65ecbf559af5099736022ab37469a81e19bdd71ab078" + }, + { + "path": "knowledge/memory/index.md", + "sha256": "7838cd3370acc8f93714d31822cae464af95c2de077d31d69fa6909d27dc1d19" + }, + { + "path": "knowledge/memory/procedural/agent-hook-lifecycle-stale-ownership-and-daemon-2026-09-22.md", + "sha256": "a79c40872b1aa67ce79b221325fb2d818d60020a8d49af336ec523004c816a64" + }, + { + "path": "knowledge/memory/procedural/agent-hook-lifecycle-transaction-and-ownership-2026-09-22.md", + "sha256": "5bd6caafd6776e60d03e195882be02b0fd1e410aede45919c8d5abfbf8fe7b63" + }, + { + "path": "knowledge/memory/procedural/agent-hook-verification-per-event-and-portable-temp-paths-2026-09-22.md", + "sha256": "fc668276ba53c62ea1d5c9bfe29a1dd1f7be5422fbb10ced0f1e00305f4cd248" + }, + { + "path": "knowledge/memory/procedural/cargo-package-ignored-metadata-clean-worktree-2026-08-25.md", + "sha256": "04e53b15116be3d12b57ee66b941d3edfe523075655509561c78eb53761f283b" + }, + { + "path": "knowledge/memory/procedural/checkout-free-gh-release-repository-context-2026-08-17.md", + "sha256": "00f0df984ad6a6401f38f8d5fa5cd34386274c85cde44b511fee73b200607c43" + }, + { + "path": "knowledge/memory/procedural/crates-io-package-size-gate-2026-08-24.md", + "sha256": "3ec14615163e2ed7ebbcd06b8f9e36428a49066f6f501ec2b8bb6233dd8bdd3c" + }, + { + "path": "knowledge/memory/procedural/crates-io-publish-idempotent-retry-2026-08-24.md", + "sha256": "1185a270b17580ff4f4b2f2efc6e1c90ab063de4d5f8825142fea221c80b4974" + }, + { + "path": "knowledge/memory/procedural/exact-commit-release-publication-and-recovery-2026-09-24.md", + "sha256": "96659e7bb666dcf239dbb18fe49e6aabe514dbae79a6a864a3a65b1db8147a36" + }, + { + "path": "knowledge/memory/procedural/index.md", + "sha256": "f90a5641d854a85107a58e99505c7b01db844cf895a291825c66d85bea760546" + }, + { + "path": "knowledge/memory/procedural/ladybug-0183-link-and-extension-alignment-2026-08-14.md", + "sha256": "8c12286fb0e657ef282794491fcd7a1dd83f6e8fcd2d2cbb09c3d1a98243cbed" + }, + { + "path": "knowledge/memory/procedural/ladybug-macos-x86-compiler-runtime-2026-08-14.md", + "sha256": "65f3868ea257af1e98d29e5a4f2a92c93e72ad6291ca75904623f0257bbc9a33" + }, + { + "path": "knowledge/memory/procedural/ladybug-release-extension-cache-variants-2026-08-13.md", + "sha256": "adaeb22ab532a90ad3305150b2f6d67c06158561eda9146821f79c694e7d0150" + }, + { + "path": "knowledge/memory/procedural/large-self-hosting-rebuild-memory-budget-2026-08-28.md", + "sha256": "55a2284660069450bb37c7ba4b9c518a1aca1e731817d80ceaa9daf60eb54bef" + }, + { + "path": "knowledge/memory/procedural/mcp-restart-session-aliasing-regression-2026-09-24.md", + "sha256": "8acf4b3da1f8ba705517e0ee975c3ad3cbcc2101f89c99e8e45b0b6536140215" + }, + { + "path": "knowledge/memory/procedural/refresh-context-metrics-clippy-2026-08-17.md", + "sha256": "4ac71e4ec991f32706563edbca2e798ee686165273fdf520cd1a92a6c4e88352" + }, + { + "path": "knowledge/memory/procedural/release-please-stale-pending-tag-guard-2026-08-17.md", + "sha256": "2eca810128ef6ba7632cbb7dabb5b7f1f8592685798c498e943d201c38a1c541" + }, + { + "path": "knowledge/memory/procedural/release-publication-skipped-ancestor-chain-2026-08-17.md", + "sha256": "57d7a0c9d0b155decf9923158b2604983592aaee0dd0a1b18f806b0396b98b7f" + }, + { + "path": "knowledge/memory/procedural/rust-modular-code-procedure.md", + "sha256": "01d0bbcab9943839ffe38712fa697b896dc075ac53e533451c190537a635c0d8" + }, + { + "path": "knowledge/memory/procedural/rust-performance-memory-efficiency-procedure.md", + "sha256": "9659831c4a9111858ac326818e94cd2247f97c3b1f0b02efe41fa535d4821291" + }, + { + "path": "knowledge/memory/procedural/rust-secure-coding-procedure.md", + "sha256": "7a81bc947232c400fde58e53f77174f8c3d49ba77dd216d9ef4d1fc2ed237495" + }, + { + "path": "knowledge/memory/procedural/shared-cargo-target-frozen-binary-provenance-2026-10-21.md", + "sha256": "1cddca5ed7337fb0ffb24626cc21a621fbd5339526e1c1e275a7de88b2594e5b" + }, + { + "path": "knowledge/memory/procedural/shared-http-daemon-upgrade-requires-registration-migration-2026-08-21.md", + "sha256": "bb0d2d4241670411eb82fab1a14aaab667d79c726b9d94a08f55a5ff165debd8" + }, + { + "path": "knowledge/memory/procedural/stable-nonblocking-hook-cache-claim-lock-2026-09-24.md", + "sha256": "cd7aeca0b0bbb525bfeb55e0276b7aa981a740b72973e41c9ea731010a35e431" + }, + { + "path": "knowledge/memory/procedural/vendored-tree-sitter-scanner-callback-contract-2026-08-25.md", + "sha256": "bf4df0f23f902bede8db064fb0036a9f0199290689dc8601204e9aa2a4b6ea02" + }, + { + "path": "knowledge/memory/procedural/windows-ci-bundled-extension-feature-2026-08-13.md", + "sha256": "01460b3810257f59424363ffe017a3811f4865a831e851acf322441c32774afe" + }, + { + "path": "knowledge/memory/procedural/windows-managed-run-cleanup-disappearance-race-2026-09-22.md", + "sha256": "b4b9fe1959b68837aa11456b6dd7e2e5eac85310e6992d52f1137ec1f527722b" + }, + { + "path": "knowledge/memory/procedural/windows-native-lifecycle-test-profile-2026-10-15.md", + "sha256": "9bf1f41d57a7f47ad9eca3c63f13bd9f66e04611aebf0172618a909bac6699e7" + }, + { + "path": "knowledge/memory/procedural/workflow-run-release-exact-run-binding-2026-08-14.md", + "sha256": "9efd65d18f6aac69fdfde9724ff963ff34c2eae61bc59fc6c778bee461c0078f" + }, + { + "path": "knowledge/memory/semantic/coordinator-predispatch-only-replay-2026-09-23.md", + "sha256": "d11eaa95e5980ae1c1628ca15e2ada897b391c1d2151f7121ba8c92ba1495f30" + }, + { + "path": "knowledge/memory/semantic/index.md", + "sha256": "1500ba656dab6f82f12a492f47ad8f62e09d05dbbec31f919dce65dd89ed9889" + }, + { + "path": "knowledge/memory/semantic/k-wiki-graph-layer-contract-2026-08-13.md", + "sha256": "2acc8b852dc645901506445c260a07510b27f293ff4f5dac4871b5c93697f61b" + }, + { + "path": "knowledge/memory/semantic/mcp-coordinator-worker-ownership-boundary.md", + "sha256": "bebdc0b1a199f9aa6f055b430510bf58d25508066cee03dba7dcaf739fefb655" + }, + { + "path": "knowledge/memory/semantic/mcp-stdio-no-idle-exit-2026-08-20.md", + "sha256": "ec403397fcaf44e7609ac9b67780f3df7dd400bf1c1446bfb79283b02fc94665" + }, + { + "path": "knowledge/memory/semantic/raw-artifact-reuse-rebuild-boundary-2026-09-23.md", + "sha256": "8819ede99354fc445ebe3fb5fdce4f2914b2f0af0fe025ebc54bdaec256fcac6" + }, + { + "path": "knowledge/memory/semantic/refresh-status-dirty-epoch-foundation-2026-09-08.md", + "sha256": "c65da2618578cc251befcc630785e6ad0f5b26be3e46391d294074cd33fa0c49" + }, + { + "path": "knowledge/memory/semantic/semantic-artifact-reuse-schema-upgrade-boundary.md", + "sha256": "ced98c016647c22d94ff8dddfb7f79713345bcebcc1b26ff4de21381fcd7ccad" + }, + { + "path": "knowledge/memory/semantic/shared-managed-mcp-http-daemon-per-repository-2026-08-20.md", + "sha256": "ab4669c486bdd7c0d3cbff612999a66927196ef0bddc4abe0917ca6910155b6c" + }, + { + "path": "knowledge/stability-backlog.md", + "sha256": "b70f603576e685282b7b454ec9039f833daeb780cc0b0992b256550cf13b698e" + }, + { + "path": "release/install/install.ps1", + "sha256": "d91997cffc0aadde95e30f57a180b864685a3cc3b9919a903dc0084809d6ea68" + }, + { + "path": "release/install/install.sh", + "sha256": "ce8a9dee18939fa14cec78ba6c30c99ab2c6473158666756cd4ecb805d830fb4" + }, + { + "path": "release-please-config.json", + "sha256": "56ef4e6d7afada2d1db5747986d928dca733d19f85094d73a2e84cc081b6716f" + }, + { + "path": "src/adapters/cli/agent_hooks.rs", + "sha256": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5" + }, + { + "path": "src/adapters/cli/dispatch.rs", + "sha256": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045" + }, + { + "path": "src/adapters/cli/format/help.rs", + "sha256": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e" + }, + { + "path": "src/adapters/cli/format/mod.rs", + "sha256": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f" + }, + { + "path": "src/adapters/cli/graph/mod.rs", + "sha256": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325" + }, + { + "path": "src/adapters/cli/graph/options.rs", + "sha256": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212" + }, + { + "path": "src/adapters/cli/install/command.rs", + "sha256": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94" + }, + { + "path": "src/adapters/cli/install/mod.rs", + "sha256": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43" + }, + { + "path": "src/adapters/cli/install/options.rs", + "sha256": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0" + }, + { + "path": "src/adapters/cli/install/verify.rs", + "sha256": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b" + }, + { + "path": "src/adapters/cli/materialization/command.rs", + "sha256": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5" + }, + { + "path": "src/adapters/cli/materialization/mod.rs", + "sha256": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03" + }, + { + "path": "src/adapters/cli/materialization_input.rs", + "sha256": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149" + }, + { + "path": "src/adapters/cli/mcp_command.rs", + "sha256": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239" + }, + { + "path": "src/adapters/cli/mod.rs", + "sha256": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43" + }, + { + "path": "src/adapters/cli/reinstall.rs", + "sha256": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff" + }, + { + "path": "src/adapters/cli/setup.rs", + "sha256": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7" + }, + { + "path": "src/adapters/cli/tests/dispatch_materialize.rs", + "sha256": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa" + }, + { + "path": "src/adapters/cli/tests/fixtures.rs", + "sha256": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88" + }, + { + "path": "src/adapters/cli/tests/graph.rs", + "sha256": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7" + }, + { + "path": "src/adapters/cli/tests/install.rs", + "sha256": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d" + }, + { + "path": "src/adapters/cli/tests/mcp.rs", + "sha256": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32" + }, + { + "path": "src/adapters/cli/tests/mod.rs", + "sha256": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d" + }, + { + "path": "src/adapters/cli/tests/watch.rs", + "sha256": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0" + }, + { + "path": "src/adapters/cli/uninstall.rs", + "sha256": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03" + }, + { + "path": "src/adapters/cli/util.rs", + "sha256": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c" + }, + { + "path": "src/adapters/cli/watch/command.rs", + "sha256": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56" + }, + { + "path": "src/adapters/cli/watch/mod.rs", + "sha256": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183" + }, + { + "path": "src/adapters/cli/watch/options.rs", + "sha256": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8" + }, + { + "path": "src/adapters/cli/watch/output.rs", + "sha256": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379" + }, + { + "path": "src/adapters/mcp/block.rs", + "sha256": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f" + }, + { + "path": "src/adapters/mcp/daemon.rs", + "sha256": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359" + }, + { + "path": "src/adapters/mcp/dispatcher.rs", + "sha256": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5" + }, + { + "path": "src/adapters/mcp/http.rs", + "sha256": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b" + }, + { + "path": "src/adapters/mcp/mod.rs", + "sha256": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e" + }, + { + "path": "src/adapters/mcp/options.rs", + "sha256": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805" + }, + { + "path": "src/adapters/mcp/protocol.rs", + "sha256": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799" + }, + { + "path": "src/adapters/mcp/refresh.rs", + "sha256": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181" + }, + { + "path": "src/adapters/mcp/state.rs", + "sha256": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6" + }, + { + "path": "src/adapters/mcp/stdio.rs", + "sha256": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38" + }, + { + "path": "src/adapters/mcp/tools.rs", + "sha256": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b" + }, + { + "path": "src/adapters/mod.rs", + "sha256": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8" + }, + { + "path": "src/agent_hooks.rs", + "sha256": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19" + }, + { + "path": "src/api/boundary_tests.rs", + "sha256": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642" + }, + { + "path": "src/api/catalog.rs", + "sha256": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01" + }, + { + "path": "src/api/catalog_support.rs", + "sha256": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4" + }, + { + "path": "src/api/context.rs", + "sha256": "b038192e4cc8258c94c7c524f9ed565017e45d7e28092b09e5490d4836db0eed" + }, + { + "path": "src/api/contracts.rs", + "sha256": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223" + }, + { + "path": "src/api/core.rs", + "sha256": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462" + }, + { + "path": "src/api/execution_context.rs", + "sha256": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2" + }, + { + "path": "src/api/facade.rs", + "sha256": "51230173cd6ea2767f1ec95306f3d3d2c42a8b07b20e56ec3c7661cbb4b96775" + }, + { + "path": "src/api/graph_read.rs", + "sha256": "0f0f6da64105186d884dab60dfb5e34e3b948a41ad6c8ca494b4904b4b9a1ab4" + }, + { + "path": "src/api/lifecycle.rs", + "sha256": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd" + }, + { + "path": "src/api/materialization.rs", + "sha256": "446e276c15cab8f5a35ad48e2df77ba52475812c904ef04caa545ef216622651" + }, + { + "path": "src/api/mod.rs", + "sha256": "3e2b72f33f55a3670764ecde9d90c844b6cb8d5d59adee131aaef2dd79dd769b" + }, + { + "path": "src/api/normalization.rs", + "sha256": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d" + }, + { + "path": "src/api/presentation_support.rs", + "sha256": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7" + }, + { + "path": "src/api/presenter.rs", + "sha256": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52" + }, + { + "path": "src/api/refresh.rs", + "sha256": "1cebe4ab394d446e4c2b99c34447182b8b2ac4135612040ffda200eefb3ac595" + }, + { + "path": "src/artifact_store.rs", + "sha256": "8a248d0f2a9e33b6b22b75548441de661fc8413a346fe0bb5512d85b1cbfa598" + }, + { + "path": "src/bin/codebase-graph.rs", + "sha256": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305" + }, + { + "path": "src/bootstrap.rs", + "sha256": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645" + }, + { + "path": "src/coordinator/tests.rs", + "sha256": "320fe112d869251467cf5e10b9026cef40a8c7839c619dce69c02e00c3f74252" + }, + { + "path": "src/coordinator.rs", + "sha256": "a3a8491d82dac4be321b0f9ae1561ad6af6f1c16b6e8461b881c2f760185d18e" + }, + { + "path": "src/daemon_service.rs", + "sha256": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42" + }, + { + "path": "src/db_writer/access.rs", + "sha256": "c783ba112f44ce247be532c475b5e39a1bdc8b19db0f1e7d27f627fcf93d3c6b" + }, + { + "path": "src/db_writer/extensions.rs", + "sha256": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0" + }, + { + "path": "src/db_writer/mod.rs", + "sha256": "32510a0dccc98be71174eb0dcd5a4e72c1164d42f1a25d77d85c8bfcc534f5fd" + }, + { + "path": "src/db_writer/phase.rs", + "sha256": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce" + }, + { + "path": "src/db_writer/request.rs", + "sha256": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed" + }, + { + "path": "src/db_writer/rss.rs", + "sha256": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c" + }, + { + "path": "src/db_writer/schema.rs", + "sha256": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e" + }, + { + "path": "src/db_writer/tests.rs", + "sha256": "10fcfc158e6fa8d86ab07cbb2ccec13464488b7ddced929f6b011c0e614f6ec7" + }, + { + "path": "src/db_writer/write.rs", + "sha256": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d" + }, + { + "path": "src/error.rs", + "sha256": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2" + }, + { + "path": "src/execution/mod.rs", + "sha256": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724" + }, + { + "path": "src/execution/parallel.rs", + "sha256": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0" + }, + { + "path": "src/execution/plan.rs", + "sha256": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a" + }, + { + "path": "src/execution/run.rs", + "sha256": "f2b7ef995dd0abdb4a927a3b2a20b9ab39d9f74c238711d1ab7d806c3ad4ed44" + }, + { + "path": "src/execution/timing.rs", + "sha256": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b" + }, + { + "path": "src/hash.rs", + "sha256": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb" + }, + { + "path": "src/lib.rs", + "sha256": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e" + }, + { + "path": "src/materialization_worker.rs", + "sha256": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496" + }, + { + "path": "src/mcp_client.rs", + "sha256": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e" + }, + { + "path": "src/normalize.rs", + "sha256": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0" + }, + { + "path": "src/parser/captures.rs", + "sha256": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6" + }, + { + "path": "src/parser/fields.rs", + "sha256": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0" + }, + { + "path": "src/parser/markdown.rs", + "sha256": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047" + }, + { + "path": "src/parser/mod.rs", + "sha256": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578" + }, + { + "path": "src/parser/tree_sitter.rs", + "sha256": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa" + }, + { + "path": "src/parser/wat.rs", + "sha256": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99" + }, + { + "path": "src/partition_builder.rs", + "sha256": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1" + }, + { + "path": "src/process_lifecycle/mod.rs", + "sha256": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663" + }, + { + "path": "src/process_lifecycle/unix.rs", + "sha256": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d" + }, + { + "path": "src/process_lifecycle/windows.rs", + "sha256": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8" + }, + { + "path": "src/profiles.rs", + "sha256": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37" + }, + { + "path": "src/protocol.rs", + "sha256": "c56b04d18060e0ae2ce43e4c9d5b59535078d7186bfcac819005d2605ae01b3d" + }, + { + "path": "src/scan.rs", + "sha256": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e" + }, + { + "path": "src/search_index/build.rs", + "sha256": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525" + }, + { + "path": "src/search_index/io.rs", + "sha256": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084" + }, + { + "path": "src/search_index/mod.rs", + "sha256": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147" + }, + { + "path": "src/search_index/model.rs", + "sha256": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f" + }, + { + "path": "src/search_index/read.rs", + "sha256": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472" + }, + { + "path": "src/search_index/tests.rs", + "sha256": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b" + }, + { + "path": "src/source_selection.rs", + "sha256": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16" + }, + { + "path": "src/staging_writer/accumulator.rs", + "sha256": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da" + }, + { + "path": "src/staging_writer/files.rs", + "sha256": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871" + }, + { + "path": "src/staging_writer/merge.rs", + "sha256": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1" + }, + { + "path": "src/staging_writer/mod.rs", + "sha256": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909" + }, + { + "path": "src/staging_writer/result.rs", + "sha256": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79" + }, + { + "path": "src/staging_writer/rows.rs", + "sha256": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b" + }, + { + "path": "src/staging_writer/spill.rs", + "sha256": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff" + }, + { + "path": "src/staging_writer/tests.rs", + "sha256": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98" + }, + { + "path": "src/staging_writer/writer.rs", + "sha256": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0" + }, + { + "path": "src/storage/atomic.rs", + "sha256": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f" + }, + { + "path": "src/storage/containment.rs", + "sha256": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077" + }, + { + "path": "src/storage/direct.rs", + "sha256": "c40e99468c2ab0a465b7a5b5ee2a8e755c872d450da5e88cd212d91365691e25" + }, + { + "path": "src/storage/inventory.rs", + "sha256": "86ee641488ad03d8914ccca2c61bdcc3a186791b3b1a5c64a22dbe5cfc7aa958" + }, + { + "path": "src/storage/layout.rs", + "sha256": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987" + }, + { + "path": "src/storage/locks.rs", + "sha256": "c6296037d04a0120cccdc405052046355d721fc2255929c7bace0e4d1a2def2b" + }, + { + "path": "src/storage/maintenance.rs", + "sha256": "c5d94f0ac68d9ed81f8dfb060928887f1c2641798236ba60ecb15f9cc9c970a7" + }, + { + "path": "src/storage/maintenance_budget.rs", + "sha256": "3288f29d3133f7f403afcf275d35e8c55fb53cc487b0251b245070e19daa27e0" + }, + { + "path": "src/storage/maintenance_json.rs", + "sha256": "d6c2162b2aa37d77667900f5491a5e09fb6a66e1c4ff7ba9d93d056794e45d9e" + }, + { + "path": "src/storage/maintenance_ownership.rs", + "sha256": "6566a1cd2f86084f2b4549e499d93854bb8e8c48a15794e9085b7d407ea21d06" + }, + { + "path": "src/storage/managed.rs", + "sha256": "aee08a9c6f09640bde3d2d0a3b50690391e294d308561544e94079c9abe2041d" + }, + { + "path": "src/storage/mod.rs", + "sha256": "9ac2905364228f9376c69fbc6347fe84b043b5ca6e63e27046bb5eeedaac6081" + }, + { + "path": "src/storage/run_workspace.rs", + "sha256": "120d4c61691a97aa4c9bb4c29c2487a3a2b53e45a63ce5288e1f5a0e587eb877" + }, + { + "path": "src/storage/test_harness.rs", + "sha256": "3d94ecbe47000f8dcc77f0f2dff3a7ec3c0bdc5087b0ab3e01b3d78d1b5cdf58" + }, + { + "path": "src/syntax_materializer/arena.rs", + "sha256": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b" + }, + { + "path": "src/syntax_materializer/builder/edges.rs", + "sha256": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde" + }, + { + "path": "src/syntax_materializer/builder/mod.rs", + "sha256": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952" + }, + { + "path": "src/syntax_materializer/builder/nodes.rs", + "sha256": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad" + }, + { + "path": "src/syntax_materializer/builder/semantic.rs", + "sha256": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a" + }, + { + "path": "src/syntax_materializer/builder/traversal.rs", + "sha256": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb" + }, + { + "path": "src/syntax_materializer/capture.rs", + "sha256": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9" + }, + { + "path": "src/syntax_materializer/ids.rs", + "sha256": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5" + }, + { + "path": "src/syntax_materializer/mod.rs", + "sha256": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112" + }, + { + "path": "src/syntax_materializer/relations.rs", + "sha256": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04" + }, + { + "path": "src/syntax_materializer/rows.rs", + "sha256": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b" + }, + { + "path": "src/syntax_materializer/tests.rs", + "sha256": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d" + }, + { + "path": "tests/agent_hooks_process.rs", + "sha256": "364d029cd0d4daf328ba46a1254e1d9a6f5df4e7fc9b0a86bcbf7740b6030478" + }, + { + "path": "tests/auto_repo_root.rs", + "sha256": "09d5d09635409c18b2d9b1da8a2272766b4fa5be63d93efd3aa4fdaf0f93fc76" + }, + { + "path": "tests/coordinator_process.rs", + "sha256": "f58387496162899a4611dda84781c98d87e6252c61844009b7ec7def4b64844c" + }, + { + "path": "tests/generation_storage.rs", + "sha256": "17e4ae34bc255345158f879f48dd6a8124ad07722ff75b8fff3f071dce70e6ce" + }, + { + "path": "tests/http_admission_process.rs", + "sha256": "3dd5533bfd85467027710b37d77be2ebfc84832d23942f3a5398c335b151dbb9" + }, + { + "path": "tests/http_daemon_process.rs", + "sha256": "fc651df00823b1d3f854d6627dc80c0d10227d78597c1adab8e417d8e0a55d46" + }, + { + "path": "tests/native_request_identity.rs", + "sha256": "f624bd73ba6b282f415a6251edca1c8d0e85a7d9bcacad1fe2d020e732c74eb9" + }, + { + "path": "tests/refresh_recovery_process.rs", + "sha256": "158e6795af9b9dd0b812f9ae7df579694c15238b4a0de5c36cca6a603fc63b5c" + }, + { + "path": "tests/storage_maintenance.rs", + "sha256": "c7f5de722a579ea665cacc04ccaf5b05680986f68f5252f62d9ca3d71c1fe693" + }, + { + "path": "tests/support/lifecycle_faults.rs", + "sha256": "c075bf1d9f247920d5052d607de2a42b2c2f76fc0cbdc120f846b42b8cf581f8" + }, + { + "path": "tests/support/storage_maintenance/mod.rs", + "sha256": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09" + }, + { + "path": "vendor/tree-sitter-wat/LICENSE", + "sha256": "28a1c2b92a241a705632869e5c3a5e83a53bb182eb273c851565ae381f12982c" + }, + { + "path": "vendor/tree-sitter-wat/UPSTREAM.md", + "sha256": "edb5f2e65b8131e5ac0f6081e4fa7b8e271034cfe1f8f6577f8f70a7a75ce225" + }, + { + "path": "vendor/tree-sitter-wat/src/node-types.json", + "sha256": "b8ee14131666f1553d8948fa661526b2aa5d4bc5f64aa5f28f662fb33ebf453a" + }, + { + "path": "vendor/tree-sitter-wat/src/parser.c", + "sha256": "ba3659413ddfa7acd66d1739ca96ace762e6d08f2c60a43d22670503fac87ec6" + }, + { + "path": "vendor/tree-sitter-wat/src/scanner.c", + "sha256": "e6197b66843756e44e67ff6e9f61790b7df44c930ed1ba5b5fc1169874fc6d60" + }, + { + "path": "vendor/tree-sitter-wat/src/tree_sitter/alloc.h", + "sha256": "b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea" + }, + { + "path": "vendor/tree-sitter-wat/src/tree_sitter/array.h", + "sha256": "5bdf6ed1a78e3409fd443e085ca967a64c188a5d082aaf7f819bccd53a471c94" + }, + { + "path": "vendor/tree-sitter-wat/src/tree_sitter/parser.h", + "sha256": "180b893c8734778fd32f372dfbc27bd6ad1cd2221f26150b31256ff6716320d2" + } +] diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review.md b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review.md new file mode 100644 index 00000000..cd045f7e --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review.md @@ -0,0 +1,51 @@ +# DC-S3 facts foundation — independent implementation review + +No confirmed P0–P3 defects were found in the reviewed foundation. Proceed with integration of these four files within the existing facts-only clearance. This review clears no collection, origin receipt, usable-backup, full Ready admission, terminal detach, controller, platform or release gate. + +Reviewer: fresh independent **GPT-6.1 Sol, Max**, configured by coordinator dispatch. The reviewer did not implement these files. Applied `/Users/rabii/.codex/skills/adversarial-review-engineer/SKILL.md` and its engineering lenses. All review source and test probes were isolated; no shared source, tests, Git, Scryer, wiki or Linear records were changed. The shared Cargo reservation was released to the coordinator after the independent rebuild/run. + +## Reviewed identity and authority + +- Baseline: `88a369f5820c7c21e05a4336f061887cc5f5d52f`; prior last-code baseline `077a8d8e1e75dbca553a4d9b0f65412ca9c5def1`; draft PR #153 supplies background only. +- Author freeze: `/private/tmp/dc-s3-facts-final-lm75n3xh/repo`. Reviewer reconstruction: `/private/tmp/cleanup-v2-s3-review-_098ctam/repo`. +- All **427** inventoried source inputs matched their supplied hashes after reconstruction and after checks. Archive comparison identified exactly the four permitted overlays: `src/storage/maintenance_budget.rs`, `maintenance_json.rs`, `maintenance.rs`, and `mod.rs`. The three foundation modules are new relative to the baseline; the fourth change registers them. +- Supplied source-manifest SHA-256 verified: `5dfe99c6a4f959caa850ffeb716c047fd951fc9331ca8c33d097b15a85e2c5b3`; diff SHA-256 verified: `016bac264996d0e6e1c210b75327b899a4166ce0d44c1805caf35427f9603676`. All 18 entries in the supplied artifact manifest were checked, including the author binary and JUnit. +- Canonical `architecture/graph-disk-cleanup-contract` was read through configured k-wiki: approved revision **0.2**, including the strict guarded wrapper and fresh mutation-only 100-entry/100-ms budget decision. Memory was recalled first; no recalled advice was treated as mutable implementation proof. +- Governing Scryer rules and explicit project `/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo` were consulted. Scope: `chg-6vx5tj`, `node-eje4rb`, `CleanupMemory` / `node-h3vg63` / `resp-hakfnc`, `MutationGrant` / `node-5maag9` / `resp-q679rf`, `MaintenanceJsonReader` / `node-5ges5d` / `resp-a2mw2q`, `MaintenanceCursor` / `node-79mkeh` / `resp-3hxr33`. No architecture claims were folded by this review. +- Graph semantic/slim searches with context 1 and dependency context 2 preceded source reading. The configured older index has no foundation-file hits. Explicit Scryer source anchors and the frozen manifest supplied the new-file mapping. Indexed `GraphPartition`, `GraphEdgeRow`, and artifact loader evidence located the surrounding source. No other project's graph or model was used. Repository Linear consultation returned no matching issue; the coordinator confirmed no S3 issue is part of this supplied review context. + +## Consequential hypotheses checked + +**Unsafe allocation and reference lifecycle.** Reviewed every unsafe block and both manual Send/Sync implementations in `maintenance_budget.rs:17–198`. The Account has one nonzero `Layout::new::()` allocation, initialized before publishing a handle, and deallocated with the same layout by the last reference. All mutable shared Account fields are atomic; `limit` is immutable. Cloning uses a relaxed atomic increment with an abort threshold below `usize::MAX`. Final release uses Release decrement plus Acquire fence before dropping/deallocating the Account. This follows the applicable ownership/order pattern described by the [Rustonomicon clone](https://doc.rust-lang.org/nomicon/arc-mutex/arc-clone.html) and [drop](https://doc.rust-lang.org/nomicon/arc-mutex/arc-drop.html) chapters; it does not introduce weak references or mutable access to Account data. + +Checked-add/CAS reserves charges before byte allocation. Split moves an existing charge without duplicating it; reservation has no Clone. Fallible allocation and oversized layouts unwind the consumed reservation. Exact byte layouts are validated; the zero-length path uses an aligned dangling u8 pointer without allocation/deallocation. Unique mutable access requires `&mut self`; immutable shared access and movement between threads respect the manual traits. ReservedBytes deallocates its bytes in its Drop body, before the reservation field releases its charge. No unsafe defect survived source tracing and isolated failure/lifecycle probes. Refcount saturation itself was reviewed statically, not reached dynamically. + +**Ceiling under contention and grant boundaries.** CAS failures retry against the current used count. A reservation keeps its Account alive after original handles are dropped. Sixteen synchronized two-thread races attempted to reserve the entire same remaining ceiling: exactly one succeeded per race, peak never exceeded the ceiling, and used returned to the fixed floor. MutationGrant is neither Send nor Sync. Its time check permits the last successfully reserved entry, while ordinary check/take rejects new entry 101; rejected multi-entry requests do not reduce the allowance. Time is cooperative and checked before starting work, not a syscall deadline. No authority or filesystem operation exists in these primitives. + +**Lexical tokenizer facts.** Audited object/array state transitions, trailing data, number syntax, UTF-8 validation, surrogate pairs, checked offsets/decoded lengths, token spans and hashes in `maintenance_json.rs`. Buffer allocation is fixed at 4096 bytes; container state is fixed at 127. The ordinary artifact loader uses `serde_json::from_reader` without disabling recursion. Cargo.lock pins serde_json 1.0.150; its actual source initializes depth 128 and rejects decrement-to-zero, matching the implemented 127-container boundary ([pinned primary source](https://docs.rs/serde_json/1.0.150/src/serde_json/de.rs.html)). Independent object and array fixtures tested depths 1–130; short reads, Interrupted retries, UTF-8/escape crossings around offset 4096, malformed inputs, decoded digests and exact spans were exercised. + +Hashes remain raw and whitespace-stripped lexical hashes. Object order is not sorted or semantically canonicalized. The isolated differential harness deliberately enabled serde_json `preserve_order`. Unicode escape flags are conservative facts, not a complete producer recognizer. Lexically valid numbers may exceed typed loader range; nonfinite serialized null, defaults, duplicate keys, raw-row joins and schema compatibility remain outside usable-backup/origin authority. Existing tests and comments accurately retain those distinctions. + +**Cursor selection and codec.** Audited fixed layout, magic/version, category bounds, checksum, canonical zero slots and installation binding in `maintenance.rs`. Selection retains the next lexicographic logical address per category and wraps; `inspected` advances for blocked candidates and rotates categories. Reverse enumeration, all eight categories, repeated codec reconstruction, wrap and every single-byte corruption were tested. Addresses must remain lossless caller-supplied logical identities. The codec has no persistence, path resolution, origin or deletion authority. Simulations establish only pure selection behavior. + +## Executed verification + +Local environment: Darwin arm64, rustc **1.96.0**. Independent root-package rebuild used unique `profile.test.package.codebase-graph.codegen-units=147`, the frozen reviewer cwd, existing shared target and the existing cleanup-s3 nextest configuration. Compilation visibly rebuilt this root from the reviewer path in **17.95 s**; no warnings appeared in that test build. + +- `cargo nextest run --lib --locked --offline --config 'profile.test.package.codebase-graph.codegen-units=147' --target-dir /Users/rabii/Projects/Repositories/codebaseGraph/target --config-file /private/tmp/codebasegraph-disk-cleanup-7t9af0a6/nextest.toml --profile cleanup-s3 --filterset 'test(storage::maintenance)'`: **66 passed**, 474 skipped, retries 0. These are 14 new foundation cases plus 52 baseline ownership cases, not 66 new tests. The list contained 540 total library tests and selected the expected 66. +- Independent binary: `codebase_graph-880b2cd34b390f45`, SHA-256 `0bb7caad579500d38248ce8b0307b3c4d25407deb9d1ea645959dc2e22c71c61`. Copied binary and dependency file are retained in `binaries/`; binary hash remained unchanged through execution. The dependency file uses relative source paths, so identity relies on visible root rebuild/cwd, the unique root setting, the full source inventory and selected test inventory together, not a binary-string pathname claim. +- Independent JUnit: `junit.xml`, SHA-256 `e25b7e41502e1a46fabfa82fa8c04edd67802310dbf393e9ee0298e517d1b8d8`. +- Isolated direct-source harness: injected Account and byte-buffer allocation failures; observed charge before allocation and at deallocation; verified zero buffers, oversized-layout rollback, split transfer, final handle release on another thread; 16 exclusive-ceiling races; **7,559** deterministic JSON mutation differentials and **428** buffer/depth boundaries; 32 all-category restart selections, 593 single-byte cursor corruptions; entry 101 rejected. Harness source is in `probe/src/main.rs`, output `probe-result-final.json`. Pinned serde_json/sha2 versions were used, with its independent transitive lockfile retained. These are fixture probes, not a production benchmark or whole-pipeline allocation measurement. +- Independent negative rustc trait probe: expected exit 1 and exactly two E0277 diagnostics, rejecting MutationGrant Send and Sync. Existing positive work-type Send+Sync unit case passed. +- Direct budget-module tests under rustc **1.82.0**: 5 passed. This qualifies that isolated module check only, not full-workspace MSRV compatibility. +- Four changed Rust files passed `rustfmt --edition 2021 --check`. All 427 frozen source inputs still matched after verification. + +## Limits and handoff + +No Miri, sanitizer, exhaustive atomic model checker, native Windows/Linux run, full workspace suite, production warning-free build, RSS bound, receipt crash matrix, raw-row usability proof, actual CLI collection convergence or live filesystem cleanup was performed or claimed. Concurrent S2 wrapper changes were deliberately absent from this S3-only snapshot; the coordinator owns their integration test and review gate. + +Requested layouts and explicit floors are accounted by these primitives; allocator slack, OS buffers and arbitrary hidden allocations inside generic Read inputs are excluded explicitly. Eventual S2/S3/S4 consumers must use the same Account and reserve their controller, paths, descriptors, decoder/results/indexes/disposal and duplicate encoding capacities before allocating, with appropriate data-before-charge drop order. The reader/cursor foundation alone cannot satisfy an entire pipeline's aggregate-memory claim. + +No confirmed finding requires an author fix for this snapshot. Pending feature and platform work remains in the existing scope and ownership. Reusable-knowledge assessment: these results are revision-specific review evidence and readily rediscovered implementation facts; the frozen-build provenance lesson already has an existing runbook. No filler memory or external publication was created. + +Machine-readable provenance and outcomes: `verification.json`. The artifact manifest is `review-artifact-sha256.json`. diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s3-fd-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/s3-fd-manifest.json new file mode 100644 index 00000000..3702d4d4 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s3-fd-manifest.json @@ -0,0 +1,349 @@ +{ + "scope": "Bounded borrowed FD and strict complete span comparator/lookup successor. P2 fixed and independently verified. Author85 root tests; reviewer16 copied-module tests plus FD/strict-span/collision probes. No native Windows/Linux/fullcollector qualification.", + "commit": "74b0527", + "files": [ + { + "path": "s3-fd-author/source-manifest.json", + "sha256": "17d5b95b1e6b70d4b2ddd416a0f3202e0f28ef44578f8e7843bff401bb53c326", + "bytes": 1145, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/source-manifest.json", + "source_sha256": "17d5b95b1e6b70d4b2ddd416a0f3202e0f28ef44578f8e7843bff401bb53c326" + }, + { + "path": "s3-fd-author/build-list.log.gz", + "sha256": "9c0a2a48a3acc5e6e58ba636a7b49bfc283ae5e3f750515f147c78e87c109179", + "bytes": 154, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/build-list.log", + "source_sha256": "789fcd5b6d984e8d77491ca3c3a0aa4decf54bc4afe82722429fd5f31204fee3" + }, + { + "path": "s3-fd-author/binaries-after.json", + "sha256": "2e0c5b51ca7be965bb5883f5b2711f2269842a4fb45ac398f006f411b6a08843", + "bytes": 413, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/binaries-after.json", + "source_sha256": "2e0c5b51ca7be965bb5883f5b2711f2269842a4fb45ac398f006f411b6a08843" + }, + { + "path": "s3-fd-author/all-inputs-before.json", + "sha256": "8e4dfa475f508e76e476e398a71781fc048b6375cc79f22c2002522f165aa529", + "bytes": 65430, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/all-inputs-before.json", + "source_sha256": "8e4dfa475f508e76e476e398a71781fc048b6375cc79f22c2002522f165aa529" + }, + { + "path": "s3-fd-author/fmt.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/fmt.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s3-fd-author/run.log.gz", + "sha256": "7257d0c82ec93d3d4a4e227d1047eb0295f9aa6c38d5264004812c19f10386c0", + "bytes": 14710, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/run.log", + "source_sha256": "c20928fe3f3c19c9d75e0685c0bf95d62b00d91dfc69057f7df2475561a0368d" + }, + { + "path": "s3-fd-author/all-inputs-after.json", + "sha256": "8e4dfa475f508e76e476e398a71781fc048b6375cc79f22c2002522f165aa529", + "bytes": 65430, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/all-inputs-after.json", + "source_sha256": "8e4dfa475f508e76e476e398a71781fc048b6375cc79f22c2002522f165aa529" + }, + { + "path": "s3-fd-author/binaries-before.json", + "sha256": "6bccec9ea04f4c44d9c076aec17f29b9445190881742caad8ca588cc5732d167", + "bytes": 456, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/binaries-before.json", + "source_sha256": "6bccec9ea04f4c44d9c076aec17f29b9445190881742caad8ca588cc5732d167" + }, + { + "path": "s3-fd-author/evidence-summary.json", + "sha256": "ec4cd19d14f068caa79fa97df6200c56a1faab502a79781cbde62d516879e967", + "bytes": 686, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/evidence-summary.json", + "source_sha256": "ec4cd19d14f068caa79fa97df6200c56a1faab502a79781cbde62d516879e967" + }, + { + "path": "s3-fd-author/artifact-inventory.json", + "sha256": "96b78aa72d6b411cc02effa1c7a6b2cffa4b30d0b0cb74ba34e5d0dae31a42d1", + "bytes": 2179, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/artifact-inventory.json", + "source_sha256": "96b78aa72d6b411cc02effa1c7a6b2cffa4b30d0b0cb74ba34e5d0dae31a42d1" + }, + { + "path": "s3-fd-author/selected-tests.txt", + "sha256": "dba5d5ab7c5d5620b70780220e803090a4a2ac35a00475993f47799baa7e7eff", + "bytes": 8547, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/selected-tests.txt", + "source_sha256": "dba5d5ab7c5d5620b70780220e803090a4a2ac35a00475993f47799baa7e7eff" + }, + { + "path": "s3-fd-author/nextest-list.json", + "sha256": "7561299bc0c3137595bd0a7ec241b53c5a55ea63dc355fd6933b258bc3d199ca", + "bytes": 101344, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/nextest-list.json", + "source_sha256": "7561299bc0c3137595bd0a7ec241b53c5a55ea63dc355fd6933b258bc3d199ca" + }, + { + "path": "s3-fd-author/junit.xml", + "sha256": "402110286a9985694e6660b48264543b730c5fca3f2a36e22f16f859d31482d7", + "bytes": 19749, + "source": "/private/tmp/dc-s3-comparator-fix-vuy0i08d/junit.xml", + "source_sha256": "402110286a9985694e6660b48264543b730c5fca3f2a36e22f16f859d31482d7" + }, + { + "path": "s3-fd-review/fixed-fd-lookup-probe-result.json", + "sha256": "8deb70d3cb2dec42674221210f6f4a68f3cc81e9162e4b4fa36a1cc7f596b4fd", + "bytes": 176, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-fd-lookup-probe-result.json", + "source_sha256": "8deb70d3cb2dec42674221210f6f4a68f3cc81e9162e4b4fa36a1cc7f596b4fd" + }, + { + "path": "s3-fd-review/comparator-probe-build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/comparator-probe-build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s3-fd-review/source-manifest.json", + "sha256": "11b2686fa394d6cc0312f5c8bdca0dc880ca7e5453068a9586429d49b3c06b93", + "bytes": 1183, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/source-manifest.json", + "source_sha256": "11b2686fa394d6cc0312f5c8bdca0dc880ca7e5453068a9586429d49b3c06b93" + }, + { + "path": "s3-fd-review/fixed-module-tests.rs", + "sha256": "c8250f93706186c4740a7fdcbf2136613d944de2cdcd0217dd3d1a4d9f131673", + "bytes": 1046, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-module-tests.rs", + "source_sha256": "c8250f93706186c4740a7fdcbf2136613d944de2cdcd0217dd3d1a4d9f131673" + }, + { + "path": "s3-fd-review/build-list.log.gz", + "sha256": "25657d44a4d99e42031601f443e55c4521e9e155c463aa0f00d3e42a017b8fed", + "bytes": 155, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/build-list.log", + "source_sha256": "11506248548f519a7664d54fcc3b7587288af5473932f33cd5f5c179ba92c7c3" + }, + { + "path": "s3-fd-review/binaries-after.json", + "sha256": "136244b9501a8e2e2ddb1e564ca8f4cd7c923a7c264858e7f86ff435f46e60dc", + "bytes": 413, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/binaries-after.json", + "source_sha256": "136244b9501a8e2e2ddb1e564ca8f4cd7c923a7c264858e7f86ff435f46e60dc" + }, + { + "path": "s3-fd-review/all-inputs-before.json", + "sha256": "4c773ea52462f6c56bdcc721496ed4e8f00b8b4784f4fc51543bb8d566a05feb", + "bytes": 65430, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/all-inputs-before.json", + "source_sha256": "4c773ea52462f6c56bdcc721496ed4e8f00b8b4784f4fc51543bb8d566a05feb" + }, + { + "path": "s3-fd-review/fd-lookup-probe-result.stderr", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fd-lookup-probe-result.stderr", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s3-fd-review/probe-externs-original.json", + "sha256": "ca178643bca29f9439d06a35ce655f71d9de346d97d41167af6fe1a59e132d10", + "bytes": 341, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/probe-externs-original.json", + "source_sha256": "ca178643bca29f9439d06a35ce655f71d9de346d97d41167af6fe1a59e132d10" + }, + { + "path": "s3-fd-review/run.log.gz", + "sha256": "0eedff2ed5b120a5bed8ec0a3abf03e2e2a43d1273cfe30d0d1ac622c45af8e4", + "bytes": 14576, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/run.log", + "source_sha256": "8818cacccd1bac08f4acc69837c1a166934a628bbc83ea4a460ba756afd6083e" + }, + { + "path": "s3-fd-review/root-runtime-dependency-match.json", + "sha256": "ab8f33171f7dc2703fdd96013b8d094b8c87797372b540b3e5ec4c36c6bc0344", + "bytes": 254, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/root-runtime-dependency-match.json", + "source_sha256": "ab8f33171f7dc2703fdd96013b8d094b8c87797372b540b3e5ec4c36c6bc0344" + }, + { + "path": "s3-fd-review/fixed-module-test-command.json", + "sha256": "70ea575c0e41daf13761a68d9d4a3cda8afe0aeb74a09f923e7c3bb21c57634f", + "bytes": 625, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-module-test-command.json", + "source_sha256": "70ea575c0e41daf13761a68d9d4a3cda8afe0aeb74a09f923e7c3bb21c57634f" + }, + { + "path": "s3-fd-review/comparator_probe.rs", + "sha256": "66ccfbb59f3e08c0324e049d558628a10ae51d74959a6a50c823d357356a4c22", + "bytes": 818, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/comparator_probe.rs", + "source_sha256": "66ccfbb59f3e08c0324e049d558628a10ae51d74959a6a50c823d357356a4c22" + }, + { + "path": "s3-fd-review/fd-lookup-probe-result.json", + "sha256": "3b50ec4572cf7158fd8b00f740d65929398e7c1804c9e923855605563068837a", + "bytes": 145, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fd-lookup-probe-result.json", + "source_sha256": "3b50ec4572cf7158fd8b00f740d65929398e7c1804c9e923855605563068837a" + }, + { + "path": "s3-fd-review/snapshot-verification.json", + "sha256": "fa23de6fbbcd52ae7e21419be294d8ab0239b949d3d9e6e49bf2030d6be0b4be", + "bytes": 176, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/snapshot-verification.json", + "source_sha256": "fa23de6fbbcd52ae7e21419be294d8ab0239b949d3d9e6e49bf2030d6be0b4be" + }, + { + "path": "s3-fd-review/fixed-rustfmt.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-rustfmt.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s3-fd-review/comparator-probe-result.txt", + "sha256": "1cff92299692426ae8972b566b678a44bb4d89df0e70fae2323865e3a324fbae", + "bytes": 253, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/comparator-probe-result.txt", + "source_sha256": "1cff92299692426ae8972b566b678a44bb4d89df0e70fae2323865e3a324fbae" + }, + { + "path": "s3-fd-review/fixed-fd-lookup-probe-command.json", + "sha256": "f50966e5320e9319c6d0d950917a9884546864824b6940be3b6668ec3fa439ef", + "bytes": 612, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-fd-lookup-probe-command.json", + "source_sha256": "f50966e5320e9319c6d0d950917a9884546864824b6940be3b6668ec3fa439ef" + }, + { + "path": "s3-fd-review/fixed-module-test-build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-module-test-build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s3-fd-review/probe-externs.json", + "sha256": "bcd2aadeb96e0b1c58741ae78eebc73366d7cafb44cd8214910180e7e7a459c3", + "bytes": 317, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/probe-externs.json", + "source_sha256": "bcd2aadeb96e0b1c58741ae78eebc73366d7cafb44cd8214910180e7e7a459c3" + }, + { + "path": "s3-fd-review/binaries-before.json", + "sha256": "1e82c27b024c99729b33987fe497945cccd6d2f7263f776c0ffe4efdff9b0647", + "bytes": 469, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/binaries-before.json", + "source_sha256": "1e82c27b024c99729b33987fe497945cccd6d2f7263f776c0ffe4efdff9b0647" + }, + { + "path": "s3-fd-review/evidence-summary.json", + "sha256": "a87ff3147ac669e88916790f435140eb83c86c66c69707459cbf4cbff14bb9f6", + "bytes": 981, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/evidence-summary.json", + "source_sha256": "a87ff3147ac669e88916790f435140eb83c86c66c69707459cbf4cbff14bb9f6" + }, + { + "path": "s3-fd-review/fixed-fd-lookup-probe-result.stderr", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-fd-lookup-probe-result.stderr", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s3-fd-review/review-artifact-sha256.json", + "sha256": "9ae7a58ccfcc2faa0153630d2d4fadd88a1156cb09d93826c4da668b4fa9293c", + "bytes": 3786, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/review-artifact-sha256.json", + "source_sha256": "9ae7a58ccfcc2faa0153630d2d4fadd88a1156cb09d93826c4da668b4fa9293c" + }, + { + "path": "s3-fd-review/probe-dependencies.json", + "sha256": "a12cc4f389503a54f95d345659ec589d9d3c54ddb7e8ccfe26fa249be6b0b270", + "bytes": 10995, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/probe-dependencies.json", + "source_sha256": "a12cc4f389503a54f95d345659ec589d9d3c54ddb7e8ccfe26fa249be6b0b270" + }, + { + "path": "s3-fd-review/fixed-all-inputs-after.json", + "sha256": "8e4dfa475f508e76e476e398a71781fc048b6375cc79f22c2002522f165aa529", + "bytes": 65430, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-all-inputs-after.json", + "source_sha256": "8e4dfa475f508e76e476e398a71781fc048b6375cc79f22c2002522f165aa529" + }, + { + "path": "s3-fd-review/selected-tests.txt", + "sha256": "a57d4f94472c56c7ef854be46f6ec990a8d5dea12676c4224c616ea0497f9117", + "bytes": 8327, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/selected-tests.txt", + "source_sha256": "a57d4f94472c56c7ef854be46f6ec990a8d5dea12676c4224c616ea0497f9117" + }, + { + "path": "s3-fd-review/verification.json", + "sha256": "dbe59bfa8d609d9bc2948d10e6f988be34d9889e858c9a0b1288194fb371217e", + "bytes": 2948, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/verification.json", + "source_sha256": "dbe59bfa8d609d9bc2948d10e6f988be34d9889e858c9a0b1288194fb371217e" + }, + { + "path": "s3-fd-review/owned-fd-envelope.json", + "sha256": "fad575dd57df12c8ead7327bbee4685e5cc5e16fa76bd8a64898a294f91b7eb3", + "bytes": 1134, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/owned-fd-envelope.json", + "source_sha256": "fad575dd57df12c8ead7327bbee4685e5cc5e16fa76bd8a64898a294f91b7eb3" + }, + { + "path": "s3-fd-review/fix-source-manifest.json", + "sha256": "17d5b95b1e6b70d4b2ddd416a0f3202e0f28ef44578f8e7843bff401bb53c326", + "bytes": 1145, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fix-source-manifest.json", + "source_sha256": "17d5b95b1e6b70d4b2ddd416a0f3202e0f28ef44578f8e7843bff401bb53c326" + }, + { + "path": "s3-fd-review/fd-lookup-probe-build.log.gz", + "sha256": "ec4f3e7bb2e6793bc53fd1f389185b32e9f5a51d7c41992046d1b80d95281536", + "bytes": 212, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fd-lookup-probe-build.log", + "source_sha256": "ed784f388b2acec0f44fb12fc711ceb07dd76650364339875a1e7925c16abc74" + }, + { + "path": "s3-fd-review/review.md", + "sha256": "cebfba5ca92aa835cea07e2dc9838c0c989891d52dccad2dfb12e462ccc47dcd", + "bytes": 11061, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/review.md", + "source_sha256": "cebfba5ca92aa835cea07e2dc9838c0c989891d52dccad2dfb12e462ccc47dcd" + }, + { + "path": "s3-fd-review/fixed-fd-lookup-probe-build.log.gz", + "sha256": "3b100c1ac92f6f264082201992ea933347ee9c6e3883f833cbf356b62c8cda5f", + "bytes": 216, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-fd-lookup-probe-build.log", + "source_sha256": "12e09c238db819106ffc97170a43f58de6e5b01e7bcb17cca6ad34f1a7a17fe0" + }, + { + "path": "s3-fd-review/nextest-list.json", + "sha256": "535506e5885c006956e68be3a3c6b451d09da492a9c8f97b1a1c91e7fbf61d7e", + "bytes": 100981, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/nextest-list.json", + "source_sha256": "535506e5885c006956e68be3a3c6b451d09da492a9c8f97b1a1c91e7fbf61d7e" + }, + { + "path": "s3-fd-review/junit.xml", + "sha256": "caf2b80f6a26f137a7b0cc4592d58e5c080b56ce35b495e1017075b3cb213eb8", + "bytes": 19273, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/junit.xml", + "source_sha256": "caf2b80f6a26f137a7b0cc4592d58e5c080b56ce35b495e1017075b3cb213eb8" + }, + { + "path": "s3-fd-review/fixed-module-test-result.txt", + "sha256": "18a3b0453fc5094ff482d1c2542251c09f3d567b7a19259f1a48a80a40093793", + "bytes": 1789, + "source": "/private/tmp/cleanup-v2-s3-fd-review-3vw48_es/fixed-module-test-result.txt", + "source_sha256": "18a3b0453fc5094ff482d1c2542251c09f3d567b7a19259f1a48a80a40093793" + } + ] +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/s4-refresh-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/s4-refresh-manifest.json new file mode 100644 index 00000000..3c135e53 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/s4-refresh-manifest.json @@ -0,0 +1,3324 @@ +{ + "scope": "Explicit cancellation and uniquely owned refresh/runtime/monitor joins;61distinctindependentcases+20repeatedfixture+2privateconcurrencyprobes. Fullcollector/report/typedrecovery/nativeisolatedprocessqualification remainspending.", + "commit": "73eaf71c3c487ef3b52d19d48808d10bd400b939", + "files": [ + { + "path": "s4-refresh-author/tracked-inputs-after.json", + "sha256": "61c25bdf58602d4f0fd58cb81e1100a5dd432164d5660588abef1c9f3f8342a7", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/tracked-inputs-after.json", + "source_sha256": "61c25bdf58602d4f0fd58cb81e1100a5dd432164d5660588abef1c9f3f8342a7" + }, + { + "path": "s4-refresh-author/baseline.txt", + "sha256": "43e0520c6d1fe72112184e30d9b1be8d4e8bc567c6ac60fb8e282307b10974a8", + "bytes": 41, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/baseline.txt", + "source_sha256": "43e0520c6d1fe72112184e30d9b1be8d4e8bc567c6ac60fb8e282307b10974a8" + }, + { + "path": "s4-refresh-author/evidence.json", + "sha256": "25568a56631ab2b8252e3bc799b4dae41b3892d3ef30fbe441cd5a81594440fa", + "bytes": 2743, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/evidence.json", + "source_sha256": "25568a56631ab2b8252e3bc799b4dae41b3892d3ef30fbe441cd5a81594440fa" + }, + { + "path": "s4-refresh-author/owned-source-manifest.json", + "sha256": "78573b8a771daef8d4830ceefd1c26ae3911672c92dc274d4a773f5f21aef241", + "bytes": 624, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/owned-source-manifest.json", + "source_sha256": "78573b8a771daef8d4830ceefd1c26ae3911672c92dc274d4a773f5f21aef241" + }, + { + "path": "s4-refresh-author/tracked-inputs-before.json", + "sha256": "61c25bdf58602d4f0fd58cb81e1100a5dd432164d5660588abef1c9f3f8342a7", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/tracked-inputs-before.json", + "source_sha256": "61c25bdf58602d4f0fd58cb81e1100a5dd432164d5660588abef1c9f3f8342a7" + }, + { + "path": "s4-refresh-author/compile-test.log.gz", + "sha256": "6809f1324fce0722ec423715675c13018a2b368aaea35bee8a514d7effca4856", + "bytes": 6160, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/compile-test.log", + "source_sha256": "72dbc99d47b8af7c5d2a68e2b52fc5dd95064934dd20552525884d8e8475b971" + }, + { + "path": "s4-refresh-author/owned.diff.gz", + "sha256": "ccc49b97a6e17dfb2fdb602b259c6b7e1069f5e20f92f7828200bbe0bddb3179", + "bytes": 12290, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/owned.diff", + "source_sha256": "b10971a694636e7e733a9b74ef8406bb06f089ec6a6ddede448532d8c38b5bb8" + }, + { + "path": "s4-refresh-author/driver-allocation-probe.rs", + "sha256": "fdcbe7c1b14f8df211c2af4be45e6e30857af9ff39da56b50b279885668f2049", + "bytes": 4974, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/driver-allocation-probe.rs", + "source_sha256": "fdcbe7c1b14f8df211c2af4be45e6e30857af9ff39da56b50b279885668f2049" + }, + { + "path": "s4-refresh-author/root-rustc-command.txt", + "sha256": "14bc2f769838fff20911c9f72c4983aa96ff0d7965b04c4b4f08c68092154fe5", + "bytes": 5900, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/root-rustc-command.txt", + "source_sha256": "14bc2f769838fff20911c9f72c4983aa96ff0d7965b04c4b4f08c68092154fe5" + }, + { + "path": "s4-refresh-author/test-inventory.txt", + "sha256": "d3dc735c1f8b99a0e084d424bce0195f67612ba77bba6fa1c5fc47e2dbdb5458", + "bytes": 56873, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/test-inventory.txt", + "source_sha256": "d3dc735c1f8b99a0e084d424bce0195f67612ba77bba6fa1c5fc47e2dbdb5458" + }, + { + "path": "s4-refresh-author/rustfmt-check.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/rustfmt-check.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-author/driver-allocation-probe.stdout", + "sha256": "3aaf0f7053e2ac9e3df32011bdb24f269428c0492af8716a2db39afaa2e9df55", + "bytes": 244, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/driver-allocation-probe.stdout", + "source_sha256": "3aaf0f7053e2ac9e3df32011bdb24f269428c0492af8716a2db39afaa2e9df55" + }, + { + "path": "s4-refresh-author/binary-manifest.json", + "sha256": "871a3b8da4f36f6b309dcba938f6c585b79335f44c965e5090c862a85c7032e4", + "bytes": 276, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/binary-manifest.json", + "source_sha256": "871a3b8da4f36f6b309dcba938f6c585b79335f44c965e5090c862a85c7032e4" + }, + { + "path": "s4-refresh-author/executed-test-inventory.json", + "sha256": "4e7dcb965ca3c01efa2774383887a8a7037c82307a58c9295dad7ad4aee188ea", + "bytes": 13588, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/executed-test-inventory.json", + "source_sha256": "4e7dcb965ca3c01efa2774383887a8a7037c82307a58c9295dad7ad4aee188ea" + }, + { + "path": "s4-refresh-author/production-byte-identical.json", + "sha256": "73f2a05d650ce4baf2ae9fc4b621d000e32307ea96540bd02a34801645cfe4bb", + "bytes": 966, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/production-byte-identical.json", + "source_sha256": "73f2a05d650ce4baf2ae9fc4b621d000e32307ea96540bd02a34801645cfe4bb" + }, + { + "path": "s4-refresh-author/fresh-root.txt", + "sha256": "06f152b53d036194031ce1a4431330be78dad7496dcaaf982b22945a1f3e17a9", + "bytes": 44, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/fresh-root.txt", + "source_sha256": "06f152b53d036194031ce1a4431330be78dad7496dcaaf982b22945a1f3e17a9" + }, + { + "path": "s4-refresh-author/driver-allocation-probe.stderr", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/driver-allocation-probe.stderr", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-author/linked-rust-inputs-after.json", + "sha256": "ecb6d941373ce23c6ae94e451b1c99d1e84ccc62eb8de4f0fbce90c3e4bf0b40", + "bytes": 5873, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/linked-rust-inputs-after.json", + "source_sha256": "ecb6d941373ce23c6ae94e451b1c99d1e84ccc62eb8de4f0fbce90c3e4bf0b40" + }, + { + "path": "s4-refresh-author/driver-allocation-compile.log.gz", + "sha256": "371abec124cc0500d76f1c39ee16886d257764f96d28621c68863b8e551a3c6e", + "bytes": 1402, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/driver-allocation-compile.log", + "source_sha256": "b5b9cf9dab8e5696608ffcfbb830a6148320c7f8c6d9d1b4e3381bc1ef9d6159" + }, + { + "path": "s4-refresh-author/nextest/cleanup-s4-refresh/junit.xml", + "sha256": "de96401e3dd8060bd6eecc8cfe1c57e8da9156b1b1ddcd718d2673829d63fd9c", + "bytes": 13847, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v4/nextest/cleanup-s4-refresh/junit.xml", + "source_sha256": "de96401e3dd8060bd6eecc8cfe1c57e8da9156b1b1ddcd718d2673829d63fd9c" + }, + { + "path": "s4-refresh-review/evidence.json", + "sha256": "52219248aab8f55e7a9faeaa18498816077d9cc1f401d97e994fcbf509007a04", + "bytes": 4097, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/evidence.json", + "source_sha256": "52219248aab8f55e7a9faeaa18498816077d9cc1f401d97e994fcbf509007a04" + }, + { + "path": "s4-refresh-review/first-59-case-junit.xml", + "sha256": "4554c3a0f1d1a4b43424c77c90ee5cb4a9622363d18411a5a0ec59c8d3b637dc", + "bytes": 13449, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/first-59-case-junit.xml", + "source_sha256": "4554c3a0f1d1a4b43424c77c90ee5cb4a9622363d18411a5a0ec59c8d3b637dc" + }, + { + "path": "s4-refresh-review/binary-test-inventory.txt", + "sha256": "a1f534544e92b85e0443cf34b61ff864e37c2787d0f4742afde8c4a67d0889c4", + "bytes": 56848, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/binary-test-inventory.txt", + "source_sha256": "a1f534544e92b85e0443cf34b61ff864e37c2787d0f4742afde8c4a67d0889c4" + }, + { + "path": "s4-refresh-review/transition-fixture-repeat.json", + "sha256": "ad6bf9e45da970491a8073ea28675f05a4c111c5ffe0de68de6dde2829e113f2", + "bytes": 6174, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/transition-fixture-repeat.json", + "source_sha256": "ad6bf9e45da970491a8073ea28675f05a4c111c5ffe0de68de6dde2829e113f2" + }, + { + "path": "s4-refresh-review/source-final-after.json", + "sha256": "61c25bdf58602d4f0fd58cb81e1100a5dd432164d5660588abef1c9f3f8342a7", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/source-final-after.json", + "source_sha256": "61c25bdf58602d4f0fd58cb81e1100a5dd432164d5660588abef1c9f3f8342a7" + }, + { + "path": "s4-refresh-review/author-v3-excluded-compile-test.log.gz", + "sha256": "47c35230dd08ba9622b0d49cf03d397fbcceef0b1c3f539fa8382e840dc8c580", + "bytes": 6290, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/author-v3-excluded-compile-test.log", + "source_sha256": "588a491475ea62ed221e83b65f8ad84ee897c59b3b2c4f57c5e5a2fdfa779d42" + }, + { + "path": "s4-refresh-review/source-before.json", + "sha256": "fad53bef7f7ead785cdceba8870dc5c3991c5f1c1179291d48b399b46f78bb3d", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/source-before.json", + "source_sha256": "fad53bef7f7ead785cdceba8870dc5c3991c5f1c1179291d48b399b46f78bb3d" + }, + { + "path": "s4-refresh-review/joined-thread-compile.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/joined-thread-compile.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-review/joined-thread-probes.log.gz", + "sha256": "3dc3510691d4baa81b04fadf21e70135af15e70cb113b2525ea04d7bb4f5082e", + "bytes": 369, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/joined-thread-probes.log", + "source_sha256": "25740c1b7eb1801f6c1cbc6bf1bc181c493725f1602106eeb90bceefde037a4d" + }, + { + "path": "s4-refresh-review/joined-thread-probes.rs", + "sha256": "6a6303515199b97769884c1c51ae862705dfbbbfe397fd554c1040f3a7f08f5b", + "bytes": 5532, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/joined-thread-probes.rs", + "source_sha256": "6a6303515199b97769884c1c51ae862705dfbbbfe397fd554c1040f3a7f08f5b" + }, + { + "path": "s4-refresh-review/compile-test-final.log.gz", + "sha256": "9200fe1770bdb8cf93f31a0c0899a93541ae604809837ec4739eab8c21172d2b", + "bytes": 6068, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/compile-test-final.log", + "source_sha256": "b805093d87994c43d25f0ce9ac317ee30e3a91d1dcb61b7585cb8fb3af4b78d4" + }, + { + "path": "s4-refresh-review/source-final-before.json", + "sha256": "61c25bdf58602d4f0fd58cb81e1100a5dd432164d5660588abef1c9f3f8342a7", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/source-final-before.json", + "source_sha256": "61c25bdf58602d4f0fd58cb81e1100a5dd432164d5660588abef1c9f3f8342a7" + }, + { + "path": "s4-refresh-review/static-review-notes.json", + "sha256": "37c6faedc4db67b4070baa3c2b80113a7ce95ae0ad3c9b9ee5fcaffc33621cf9", + "bytes": 1346, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/static-review-notes.json", + "source_sha256": "37c6faedc4db67b4070baa3c2b80113a7ce95ae0ad3c9b9ee5fcaffc33621cf9" + }, + { + "path": "s4-refresh-review/context.json", + "sha256": "7db305c621714f859cfb486a11b3a6d3fed7610ac43ec45a0ba005e70162cccf", + "bytes": 667, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/context.json", + "source_sha256": "7db305c621714f859cfb486a11b3a6d3fed7610ac43ec45a0ba005e70162cccf" + }, + { + "path": "s4-refresh-review/compile-test-additional.log.gz", + "sha256": "35ca6e456a76649e5fd3c27e8fc22a490638cdfb108a62734bf3bdb1da55a5e5", + "bytes": 1311, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/compile-test-additional.log", + "source_sha256": "08524a2c531ab762b5c724b125271cd117886a1463e4c058283be2baf1c1fdc9" + }, + { + "path": "s4-refresh-review/final-context.json", + "sha256": "3131337ac71fba0d6c2982894cbb0b597f8cff01f520342ec6318f138f60ce67", + "bytes": 555, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-context.json", + "source_sha256": "3131337ac71fba0d6c2982894cbb0b597f8cff01f520342ec6318f138f60ce67" + }, + { + "path": "s4-refresh-review/review.md", + "sha256": "56ee5a469d71a3986bd4d5018f183da4c34139b5c8043fa585194575c0324bb1", + "bytes": 8646, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/review.md", + "source_sha256": "56ee5a469d71a3986bd4d5018f183da4c34139b5c8043fa585194575c0324bb1" + }, + { + "path": "s4-refresh-review/execution-proof.json", + "sha256": "1427730613ecef05ecdd0faa80154959fefb480fb06d4c10daeb6faf7e64108b", + "bytes": 1355, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/execution-proof.json", + "source_sha256": "1427730613ecef05ecdd0faa80154959fefb480fb06d4c10daeb6faf7e64108b" + }, + { + "path": "s4-refresh-review/joined-thread-exact.rs", + "sha256": "21703c146941ed079374ccba8d65eef5de337d72e083f34d9e80dac05eea8a12", + "bytes": 2848, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/joined-thread-exact.rs", + "source_sha256": "21703c146941ed079374ccba8d65eef5de337d72e083f34d9e80dac05eea8a12" + }, + { + "path": "s4-refresh-review/nextest/refresh-review-additional/junit.xml", + "sha256": "6a6498b975b4d78699f5468ef72378ae1f610de0ffba3ffc97fc91ae7dfa783c", + "bytes": 718, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/nextest/refresh-review-additional/junit.xml", + "source_sha256": "6a6498b975b4d78699f5468ef72378ae1f610de0ffba3ffc97fc91ae7dfa783c" + }, + { + "path": "s4-refresh-review/nextest/refresh-review/junit.xml", + "sha256": "4554c3a0f1d1a4b43424c77c90ee5cb4a9622363d18411a5a0ec59c8d3b637dc", + "bytes": 13449, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/nextest/refresh-review/junit.xml", + "source_sha256": "4554c3a0f1d1a4b43424c77c90ee5cb4a9622363d18411a5a0ec59c8d3b637dc" + }, + { + "path": "s4-refresh-review/final-repo/CHANGELOG.md", + "sha256": "cfde98196d430a4077eff9bff66c7575ff05095f38dc4b58c740eb08bd39084d", + "bytes": 19576, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/CHANGELOG.md", + "source_sha256": "cfde98196d430a4077eff9bff66c7575ff05095f38dc4b58c740eb08bd39084d" + }, + { + "path": "s4-refresh-review/final-repo/build.rs", + "sha256": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2", + "bytes": 3089, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/build.rs", + "source_sha256": "3d0863234c3273c814594d28d8185c88030e6882bdb505b974812c62a74b65d2" + }, + { + "path": "s4-refresh-review/final-repo/README.md", + "sha256": "79ad620f0a8c4fe84425faefa5406e09ba04f14ae83410f62762cca30163b1b5", + "bytes": 10186, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/README.md", + "source_sha256": "79ad620f0a8c4fe84425faefa5406e09ba04f14ae83410f62762cca30163b1b5" + }, + { + "path": "s4-refresh-review/final-repo/release-please-config.json", + "sha256": "56ef4e6d7afada2d1db5747986d928dca733d19f85094d73a2e84cc081b6716f", + "bytes": 547, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/release-please-config.json", + "source_sha256": "56ef4e6d7afada2d1db5747986d928dca733d19f85094d73a2e84cc081b6716f" + }, + { + "path": "s4-refresh-review/final-repo/.release-please-manifest.json", + "sha256": "cd76ee03fb0ca1f78049405025ad82d24d8c13a7c2a11e2db4556456939b963f", + "bytes": 19, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/.release-please-manifest.json", + "source_sha256": "cd76ee03fb0ca1f78049405025ad82d24d8c13a7c2a11e2db4556456939b963f" + }, + { + "path": "s4-refresh-review/final-repo/SECURITY.md", + "sha256": "a838500eac12bd53304f485c21052df40435b4ed37cb2823fe63f3ae225e44e5", + "bytes": 2145, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/SECURITY.md", + "source_sha256": "a838500eac12bd53304f485c21052df40435b4ed37cb2823fe63f3ae225e44e5" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/diagnostics.rs", + "sha256": "7b6e8cc14482d99a004f2115607da0ad675d9b113b467d194b97ed3081abca54", + "bytes": 21795, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/diagnostics.rs", + "source_sha256": "7b6e8cc14482d99a004f2115607da0ad675d9b113b467d194b97ed3081abca54" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/memory.rs", + "sha256": "a4b8b4e03c2ed19d6169bab98291f89d81108736d642fb3aec3657fa08949262", + "bytes": 12919, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/memory.rs", + "source_sha256": "a4b8b4e03c2ed19d6169bab98291f89d81108736d642fb3aec3657fa08949262" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/rendering.rs", + "sha256": "ea6328e283ec169824fad347d446a79e9ef0e4f5d10fcad8664bc862b991b014", + "bytes": 17078, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/rendering.rs", + "source_sha256": "ea6328e283ec169824fad347d446a79e9ef0e4f5d10fcad8664bc862b991b014" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/diagnostic_transports.rs", + "sha256": "6340ce2a5d43ac63d947ac44909b69bd9cb5129fd2e3e80600e623eb89520a59", + "bytes": 10557, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/diagnostic_transports.rs", + "source_sha256": "6340ce2a5d43ac63d947ac44909b69bd9cb5129fd2e3e80600e623eb89520a59" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/bundle_reader.rs", + "sha256": "e823f28854b532e109525c961151164cec00c4f24d391249daa428bd18c44831", + "bytes": 11738, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/bundle_reader.rs", + "source_sha256": "e823f28854b532e109525c961151164cec00c4f24d391249daa428bd18c44831" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/api_integration.rs", + "sha256": "1bbfa6812a737c07d7f3b566e82125e24aefdd54409eaf83faa1877b0cc04c00", + "bytes": 65616, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/api_integration.rs", + "source_sha256": "1bbfa6812a737c07d7f3b566e82125e24aefdd54409eaf83faa1877b0cc04c00" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/transports.rs", + "sha256": "4908e8f16af4e57fbfe56158986d7c4cf807f5bbfdf4ca85203f48ada1959830", + "bytes": 16036, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/transports.rs", + "source_sha256": "4908e8f16af4e57fbfe56158986d7c4cf807f5bbfdf4ca85203f48ada1959830" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/projection.rs", + "sha256": "6add0a4bbc02401cb5ebd733ebba64d7e47e3c6f97f60d842f6cbc40df2da314", + "bytes": 8443, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/projection.rs", + "source_sha256": "6add0a4bbc02401cb5ebd733ebba64d7e47e3c6f97f60d842f6cbc40df2da314" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/refresh.rs", + "sha256": "a8b90c02df7a6ced0ef78b9055ba25fc92cef0f2af2b00d9f1383971114ef82d", + "bytes": 12456, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/refresh.rs", + "source_sha256": "a8b90c02df7a6ced0ef78b9055ba25fc92cef0f2af2b00d9f1383971114ef82d" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/search.rs", + "sha256": "73cf862aa1e154bb830f43d6d64d237ecd0e7f9924af7d3add37d4a93f58e002", + "bytes": 6868, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/search.rs", + "source_sha256": "73cf862aa1e154bb830f43d6d64d237ecd0e7f9924af7d3add37d4a93f58e002" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/conformance.rs", + "sha256": "2110c007ca8bcb2c77be5f5b2592ab8865d26b422bb4ec2cf0501243f0c27f4d", + "bytes": 3768, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/conformance.rs", + "source_sha256": "2110c007ca8bcb2c77be5f5b2592ab8865d26b422bb4ec2cf0501243f0c27f4d" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/compilation.rs", + "sha256": "036e0c6ad7a6ecca6158d3b04a84032b1ddf83aec7f9519a0094ddfc91ec7a6d", + "bytes": 9687, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/compilation.rs", + "source_sha256": "036e0c6ad7a6ecca6158d3b04a84032b1ddf83aec7f9519a0094ddfc91ec7a6d" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/authoring.rs", + "sha256": "ccf184723141f0a80c5015d18e467deb70480a57a3da099ffc34207997414f5c", + "bytes": 22583, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/authoring.rs", + "source_sha256": "ccf184723141f0a80c5015d18e467deb70480a57a3da099ffc34207997414f5c" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/comprehensive/index.md", + "sha256": "ec44e48209becae3111c1bd883f218923c6b2d3cc885c017824ba26a460fb5d6", + "bytes": 174, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/comprehensive/index.md", + "source_sha256": "ec44e48209becae3111c1bd883f218923c6b2d3cc885c017824ba26a460fb5d6" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/comprehensive/decisions/adr-002.md", + "sha256": "ee5c69e25b8f92d32378116a2e42ade139cd4de9f27b8f80c870fd9780c1862d", + "bytes": 179, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/comprehensive/decisions/adr-002.md", + "source_sha256": "ee5c69e25b8f92d32378116a2e42ade139cd4de9f27b8f80c870fd9780c1862d" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/comprehensive/guides/overview.md", + "sha256": "e43357817795d828af91dee0398c4ecf8ede018147319dfc965e7d5b38638c4a", + "bytes": 138, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/comprehensive/guides/overview.md", + "source_sha256": "e43357817795d828af91dee0398c4ecf8ede018147319dfc965e7d5b38638c4a" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/comprehensive/guides/log.md", + "sha256": "4e0876ba1910e475f81c85fe0f821d31e37860edae1d81a72c740b6ce7f978cf", + "bytes": 78, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/comprehensive/guides/log.md", + "source_sha256": "4e0876ba1910e475f81c85fe0f821d31e37860edae1d81a72c740b6ce7f978cf" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/comprehensive/guides/index.md", + "sha256": "74050508a412220f57386f01eb0b4371f122cf47b8b5a652a39990fe0a4782fe", + "bytes": 43, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/comprehensive/guides/index.md", + "source_sha256": "74050508a412220f57386f01eb0b4371f122cf47b8b5a652a39990fe0a4782fe" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/multi_bundle/beta/index.md", + "sha256": "cf2d6b5e006222ca29ce1fc532f0331d7d51d22282abb605cd26fb9c8119aaa0", + "bytes": 53, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/multi_bundle/beta/index.md", + "source_sha256": "cf2d6b5e006222ca29ce1fc532f0331d7d51d22282abb605cd26fb9c8119aaa0" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/multi_bundle/beta/page.md", + "sha256": "4b2e49cd21250f1efdba35b787578f147779e9abb3871c1d45c5298133ab83c8", + "bytes": 75, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/multi_bundle/beta/page.md", + "source_sha256": "4b2e49cd21250f1efdba35b787578f147779e9abb3871c1d45c5298133ab83c8" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/multi_bundle/alpha/index.md", + "sha256": "e91e48f91cff58e2249470c36da84313fc043b8b32ba0367d525be78dc7d7124", + "bytes": 55, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/multi_bundle/alpha/index.md", + "source_sha256": "e91e48f91cff58e2249470c36da84313fc043b8b32ba0367d525be78dc7d7124" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/multi_bundle/alpha/page.md", + "sha256": "893778b136b09da256a4801c71ada991ba7806e2b1dc3098f5d56e0556cf5758", + "bytes": 78, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/multi_bundle/alpha/page.md", + "source_sha256": "893778b136b09da256a4801c71ada991ba7806e2b1dc3098f5d56e0556cf5758" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/minimal/index.md", + "sha256": "57294be90498ad361c2156ac3f4986572b672713299597587d7612254819cd63", + "bytes": 128, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/minimal/index.md", + "source_sha256": "57294be90498ad361c2156ac3f4986572b672713299597587d7612254819cd63" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/minimal/decisions/adr-001.md", + "sha256": "cf2c2dcb1b5f1ed96212aec1a1f3f463d9be5b0bcf11639221ced40edb2cc63e", + "bytes": 295, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/minimal/decisions/adr-001.md", + "source_sha256": "cf2c2dcb1b5f1ed96212aec1a1f3f463d9be5b0bcf11639221ced40edb2cc63e" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/malformed/tagged-extension.md", + "sha256": "d2cd20fb5604c48279c8d88a5f52e7d83e7ceb5115cf26e5345d8f79badbbfa0", + "bytes": 217, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/malformed/tagged-extension.md", + "source_sha256": "d2cd20fb5604c48279c8d88a5f52e7d83e7ceb5115cf26e5345d8f79badbbfa0" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/malformed/stray-version.md", + "sha256": "d949cba772124200cb4baa256980447f30189eb65cf46c4f0de8a3fc404753a1", + "bytes": 181, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/malformed/stray-version.md", + "source_sha256": "d949cba772124200cb4baa256980447f30189eb65cf46c4f0de8a3fc404753a1" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/malformed/no-frontmatter.md", + "sha256": "9975ae24341afd498f46f74e0e6d03de80b33a857322e4f4f3041a1d717dfd18", + "bytes": 63, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/malformed/no-frontmatter.md", + "source_sha256": "9975ae24341afd498f46f74e0e6d03de80b33a857322e4f4f3041a1d717dfd18" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/malformed/log.md", + "sha256": "d26bf0adce832f845e82a01e6e2d84c00c35383d7e04e86f1ac25ff76f2eda34", + "bytes": 88, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/malformed/log.md", + "source_sha256": "d26bf0adce832f845e82a01e6e2d84c00c35383d7e04e86f1ac25ff76f2eda34" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/malformed/invalid-frontmatter.md", + "sha256": "08243906665df2b1bb64ab1beefe56662b1ca220a89711c0b5899ad0ee572987", + "bytes": 109, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/malformed/invalid-frontmatter.md", + "source_sha256": "08243906665df2b1bb64ab1beefe56662b1ca220a89711c0b5899ad0ee572987" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/malformed/missing-type.md", + "sha256": "8616868d3786290e5241ae7af997745318573f6dc623163d721708eede778174", + "bytes": 141, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/malformed/missing-type.md", + "source_sha256": "8616868d3786290e5241ae7af997745318573f6dc623163d721708eede778174" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/tests/fixtures/malformed/nested/index.md", + "sha256": "c9d1642dafd370c907cf06ae498162b9447f3648eea2729e862eeb249137a3f6", + "bytes": 92, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/tests/fixtures/malformed/nested/index.md", + "source_sha256": "c9d1642dafd370c907cf06ae498162b9447f3648eea2729e862eeb249137a3f6" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/lib.rs", + "sha256": "b38e9b31eabda34565ab5be588cdc291d67006204ba27385b02634be1be24c8c", + "bytes": 469, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/lib.rs", + "source_sha256": "b38e9b31eabda34565ab5be588cdc291d67006204ba27385b02634be1be24c8c" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/memory.rs", + "sha256": "8e6402586f2190e536ecb1d6144735a77b08640ef85e554c148d0049cbfbaeea", + "bytes": 15936, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/memory.rs", + "source_sha256": "8e6402586f2190e536ecb1d6144735a77b08640ef85e554c148d0049cbfbaeea" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/service.rs", + "sha256": "9aee89282f778101705752e4f597f3b1642b0d92cb464372ab41cc169833a9e4", + "bytes": 32633, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/service.rs", + "source_sha256": "9aee89282f778101705752e4f597f3b1642b0d92cb464372ab41cc169833a9e4" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/graph_context.rs", + "sha256": "23fba237d4b6117ac914965a87504a7ffec4c02b958b887f536f7d86f2293e1c", + "bytes": 9127, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/graph_context.rs", + "source_sha256": "23fba237d4b6117ac914965a87504a7ffec4c02b958b887f536f7d86f2293e1c" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/refresh.rs", + "sha256": "fa3c1e2199b8ef6ed0a50e8c59d3e5b0819e896ba262673d071f91175d1b1b5d", + "bytes": 13355, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/refresh.rs", + "source_sha256": "fa3c1e2199b8ef6ed0a50e8c59d3e5b0819e896ba262673d071f91175d1b1b5d" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/conformance.rs", + "sha256": "d70c4149a337268e82d4cb1bbd9cb069c423c3b59f452de9a19f03eab71bb108", + "bytes": 8394, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/conformance.rs", + "source_sha256": "d70c4149a337268e82d4cb1bbd9cb069c423c3b59f452de9a19f03eab71bb108" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/diagnostic.rs", + "sha256": "05d96b05a6c569e7378e6d61ee7ad73e524e60ee0cf29d02537a6be75e2a1204", + "bytes": 1413, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/diagnostic.rs", + "source_sha256": "05d96b05a6c569e7378e6d61ee7ad73e524e60ee0cf29d02537a6be75e2a1204" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/model.rs", + "sha256": "37495fe19bdb12bcf4247b06b1cb534891e834cf1b13becade4f917e8bdaef57", + "bytes": 6916, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/model.rs", + "source_sha256": "37495fe19bdb12bcf4247b06b1cb534891e834cf1b13becade4f917e8bdaef57" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/bin/k-wiki.rs", + "sha256": "7688bb538d30ed6d3c1aa5147f52b604f364efd52104466233ba71b88ccfafba", + "bytes": 14705, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/bin/k-wiki.rs", + "source_sha256": "7688bb538d30ed6d3c1aa5147f52b604f364efd52104466233ba71b88ccfafba" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/render/mod.rs", + "sha256": "72d933e017228db06805568232c3dab45ad9bb2caf9336e954b7442f5ed6e8dd", + "bytes": 75425, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/render/mod.rs", + "source_sha256": "72d933e017228db06805568232c3dab45ad9bb2caf9336e954b7442f5ed6e8dd" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/bundle/mod.rs", + "sha256": "89648f228ce2bb1f60106ac451c68c0cfae17d407a7898c4e82e7daa2230614f", + "bytes": 22245, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/bundle/mod.rs", + "source_sha256": "89648f228ce2bb1f60106ac451c68c0cfae17d407a7898c4e82e7daa2230614f" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/search/mod.rs", + "sha256": "09b2dc7d91aee673ff137f1d09ba0a4320a42f39972ea0ebc45b3f6a723914b3", + "bytes": 13256, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/search/mod.rs", + "source_sha256": "09b2dc7d91aee673ff137f1d09ba0a4320a42f39972ea0ebc45b3f6a723914b3" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/adapters/mod.rs", + "sha256": "9fba7cf7de016731656f301a2ff86a7334ed898bced55df89650511422e5ef84", + "bytes": 1468, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/adapters/mod.rs", + "source_sha256": "9fba7cf7de016731656f301a2ff86a7334ed898bced55df89650511422e5ef84" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/adapters/install.rs", + "sha256": "7be961ca1516892e4b63f4c32a8536996bdca1e1f5447d294028972b20c5e4bf", + "bytes": 50919, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/adapters/install.rs", + "source_sha256": "7be961ca1516892e4b63f4c32a8536996bdca1e1f5447d294028972b20c5e4bf" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/adapters/http.rs", + "sha256": "8ae80aa4601a8776369cbf6426f23ab986c51626d95673561673fb29663f2aa4", + "bytes": 10606, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/adapters/http.rs", + "source_sha256": "8ae80aa4601a8776369cbf6426f23ab986c51626d95673561673fb29663f2aa4" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/adapters/mcp.rs", + "sha256": "4baad85ade6fcf8f2eb7a69b5c07929bacc783e65d2bb8c3192495604f2b804d", + "bytes": 10992, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/adapters/mcp.rs", + "source_sha256": "4baad85ade6fcf8f2eb7a69b5c07929bacc783e65d2bb8c3192495604f2b804d" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/adapters/cli.rs", + "sha256": "b5d5d054a2a063f5b8f881547aa3d34c75aae74dbf94465a0a5ad353c1926bf4", + "bytes": 11985, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/adapters/cli.rs", + "source_sha256": "b5d5d054a2a063f5b8f881547aa3d34c75aae74dbf94465a0a5ad353c1926bf4" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/authoring/page.rs", + "sha256": "4ac9552f9407cc8511c874d1d12e4c290eec933345c51045b0c179a5fe40aaea", + "bytes": 2189, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/authoring/page.rs", + "source_sha256": "4ac9552f9407cc8511c874d1d12e4c290eec933345c51045b0c179a5fe40aaea" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/authoring/write.rs", + "sha256": "f5b6822859ca46e37c7121420de749eacdcc454863f12261b48e5fd345821dbf", + "bytes": 10669, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/authoring/write.rs", + "source_sha256": "f5b6822859ca46e37c7121420de749eacdcc454863f12261b48e5fd345821dbf" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/authoring/bundle.rs", + "sha256": "4cc974d441227b576dc0ccbb873ea7fe232e1f87f4ecf01b52fc927357e41e32", + "bytes": 20400, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/authoring/bundle.rs", + "source_sha256": "4cc974d441227b576dc0ccbb873ea7fe232e1f87f4ecf01b52fc927357e41e32" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/authoring/mod.rs", + "sha256": "739484eaf8e3e02614c9ef74c36508fb6751aafcb3152ecbe68f9bd73862d533", + "bytes": 605, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/authoring/mod.rs", + "source_sha256": "739484eaf8e3e02614c9ef74c36508fb6751aafcb3152ecbe68f9bd73862d533" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/api/registry.rs", + "sha256": "596fbe985b6e2af9e5cd0b5653baca585572cf9904f9732c7eda9464188ad0d1", + "bytes": 20420, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/api/registry.rs", + "source_sha256": "596fbe985b6e2af9e5cd0b5653baca585572cf9904f9732c7eda9464188ad0d1" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/api/facade.rs", + "sha256": "559933fe8aa20ac49582617993a31df620f0e89360151fc3ddc0d53ac0a07bdf", + "bytes": 2042, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/api/facade.rs", + "source_sha256": "559933fe8aa20ac49582617993a31df620f0e89360151fc3ddc0d53ac0a07bdf" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/api/mod.rs", + "sha256": "cecdbbe2ddda8302be2e99e8e95afe32910df6aa367dc3065270d91cb7bc5145", + "bytes": 361, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/api/mod.rs", + "source_sha256": "cecdbbe2ddda8302be2e99e8e95afe32910df6aa367dc3065270d91cb7bc5145" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/api/contracts.rs", + "sha256": "019f08b4dad8f1c250b0b01de71f16d262056970979e84dc1c0ae23f45f553ea", + "bytes": 8255, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/api/contracts.rs", + "source_sha256": "019f08b4dad8f1c250b0b01de71f16d262056970979e84dc1c0ae23f45f553ea" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/compiler/mod.rs", + "sha256": "ba3be8236266cb742b5432484cc8ec4042e3da47dd74259329008ae25a876365", + "bytes": 36565, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/compiler/mod.rs", + "source_sha256": "ba3be8236266cb742b5432484cc8ec4042e3da47dd74259329008ae25a876365" + }, + { + "path": "s4-refresh-review/final-repo/crates/k-wiki/src/projection/mod.rs", + "sha256": "91b213bc252e2b895f1fef6e70cde41f93f2989bc7a98712d13a6b99b9845594", + "bytes": 29746, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/k-wiki/src/projection/mod.rs", + "source_sha256": "91b213bc252e2b895f1fef6e70cde41f93f2989bc7a98712d13a6b99b9845594" + }, + { + "path": "s4-refresh-review/final-repo/crates/xtask/src/release.rs", + "sha256": "dc74317037a68df27c98c24678d2b46589ef64fc1ef0d484b415cae33435ebd7", + "bytes": 5026, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/xtask/src/release.rs", + "source_sha256": "dc74317037a68df27c98c24678d2b46589ef64fc1ef0d484b415cae33435ebd7" + }, + { + "path": "s4-refresh-review/final-repo/crates/xtask/src/main.rs", + "sha256": "a4eb81730661eddb10a133c3c106c492cd611b5d7b827996edcd81a797e713e9", + "bytes": 94323, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/xtask/src/main.rs", + "source_sha256": "a4eb81730661eddb10a133c3c106c492cd611b5d7b827996edcd81a797e713e9" + }, + { + "path": "s4-refresh-review/final-repo/crates/xtask/src/release/workflow_tests.rs", + "sha256": "549e2b6cfabfe0e60717dc186ecf94b1a3e598d7ee670857460f1f3bb1b59861", + "bytes": 12011, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/crates/xtask/src/release/workflow_tests.rs", + "source_sha256": "549e2b6cfabfe0e60717dc186ecf94b1a3e598d7ee670857460f1f3bb1b59861" + }, + { + "path": "s4-refresh-review/final-repo/tests/coordinator_process.rs", + "sha256": "f58387496162899a4611dda84781c98d87e6252c61844009b7ec7def4b64844c", + "bytes": 19000, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/coordinator_process.rs", + "source_sha256": "f58387496162899a4611dda84781c98d87e6252c61844009b7ec7def4b64844c" + }, + { + "path": "s4-refresh-review/final-repo/tests/http_admission_process.rs", + "sha256": "3dd5533bfd85467027710b37d77be2ebfc84832d23942f3a5398c335b151dbb9", + "bytes": 14311, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/http_admission_process.rs", + "source_sha256": "3dd5533bfd85467027710b37d77be2ebfc84832d23942f3a5398c335b151dbb9" + }, + { + "path": "s4-refresh-review/final-repo/tests/agent_hooks_process.rs", + "sha256": "364d029cd0d4daf328ba46a1254e1d9a6f5df4e7fc9b0a86bcbf7740b6030478", + "bytes": 29611, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/agent_hooks_process.rs", + "source_sha256": "364d029cd0d4daf328ba46a1254e1d9a6f5df4e7fc9b0a86bcbf7740b6030478" + }, + { + "path": "s4-refresh-review/final-repo/tests/native_request_identity.rs", + "sha256": "f624bd73ba6b282f415a6251edca1c8d0e85a7d9bcacad1fe2d020e732c74eb9", + "bytes": 5032, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/native_request_identity.rs", + "source_sha256": "f624bd73ba6b282f415a6251edca1c8d0e85a7d9bcacad1fe2d020e732c74eb9" + }, + { + "path": "s4-refresh-review/final-repo/tests/auto_repo_root.rs", + "sha256": "09d5d09635409c18b2d9b1da8a2272766b4fa5be63d93efd3aa4fdaf0f93fc76", + "bytes": 7865, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/auto_repo_root.rs", + "source_sha256": "09d5d09635409c18b2d9b1da8a2272766b4fa5be63d93efd3aa4fdaf0f93fc76" + }, + { + "path": "s4-refresh-review/final-repo/tests/generation_storage.rs", + "sha256": "17e4ae34bc255345158f879f48dd6a8124ad07722ff75b8fff3f071dce70e6ce", + "bytes": 17447, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/generation_storage.rs", + "source_sha256": "17e4ae34bc255345158f879f48dd6a8124ad07722ff75b8fff3f071dce70e6ce" + }, + { + "path": "s4-refresh-review/final-repo/tests/storage_maintenance.rs", + "sha256": "81d0800bcfa1117f334117408d27e5b1a6b8cc98274e91ad87ca5a16eb41faed", + "bytes": 6746, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/storage_maintenance.rs", + "source_sha256": "81d0800bcfa1117f334117408d27e5b1a6b8cc98274e91ad87ca5a16eb41faed" + }, + { + "path": "s4-refresh-review/final-repo/tests/refresh_recovery_process.rs", + "sha256": "158e6795af9b9dd0b812f9ae7df579694c15238b4a0de5c36cca6a603fc63b5c", + "bytes": 34405, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/refresh_recovery_process.rs", + "source_sha256": "158e6795af9b9dd0b812f9ae7df579694c15238b4a0de5c36cca6a603fc63b5c" + }, + { + "path": "s4-refresh-review/final-repo/tests/http_daemon_process.rs", + "sha256": "fc651df00823b1d3f854d6627dc80c0d10227d78597c1adab8e417d8e0a55d46", + "bytes": 44602, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/http_daemon_process.rs", + "source_sha256": "fc651df00823b1d3f854d6627dc80c0d10227d78597c1adab8e417d8e0a55d46" + }, + { + "path": "s4-refresh-review/final-repo/tests/support/lifecycle_faults.rs", + "sha256": "c075bf1d9f247920d5052d607de2a42b2c2f76fc0cbdc120f846b42b8cf581f8", + "bytes": 36889, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/support/lifecycle_faults.rs", + "source_sha256": "c075bf1d9f247920d5052d607de2a42b2c2f76fc0cbdc120f846b42b8cf581f8" + }, + { + "path": "s4-refresh-review/final-repo/tests/support/storage_maintenance/mod.rs", + "sha256": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09", + "bytes": 4467, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/tests/support/storage_maintenance/mod.rs", + "source_sha256": "bf79e22e33cd260953e4eaa1c7ab323772c62f3ebe6ade166f870b6250304d09" + }, + { + "path": "s4-refresh-review/final-repo/docs/mcp.md", + "sha256": "6267772875d91ce2c63840248933b1056ad0f175d91351e7e057e07ea05ceb83", + "bytes": 11815, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/mcp.md", + "source_sha256": "6267772875d91ce2c63840248933b1056ad0f175d91351e7e057e07ea05ceb83" + }, + { + "path": "s4-refresh-review/final-repo/docs/troubleshooting.md", + "sha256": "4ef9e29cb7edac9d1918d9dfe5e7316fda4a4a322c2b4c313c21d6854b74e2cd", + "bytes": 6699, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/troubleshooting.md", + "source_sha256": "4ef9e29cb7edac9d1918d9dfe5e7316fda4a4a322c2b4c313c21d6854b74e2cd" + }, + { + "path": "s4-refresh-review/final-repo/docs/graph_output_token_comparison.md", + "sha256": "da24d64e7e78edcd74ff0d8c76498af037740e3caf8c71fab092127fac08d332", + "bytes": 1933, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/graph_output_token_comparison.md", + "source_sha256": "da24d64e7e78edcd74ff0d8c76498af037740e3caf8c71fab092127fac08d332" + }, + { + "path": "s4-refresh-review/final-repo/docs/k-wiki.md", + "sha256": "a88834489a890251912da9153df030eed7577cd71382c8d1af15b85d8e377a8a", + "bytes": 10982, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/k-wiki.md", + "source_sha256": "a88834489a890251912da9153df030eed7577cd71382c8d1af15b85d8e377a8a" + }, + { + "path": "s4-refresh-review/final-repo/docs/readme-ux-rewrite-proposal.md", + "sha256": "cb6b3d736a571d3609e87b0bdad3f935afa7ba7355c743a1cb0bac43059a99ed", + "bytes": 18385, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/readme-ux-rewrite-proposal.md", + "source_sha256": "cb6b3d736a571d3609e87b0bdad3f935afa7ba7355c743a1cb0bac43059a99ed" + }, + { + "path": "s4-refresh-review/final-repo/docs/release.md", + "sha256": "e7456beda70e58b2049ed13f1484731544a9fb89cf49d8bf0f7d2435723d02bf", + "bytes": 10503, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/release.md", + "source_sha256": "e7456beda70e58b2049ed13f1484731544a9fb89cf49d8bf0f7d2435723d02bf" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence.json", + "sha256": "8cb9670ace63fc10d02447c4f612f306242a060e7d1882e4992cae7ba6347d40", + "bytes": 65603, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence.json", + "source_sha256": "8cb9670ace63fc10d02447c4f612f306242a060e7d1882e4992cae7ba6347d40" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/worker-recovery-evidence-2026-10-20.json", + "sha256": "a74d172ee20714ab3d61341b04cde56acaee60e84091ef660868ba046f4227d0", + "bytes": 18086, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/worker-recovery-evidence-2026-10-20.json", + "source_sha256": "a74d172ee20714ab3d61341b04cde56acaee60e84091ef660868ba046f4227d0" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-integration.json", + "sha256": "f33d2a4410e0c2b6785c14bda9330f878973827751fa65e3f4521c98a46168d0", + "bytes": 29018, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-integration.json", + "source_sha256": "f33d2a4410e0c2b6785c14bda9330f878973827751fa65e3f4521c98a46168d0" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s4-reused-target-excluded-junit.xml", + "sha256": "ed70ff0327920ea194f4df6fd76ceac547cd67db2ccdc03b96c06f8c9c4d408c", + "bytes": 4102, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s4-reused-target-excluded-junit.xml", + "source_sha256": "ed70ff0327920ea194f4df6fd76ceac547cd67db2ccdc03b96c06f8c9c4d408c" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-independent-junit.xml", + "sha256": "640d1dc448e77ea726b0468255a488c9badce39a955176e7fa63158263fcdc76", + "bytes": 1742, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-independent-junit.xml", + "source_sha256": "640d1dc448e77ea726b0468255a488c9badce39a955176e7fa63158263fcdc76" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-independent-junit.xml", + "sha256": "3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216", + "bytes": 4102, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-independent-junit.xml", + "source_sha256": "3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-junit.xml", + "sha256": "698d41772c871f1398e5cf926c415a93af41ef65bd73b298220323266a7e761f", + "bytes": 68037, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/final-foundation-junit.xml", + "source_sha256": "698d41772c871f1398e5cf926c415a93af41ef65bd73b298220323266a7e761f" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-fix-verification.json", + "sha256": "5fd30a710e42520ee3732b73470870687f8511830cca98ce2e218062abd3279e", + "bytes": 8193, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-fix-verification.json", + "source_sha256": "5fd30a710e42520ee3732b73470870687f8511830cca98ce2e218062abd3279e" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-review-evidence.json", + "sha256": "6101c50b30b24228d473ff86ab98c4d6d16128fd8e98195705df595ea5d22729", + "bytes": 1917, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s4-postpublication-review-evidence.json", + "source_sha256": "6101c50b30b24228d473ff86ab98c4d6d16128fd8e98195705df595ea5d22729" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-rebuild-provenance.json", + "sha256": "acef411c4c6d5a68565001d23d69da28bd483350e1ea74dfc64a15e98fbbfdc0", + "bytes": 1609, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/s2-epoch-read-fix-rebuild-provenance.json", + "source_sha256": "acef411c4c6d5a68565001d23d69da28bd483350e1ea74dfc64a15e98fbbfdc0" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-foundation-review.json", + "sha256": "f3286fa16726d54717d3fa90d79ed817e86878af3a6bab13f416462d9ffe9549", + "bytes": 5645, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/integrated-foundation-review.json", + "source_sha256": "f3286fa16726d54717d3fa90d79ed817e86878af3a6bab13f416462d9ffe9549" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/integration-junit.xml", + "sha256": "3562f1cb18f27b76ecd94f3b71770d2659b91836e24c37f9bebc7c1c2adb8e76", + "bytes": 67215, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-foundation-evidence/integration-junit.xml", + "source_sha256": "3562f1cb18f27b76ecd94f3b71770d2659b91836e24c37f9bebc7c1c2adb8e76" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json", + "sha256": "ff7043693f6cd3bf978921262ae7c9e83ad4772bf760d1db777f169f93694600", + "bytes": 4168, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-narrow-initial-checkpoint.json", + "source_sha256": "ff7043693f6cd3bf978921262ae7c9e83ad4772bf760d1db777f169f93694600" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml", + "sha256": "43789b2717a390d201e486c0c1def0e0f7aa8cfe3548a61fe2ee4fc8546e0603", + "bytes": 6563, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s1-post-main-junit.xml", + "source_sha256": "43789b2717a390d201e486c0c1def0e0f7aa8cfe3548a61fe2ee4fc8546e0603" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr", + "sha256": "3789ba8ed22ae4a82b1fc7adaea6cd8d86d3c94beb1f30f698bd4afb3f7e6ff0", + "bytes": 590, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-msrv-metadata.stderr", + "source_sha256": "3789ba8ed22ae4a82b1fc7adaea6cd8d86d3c94beb1f30f698bd4afb3f7e6ff0" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml", + "sha256": "c4d0cbb89af4996d859c7da5b1bed3b04a1a9b90dd44135bc7c9d2d2c9ecfb3f", + "bytes": 2591, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s4-scheduler-primitive-junit.xml", + "source_sha256": "c4d0cbb89af4996d859c7da5b1bed3b04a1a9b90dd44135bc7c9d2d2c9ecfb3f" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "sha256": "c2dd357024899ff4a64e9d4de15f181a4f9b827e460e509d2ed7e8b1a09ba3f3", + "bytes": 18719, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-source-manifest.json", + "source_sha256": "c2dd357024899ff4a64e9d4de15f181a4f9b827e460e509d2ed7e8b1a09ba3f3" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stderr", + "sha256": "e4ed00080d9030eba59f6c9af6ac3d6b784e69ad1e9dcb2ce9107c19fb07be0d", + "bytes": 877, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stderr", + "source_sha256": "e4ed00080d9030eba59f6c9af6ac3d6b784e69ad1e9dcb2ce9107c19fb07be0d" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt", + "sha256": "ffd58ad5367a31890d30ae14515523e74e76c810742d011f028c46a4729ae6ed", + "bytes": 205, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s3-expanded-reviewer-probe-notes.txt", + "source_sha256": "ffd58ad5367a31890d30ae14515523e74e76c810742d011f028c46a4729ae6ed" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stdout", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stdout", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml", + "sha256": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876", + "bytes": 30542, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml", + "source_sha256": "6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json", + "sha256": "65d9f8a5c1124a215f66e63f5819f60aef52382559cb627d807ce7a7d5bab718", + "bytes": 9335, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s3-expanded-review-expanded-report.json", + "source_sha256": "65d9f8a5c1124a215f66e63f5819f60aef52382559cb627d807ce7a7d5bab718" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-narrow-checkpoint.json", + "sha256": "47dd5518ed1d1cc5dac128acda06e2431ced50586af17fd79bc27245861f1db9", + "bytes": 8705, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-narrow-checkpoint.json", + "source_sha256": "47dd5518ed1d1cc5dac128acda06e2431ced50586af17fd79bc27245861f1db9" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/review-outcomes.json", + "sha256": "bac2d4ad1d32aa54f709c6ccfd34b4a3b5917f0822d3524e1b8f70378cc5ccbe", + "bytes": 3817, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/review-outcomes.json", + "source_sha256": "bac2d4ad1d32aa54f709c6ccfd34b4a3b5917f0822d3524e1b8f70378cc5ccbe" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr", + "sha256": "37deacba5b2b624d0962949aa5cb60ce368a873c072cfff764cf836bb098a66b", + "bytes": 1088, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/audit-regressions.stderr", + "source_sha256": "37deacba5b2b624d0962949aa5cb60ce368a873c072cfff764cf836bb098a66b" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json", + "sha256": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627", + "bytes": 308, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/original-review-external_skip-probe-result.json", + "source_sha256": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regression-execution.json", + "sha256": "55763bb75fe573cf97fbe8878687d8e12c30836fa6ee991ee7b7a71451779611", + "bytes": 894, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regression-execution.json", + "source_sha256": "55763bb75fe573cf97fbe8878687d8e12c30836fa6ee991ee7b7a71451779611" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stdout", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/historical-eight-audit-regressions.stdout", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json", + "sha256": "c750c93245f79df2379200a4a43fb8b5b94c48a7d351b22da1eefb8e05bcc91d", + "bytes": 1088, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/audit-regression-execution.json", + "source_sha256": "c750c93245f79df2379200a4a43fb8b5b94c48a7d351b22da1eefb8e05bcc91d" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml", + "sha256": "8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414", + "bytes": 2591, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s4-independent-source-junit.xml", + "source_sha256": "8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json", + "sha256": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "bytes": 1918, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s2-family-focused-source-manifest.json", + "source_sha256": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json", + "sha256": "95f89f93b01ad3c9b02cbab6b97278cdbb14d604080963a4a2b8a08deee39c5c", + "bytes": 2254, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s3-expanded-source-manifest.json", + "source_sha256": "95f89f93b01ad3c9b02cbab6b97278cdbb14d604080963a4a2b8a08deee39c5c" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json", + "sha256": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627", + "bytes": 308, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/original-review-foundation_skip-probe-result.json", + "source_sha256": "f7acbd67cb306f2b980f60c336dbbd12800a018666b1915b32bf5b4e392c3627" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json", + "sha256": "0dc7bd221b6d210f260677b1c2027b707a3bd0f8a0a314d35eefb64d0402e151", + "bytes": 7322, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/msrv-readiness.json", + "source_sha256": "0dc7bd221b6d210f260677b1c2027b707a3bd0f8a0a314d35eefb64d0402e151" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml", + "sha256": "3b8c4e663c2c93d3c52f93da6af4b17096b529deff117ce4f9b191ad766e44b7", + "bytes": 784, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-external-health-fixverify-junit.xml", + "source_sha256": "3b8c4e663c2c93d3c52f93da6af4b17096b529deff117ce4f9b191ad766e44b7" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json", + "sha256": "ea74ad1208875efa0e56d2f168bcec49c409cfe9219387e8e0b3f85a15645f86", + "bytes": 3426, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-external-health-source-manifest.json", + "source_sha256": "ea74ad1208875efa0e56d2f168bcec49c409cfe9219387e8e0b3f85a15645f86" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml", + "sha256": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627", + "bytes": 6885, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s3-expanded-junit.xml", + "source_sha256": "e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-narrow-audit-fix-checkpoint.json", + "sha256": "330a275563716dcd528c145b9ff785ac7ab686088a86572d1379f11aa9d278be", + "bytes": 8013, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-narrow-audit-fix-checkpoint.json", + "source_sha256": "330a275563716dcd528c145b9ff785ac7ab686088a86572d1379f11aa9d278be" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/foundation-reports.json", + "sha256": "88eff4652ee0d4914980912c2a9600481bb891bc539706fea1677062133b3bb8", + "bytes": 78925, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/foundation-reports.json", + "source_sha256": "88eff4652ee0d4914980912c2a9600481bb891bc539706fea1677062133b3bb8" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml", + "sha256": "ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92", + "bytes": 29509, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-junit.xml", + "source_sha256": "ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr", + "sha256": "d75191f480933414ac1b27956d159e407be16eecac57c0d782ec127207ea333e", + "bytes": 690, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-msrv-ready-metadata.stderr", + "source_sha256": "d75191f480933414ac1b27956d159e407be16eecac57c0d782ec127207ea333e" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json", + "sha256": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8", + "bytes": 1918, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s2-family-regression-source-manifest.json", + "source_sha256": "6437fb98f04feaced02de55c63f5063822bb8a7eb415b2dcede249face8e9be8" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml", + "sha256": "8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6", + "bytes": 784, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-external-health-junit.xml", + "source_sha256": "8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr", + "sha256": "38b1121bacbb008a468e084a6453f3b81fb7972fc81e6670176e212381b8aa3e", + "bytes": 31361, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stderr", + "source_sha256": "38b1121bacbb008a468e084a6453f3b81fb7972fc81e6670176e212381b8aa3e" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json", + "sha256": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "bytes": 2167, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.json", + "source_sha256": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json", + "sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "bytes": 14451, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-before.json", + "source_sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json", + "sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "bytes": 14451, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-build.json", + "source_sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json", + "sha256": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "bytes": 180, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-after.json", + "source_sha256": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout", + "sha256": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718", + "bytes": 2167, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-list.stdout", + "source_sha256": "6af90f834c45d9e3f498886d5603a4bc5cb3f84d10aed4447f56070efe1e6718" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stdout", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stdout", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stderr", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/cargo-metadata.stderr", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json", + "sha256": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e", + "bytes": 180, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/binary-before.json", + "source_sha256": "02d869e982cddd78d2b4255080892ed878300f015985c7b06bf032c9ae25d33e" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json", + "sha256": "a0dbc8b2f618b3e0c2157cdbbdba9e6a58676e8b02d5210e75263d33cb287651", + "bytes": 136, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/capture-state.json", + "source_sha256": "a0dbc8b2f618b3e0c2157cdbbdba9e6a58676e8b02d5210e75263d33cb287651" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json", + "sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe", + "bytes": 14451, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/source-after-execution.json", + "source_sha256": "bc150dcc98147c70c49fd4f17a0656608d7ca8433f14b7b176727c033d3356fe" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr", + "sha256": "df966e27aff804782c1143e8aa9325367cfb66ead53b60fdb86348e6175bea1a", + "bytes": 534, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-evidence/s5-fresh-capture/execution.stderr", + "source_sha256": "df966e27aff804782c1143e8aa9325367cfb66ead53b60fdb86348e6175bea1a" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-evidence.json", + "sha256": "b86a47a9a5d001abe3fe00a03b66df99b76f2223257665fd79b3abb246570b64", + "bytes": 11175, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-evidence.json", + "source_sha256": "b86a47a9a5d001abe3fe00a03b66df99b76f2223257665fd79b3abb246570b64" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.rs", + "sha256": "b24770c4ca6526c0d200eec787da638442dc8eedb3936cca1f4330ee51686cc5", + "bytes": 2008, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.rs", + "source_sha256": "b24770c4ca6526c0d200eec787da638442dc8eedb3936cca1f4330ee51686cc5" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-source-manifest.json", + "sha256": "5dfe99c6a4f959caa850ffeb716c047fd951fc9331ca8c33d097b15a85e2c5b3", + "bytes": 938, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-source-manifest.json", + "source_sha256": "5dfe99c6a4f959caa850ffeb716c047fd951fc9331ca8c33d097b15a85e2c5b3" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review.md", + "sha256": "f07605e110eb62c874d99372e9dc5bc8a04123dd937ffa17f4a736a9ad923e4d", + "bytes": 2703, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review.md", + "source_sha256": "f07605e110eb62c874d99372e9dc5bc8a04123dd937ffa17f4a736a9ad923e4d" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review.md", + "sha256": "238862119d90368ad64bd34c7c6239ef8a1d0a852d5fe2d299834e4204249c6b", + "bytes": 11598, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review.md", + "source_sha256": "238862119d90368ad64bd34c7c6239ef8a1d0a852d5fe2d299834e4204249c6b" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.json", + "sha256": "b947d90c3e29f0774a4d92d09e34cf7166aaae0ef666d96b3a3e89ec6457f930", + "bytes": 8215, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.json", + "source_sha256": "b947d90c3e29f0774a4d92d09e34cf7166aaae0ef666d96b3a3e89ec6457f930" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.stderr", + "sha256": "cc7485801f6bb80c9171c83cca83ec3c0adf9a546ea557061abe39d466e6fd91", + "bytes": 2400, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.stderr", + "source_sha256": "cc7485801f6bb80c9171c83cca83ec3c0adf9a546ea557061abe39d466e6fd91" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-source-manifest.json", + "sha256": "948869d96918ed4f98b9f237569e3c4059b0c67beb7ed8fa7b98b9992f7105a3", + "bytes": 51171, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-source-manifest.json", + "source_sha256": "948869d96918ed4f98b9f237569e3c4059b0c67beb7ed8fa7b98b9992f7105a3" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-evidence.json", + "sha256": "6c5f1e170737986c81462d7db4b6573c589a3704e5b8cf47a142e94b692e0540", + "bytes": 1959, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-evidence.json", + "source_sha256": "6c5f1e170737986c81462d7db4b6573c589a3704e5b8cf47a142e94b692e0540" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/reviewed-foundations-manifest.json", + "sha256": "5e8a93ce6d844198fb9cb73100993bf459775822548c77e548604164ad7cc933", + "bytes": 5186, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/reviewed-foundations-manifest.json", + "source_sha256": "5e8a93ce6d844198fb9cb73100993bf459775822548c77e548604164ad7cc933" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-parallel-junit.xml", + "sha256": "0545f63aa2d69444b6e7d93a38b1f05aaf970d341b06904a10631a16eaced580", + "bytes": 6783, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-parallel-junit.xml", + "source_sha256": "0545f63aa2d69444b6e7d93a38b1f05aaf970d341b06904a10631a16eaced580" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-evidence.json", + "sha256": "5d2a7b6844562af42acd9b532069fb18a4341ce0ea152329028ec41c52d1913a", + "bytes": 3144, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-evidence.json", + "source_sha256": "5d2a7b6844562af42acd9b532069fb18a4341ce0ea152329028ec41c52d1913a" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-junit.xml", + "sha256": "e522dad23d13903d49a0c712eb11acc323a670718f6c92b793c0029e3bee1618", + "bytes": 15415, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-author-junit.xml", + "source_sha256": "e522dad23d13903d49a0c712eb11acc323a670718f6c92b793c0029e3bee1618" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-evidence.json", + "sha256": "4d9d466f31c02de409fa03c915e3246f9c7dfd08ad53c2a0f697b7324f7fa401", + "bytes": 4323, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-evidence.json", + "source_sha256": "4d9d466f31c02de409fa03c915e3246f9c7dfd08ad53c2a0f697b7324f7fa401" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe-result.json", + "sha256": "f4a0a5267368cf8790ed916d538bbbf1bd4346ba98133dd7c2715b1845911768", + "bytes": 214, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe-result.json", + "source_sha256": "f4a0a5267368cf8790ed916d538bbbf1bd4346ba98133dd7c2715b1845911768" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.md", + "sha256": "ee46e7b085b791a1d115e528406996724a1bee872289ff78b44315c146ab90fe", + "bytes": 3166, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-review.md", + "source_sha256": "ee46e7b085b791a1d115e528406996724a1bee872289ff78b44315c146ab90fe" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-snapshot.json", + "sha256": "1be27a626cc5300c2d9026e27e4071f216ca8b4bc09516f3ec4e2623cc88d77c", + "bytes": 1034, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-snapshot.json", + "source_sha256": "1be27a626cc5300c2d9026e27e4071f216ca8b4bc09516f3ec4e2623cc88d77c" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-scope.json", + "sha256": "3f54b537c928eeba44b5055c764540c32ecc9424da48c2c0af0176bec9098d90", + "bytes": 4361, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-scope.json", + "source_sha256": "3f54b537c928eeba44b5055c764540c32ecc9424da48c2c0af0176bec9098d90" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.md", + "sha256": "583c1db9663998b036ed62b31c4e8fee9269b82d5b0eede713492f52671f0220", + "bytes": 3017, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.md", + "source_sha256": "583c1db9663998b036ed62b31c4e8fee9269b82d5b0eede713492f52671f0220" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.rs", + "sha256": "4093f1f0d456229717ebfe9cecd64d8f4b90e5bd375fd33f18906ffdf38a705a", + "bytes": 319, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-negative-traits.rs", + "source_sha256": "4093f1f0d456229717ebfe9cecd64d8f4b90e5bd375fd33f18906ffdf38a705a" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-junit.xml", + "sha256": "b539f8fa4d2cc86f107177ed8951a5858797c34e70bb17e43dcf260c1b4a6fee", + "bytes": 6784, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-junit.xml", + "source_sha256": "b539f8fa4d2cc86f107177ed8951a5858797c34e70bb17e43dcf260c1b4a6fee" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-junit.xml", + "sha256": "e25b7e41502e1a46fabfa82fa8c04edd67802310dbf393e9ee0298e517d1b8d8", + "bytes": 15415, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-junit.xml", + "source_sha256": "e25b7e41502e1a46fabfa82fa8c04edd67802310dbf393e9ee0298e517d1b8d8" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-binaries.json", + "sha256": "0269a6b5dd9910ecf56beb76f83fc07c1a73c7e40e41bb49b1fdd2d40dfb8af5", + "bytes": 423, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-binaries.json", + "source_sha256": "0269a6b5dd9910ecf56beb76f83fc07c1a73c7e40e41bb49b1fdd2d40dfb8af5" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe.rs", + "sha256": "a0bcf2101afc8f0d860123aceea0ce98e5d6987eab2e88f175c45e35eee799fe", + "bytes": 11800, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-probe.rs", + "source_sha256": "a0bcf2101afc8f0d860123aceea0ce98e5d6987eab2e88f175c45e35eee799fe" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-source-manifest.json", + "sha256": "714756d3f2dda06b71111bdda56bd8405c1dbd2456880b0ec34ac4d3f3d7cba2", + "bytes": 65147, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s3-facts-review-source-manifest.json", + "source_sha256": "714756d3f2dda06b71111bdda56bd8405c1dbd2456880b0ec34ac4d3f3d7cba2" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-serial-junit.xml", + "sha256": "379e8d170ad21d976015991470bb8ed894232b091876f84e31226b128345bfb7", + "bytes": 6783, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-author-serial-junit.xml", + "source_sha256": "379e8d170ad21d976015991470bb8ed894232b091876f84e31226b128345bfb7" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.json", + "sha256": "dab6d886ea2a9377b8475ea94a9b24b1743dc08e6b899ae62bda6fc0a93cfe85", + "bytes": 9711, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-windows-prefix-review.json", + "source_sha256": "dab6d886ea2a9377b8475ea94a9b24b1743dc08e6b899ae62bda6fc0a93cfe85" + }, + { + "path": "s4-refresh-review/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.log.gz", + "sha256": "44d44585ae491a876381dbe88337a78427c39a02f63515977a0405f148fe157c", + "bytes": 3524, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-tilde-probe.log", + "source_sha256": "417acd28bdc8143feeda839a24ffe0d01cbb1cbe3ef3f125a16dc1d370c06bb6" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/stability-backlog.md", + "sha256": "b70f603576e685282b7b454ec9039f833daeb780cc0b0992b256550cf13b698e", + "bytes": 14381, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/stability-backlog.md", + "source_sha256": "b70f603576e685282b7b454ec9039f833daeb780cc0b0992b256550cf13b698e" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/index.md", + "sha256": "87aece4723c5682ea96f4569b0b1813d48ae6c59d285ebd9c8a42c9be211a474", + "bytes": 111, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/index.md", + "source_sha256": "87aece4723c5682ea96f4569b0b1813d48ae6c59d285ebd9c8a42c9be211a474" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/index.md", + "sha256": "7838cd3370acc8f93714d31822cae464af95c2de077d31d69fa6909d27dc1d19", + "bytes": 145, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/index.md", + "source_sha256": "7838cd3370acc8f93714d31822cae464af95c2de077d31d69fa6909d27dc1d19" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/windows-emptiness-probe-must-not-terminate-jobs-2026-10-15.md", + "sha256": "fb8f2f903d100211666c0850a600d6b3e8286c369dffbdfe936a5af69aeff94b", + "bytes": 3715, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/windows-emptiness-probe-must-not-terminate-jobs-2026-10-15.md", + "source_sha256": "fb8f2f903d100211666c0850a600d6b3e8286c369dffbdfe936a5af69aeff94b" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/windows-mcp-daemon-state-publishes-before-api-readiness-2026-09-21.md", + "sha256": "e7db20c695cf90435fde23e54f4c113bb0ff38c8097154e5cc95926e8d9d2148", + "bytes": 2467, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/windows-mcp-daemon-state-publishes-before-api-readiness-2026-09-21.md", + "source_sha256": "e7db20c695cf90435fde23e54f4c113bb0ff38c8097154e5cc95926e8d9d2148" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/copilot-cli-native-hook-payload-omits-event-2026-09-22.md", + "sha256": "32345f16b266bb7f2cb9f2c36e908888fea928837d2f8d216b9379c15e3fea58", + "bytes": 1986, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/copilot-cli-native-hook-payload-omits-event-2026-09-22.md", + "source_sha256": "32345f16b266bb7f2cb9f2c36e908888fea928837d2f8d216b9379c15e3fea58" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/windows-notify-verbatim-watch-paths-2026-08-21.md", + "sha256": "fc1c15f94a6beac896dbe4fbc0c7851172c7307435c0bd2c0383d5f54c00cc78", + "bytes": 2619, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/windows-notify-verbatim-watch-paths-2026-08-21.md", + "source_sha256": "fc1c15f94a6beac896dbe4fbc0c7851172c7307435c0bd2c0383d5f54c00cc78" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/macos-current-rss-materialization-supervision-2026-08-18.md", + "sha256": "96013fdf89242f107a8b506e883dff0ae037d0b5d3294e461857b8cd59a50267", + "bytes": 2253, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/macos-current-rss-materialization-supervision-2026-08-18.md", + "source_sha256": "96013fdf89242f107a8b506e883dff0ae037d0b5d3294e461857b8cd59a50267" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/containment-proof-must-survive-candidate-cleanup-2026-10-15.md", + "sha256": "34c176cc6f641df51959f04d0694cf7ead4abbcefb218cac533bb4796d1e80ae", + "bytes": 3548, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/containment-proof-must-survive-candidate-cleanup-2026-10-15.md", + "source_sha256": "34c176cc6f641df51959f04d0694cf7ead4abbcefb218cac533bb4796d1e80ae" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/guard-native-resource-after-sdk-path-projection-2026-10-21.md", + "sha256": "e2a0c2b2e30071dac813845fcfa25f449dd81bcc873c74f74eb003605c82263b", + "bytes": 3001, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/guard-native-resource-after-sdk-path-projection-2026-10-21.md", + "source_sha256": "e2a0c2b2e30071dac813845fcfa25f449dd81bcc873c74f74eb003605c82263b" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/ci-release-artifact-recipe-drift-2026-08-13.md", + "sha256": "8abc08782c4de0b0732d3e17d83389f936b161cb72ed9071f4424a4708491811", + "bytes": 1734, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/ci-release-artifact-recipe-drift-2026-08-13.md", + "source_sha256": "8abc08782c4de0b0732d3e17d83389f936b161cb72ed9071f4424a4708491811" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/atomic-temp-path-process-scope-2026-08-21.md", + "sha256": "873044c1ded70c01d5e048de1d234a37d391a2927e18136da2ac6216be535027", + "bytes": 1901, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/atomic-temp-path-process-scope-2026-08-21.md", + "source_sha256": "873044c1ded70c01d5e048de1d234a37d391a2927e18136da2ac6216be535027" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/recovery-review-branch-and-live-version-alignment-2026-10-20.md", + "sha256": "d44b6c2c037b34f29903b62d907e7ba55e34d627a4430bd5f9405c941b15d681", + "bytes": 2283, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/recovery-review-branch-and-live-version-alignment-2026-10-20.md", + "source_sha256": "d44b6c2c037b34f29903b62d907e7ba55e34d627a4430bd5f9405c941b15d681" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/k-wiki-domain-errors-need-wire-boundary-regressions-2026-10-16.md", + "sha256": "86a67e567ff6299810850668095411b5ca671ab4050f1e5393af1d821b2e4356", + "bytes": 2236, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/k-wiki-domain-errors-need-wire-boundary-regressions-2026-10-16.md", + "source_sha256": "86a67e567ff6299810850668095411b5ca671ab4050f1e5393af1d821b2e4356" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/agent-hook-live-mcp-budget-eagain-2026-09-23.md", + "sha256": "e1bfe57c3611584a4355357ebf9836b3b16a07b3cbfc1b39cc725c75eeaf2cb3", + "bytes": 2645, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/agent-hook-live-mcp-budget-eagain-2026-09-23.md", + "source_sha256": "e1bfe57c3611584a4355357ebf9836b3b16a07b3cbfc1b39cc725c75eeaf2cb3" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/untagged-release-pr-blocks-later-proposals-2026-09-24.md", + "sha256": "efdf88a699dc3543ba8acae4c2912d1227cbf978c56f451f158daf6ce705aa24", + "bytes": 2510, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/untagged-release-pr-blocks-later-proposals-2026-09-24.md", + "source_sha256": "efdf88a699dc3543ba8acae4c2912d1227cbf978c56f451f158daf6ce705aa24" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/lifecycle-fixtures-must-recognize-retired-startups-2026-10-16.md", + "sha256": "a9a22fc2bb20309451aac96df351748b4c6226bec7c503b0ab7fd18364391b3d", + "bytes": 2137, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/lifecycle-fixtures-must-recognize-retired-startups-2026-10-16.md", + "source_sha256": "a9a22fc2bb20309451aac96df351748b4c6226bec7c503b0ab7fd18364391b3d" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/native-wiki-smoke-needs-recommended-metadata-2026-10-16.md", + "sha256": "19616adf6edc905ab2ef5c1946d6435cc76205adaefdc41831eaabe6281fb056", + "bytes": 1967, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/native-wiki-smoke-needs-recommended-metadata-2026-10-16.md", + "source_sha256": "19616adf6edc905ab2ef5c1946d6435cc76205adaefdc41831eaabe6281fb056" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/native-startup-backend-label-ci-2026-09-08.md", + "sha256": "6f51c9792ca69a08f4ae5b0b0026d083da270740c35e843a8aa0904490d1da03", + "bytes": 2573, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/native-startup-backend-label-ci-2026-09-08.md", + "source_sha256": "6f51c9792ca69a08f4ae5b0b0026d083da270740c35e843a8aa0904490d1da03" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/stopped-http-daemon-transport-send-error-2026-08-24.md", + "sha256": "0ec06e9cfc462cf20f6c264905db3943cfb8ea4635d57c29feee8043f97792a8", + "bytes": 3784, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/stopped-http-daemon-transport-send-error-2026-08-24.md", + "source_sha256": "0ec06e9cfc462cf20f6c264905db3943cfb8ea4635d57c29feee8043f97792a8" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/k-wiki-yaml-errors-need-origin-redaction-2026-10-16.md", + "sha256": "dd36fbe95e9edade8fdaf983549f121dbb4d3a3c489d7547b75b3025fc1b9db1", + "bytes": 2297, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/k-wiki-yaml-errors-need-origin-redaction-2026-10-16.md", + "source_sha256": "dd36fbe95e9edade8fdaf983549f121dbb4d3a3c489d7547b75b3025fc1b9db1" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/direct-destination-spelling-recovery-identity-2026-09-08.md", + "sha256": "ed15cd682e7725d2a0366ecc883ea889fc688b9516c2b4d5ba051186759b7d95", + "bytes": 2462, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/direct-destination-spelling-recovery-identity-2026-09-08.md", + "source_sha256": "ed15cd682e7725d2a0366ecc883ea889fc688b9516c2b4d5ba051186759b7d95" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/mcp-daemon-client-disconnect-broken-pipe-2026-09-22.md", + "sha256": "c3c46bbb6a371af191fa82a7f2b113c9dec73fd90a20fa96b75cfce604c48fb3", + "bytes": 2393, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/mcp-daemon-client-disconnect-broken-pipe-2026-09-22.md", + "source_sha256": "c3c46bbb6a371af191fa82a7f2b113c9dec73fd90a20fa96b75cfce604c48fb3" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/coordinator-stale-route-authentication-recovery-2026-08-20.md", + "sha256": "cca5202d3efd03cedef7fa09ee3e502bbf78e224cc101303042b99c4cd2455ba", + "bytes": 1884, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/coordinator-stale-route-authentication-recovery-2026-08-20.md", + "source_sha256": "cca5202d3efd03cedef7fa09ee3e502bbf78e224cc101303042b99c4cd2455ba" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/slow-reader-test-waits-for-response-transmission-2026-09-24.md", + "sha256": "16992cc061c05a114d0fab558ed0b86283767b0611d2903cf4d0281417945e26", + "bytes": 2085, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/slow-reader-test-waits-for-response-transmission-2026-09-24.md", + "source_sha256": "16992cc061c05a114d0fab558ed0b86283767b0611d2903cf4d0281417945e26" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/materialization-reference-memory-gates-2026-08-18.md", + "sha256": "de4ef170b04a3e979f37c5242f39e74d549b3916876d97bd0069ad0facef75ef", + "bytes": 1837, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/materialization-reference-memory-gates-2026-08-18.md", + "source_sha256": "de4ef170b04a3e979f37c5242f39e74d549b3916876d97bd0069ad0facef75ef" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/coordinator-watcher-readiness-race-2026-08-21.md", + "sha256": "19825664cf3499746857f6f02edcced26605a03755b054e09ef5ca048cb8b41f", + "bytes": 2638, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/coordinator-watcher-readiness-race-2026-08-21.md", + "source_sha256": "19825664cf3499746857f6f02edcced26605a03755b054e09ef5ca048cb8b41f" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/database-use-unicode-alias-parent-family-2026-10-21.md", + "sha256": "01d6d14432a32b2bf3f43244a5b8811426e1250df7d93c65837543acfb50a84d", + "bytes": 3035, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/database-use-unicode-alias-parent-family-2026-10-21.md", + "source_sha256": "01d6d14432a32b2bf3f43244a5b8811426e1250df7d93c65837543acfb50a84d" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/windows-direct-materialization-path-limit-2026-08-13.md", + "sha256": "689e41a8df9ae3d8d8eb1e2afdc5a89a1a8116f071a2b7670a2dd317e80d93af", + "bytes": 1767, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/windows-direct-materialization-path-limit-2026-08-13.md", + "source_sha256": "689e41a8df9ae3d8d8eb1e2afdc5a89a1a8116f071a2b7670a2dd317e80d93af" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/index.md", + "sha256": "98d6b26ae479e29b0253cf2de40ac081bb4fe08f27ea07efd64fd76a8c2e1308", + "bytes": 165, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/index.md", + "source_sha256": "98d6b26ae479e29b0253cf2de40ac081bb4fe08f27ea07efd64fd76a8c2e1308" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/wiki-recall-utf8-snippet-panic-2026-10-01.md", + "sha256": "c323039532274caf784ffa6c0eaffc8f15cd60386427cf0d8ee3427d0961dee3", + "bytes": 2637, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/wiki-recall-utf8-snippet-panic-2026-10-01.md", + "source_sha256": "c323039532274caf784ffa6c0eaffc8f15cd60386427cf0d8ee3427d0961dee3" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/config-root-loss-stops-refresher-2026-09-08.md", + "sha256": "25749a43e8d29514164f3a0b9ea3a0aaa0873a02f858a3b29dee6b22f81da84d", + "bytes": 2613, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/config-root-loss-stops-refresher-2026-09-08.md", + "source_sha256": "25749a43e8d29514164f3a0b9ea3a0aaa0873a02f858a3b29dee6b22f81da84d" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/windows-watch-test-marker-lock-2026-08-20.md", + "sha256": "5a7c02c054f3c6b1700e65ecbf559af5099736022ab37469a81e19bdd71ab078", + "bytes": 1536, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/windows-watch-test-marker-lock-2026-08-20.md", + "source_sha256": "5a7c02c054f3c6b1700e65ecbf559af5099736022ab37469a81e19bdd71ab078" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/mcp-refresh-rebuild-convoy-memory-overload-2026-08-14.md", + "sha256": "b6d8d43d95a6a106ed8d171522593bbb8c3cb265323d7d00ab5f604e3de01ff0", + "bytes": 3119, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/mcp-refresh-rebuild-convoy-memory-overload-2026-08-14.md", + "source_sha256": "b6d8d43d95a6a106ed8d171522593bbb8c3cb265323d7d00ab5f604e3de01ff0" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/direct-prepared-replay-deletes-promoted-sidecar-2026-09-23.md", + "sha256": "b792bbcc60b629ce871d350b77455173bf147ac1e84d6c9bcf1bcac428d279dc", + "bytes": 2626, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/direct-prepared-replay-deletes-promoted-sidecar-2026-09-23.md", + "source_sha256": "b792bbcc60b629ce871d350b77455173bf147ac1e84d6c9bcf1bcac428d279dc" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/successful-release-merge-starved-by-main-tip-guard-2026-09-23.md", + "sha256": "2af2037c6f0ff099b602fe17193db91233e24d0bc6ed15975879b97a730d4f02", + "bytes": 3243, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/successful-release-merge-starved-by-main-tip-guard-2026-09-23.md", + "source_sha256": "2af2037c6f0ff099b602fe17193db91233e24d0bc6ed15975879b97a730d4f02" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/coordinator-frame-timeout-retry-2026-08-21.md", + "sha256": "80bf6d574cda7e7725b517fcd5d2d9d48e48fa79bc72d050bfa53e456d5e1ecf", + "bytes": 2702, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/coordinator-frame-timeout-retry-2026-08-21.md", + "source_sha256": "80bf6d574cda7e7725b517fcd5d2d9d48e48fa79bc72d050bfa53e456d5e1ecf" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/episodic/recovery-fixture-caller-and-handoff-preconditions-2026-10-20.md", + "sha256": "1b1d95b8a78540f0d7cfc017a6eeec9184136b3e25d23c15181ef2c89626dd1f", + "bytes": 3346, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/episodic/recovery-fixture-caller-and-handoff-preconditions-2026-10-20.md", + "source_sha256": "1b1d95b8a78540f0d7cfc017a6eeec9184136b3e25d23c15181ef2c89626dd1f" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/shared-managed-mcp-http-daemon-per-repository-2026-08-20.md", + "sha256": "ab4669c486bdd7c0d3cbff612999a66927196ef0bddc4abe0917ca6910155b6c", + "bytes": 1566, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/shared-managed-mcp-http-daemon-per-repository-2026-08-20.md", + "source_sha256": "ab4669c486bdd7c0d3cbff612999a66927196ef0bddc4abe0917ca6910155b6c" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/mcp-stdio-no-idle-exit-2026-08-20.md", + "sha256": "ec403397fcaf44e7609ac9b67780f3df7dd400bf1c1446bfb79283b02fc94665", + "bytes": 1352, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/mcp-stdio-no-idle-exit-2026-08-20.md", + "source_sha256": "ec403397fcaf44e7609ac9b67780f3df7dd400bf1c1446bfb79283b02fc94665" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/index.md", + "sha256": "1500ba656dab6f82f12a492f47ad8f62e09d05dbbec31f919dce65dd89ed9889", + "bytes": 141, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/index.md", + "source_sha256": "1500ba656dab6f82f12a492f47ad8f62e09d05dbbec31f919dce65dd89ed9889" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/refresh-status-dirty-epoch-foundation-2026-09-08.md", + "sha256": "c65da2618578cc251befcc630785e6ad0f5b26be3e46391d294074cd33fa0c49", + "bytes": 1982, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/refresh-status-dirty-epoch-foundation-2026-09-08.md", + "source_sha256": "c65da2618578cc251befcc630785e6ad0f5b26be3e46391d294074cd33fa0c49" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/mcp-coordinator-worker-ownership-boundary.md", + "sha256": "bebdc0b1a199f9aa6f055b430510bf58d25508066cee03dba7dcaf739fefb655", + "bytes": 2305, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/mcp-coordinator-worker-ownership-boundary.md", + "source_sha256": "bebdc0b1a199f9aa6f055b430510bf58d25508066cee03dba7dcaf739fefb655" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/k-wiki-graph-layer-contract-2026-08-13.md", + "sha256": "2acc8b852dc645901506445c260a07510b27f293ff4f5dac4871b5c93697f61b", + "bytes": 1285, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/k-wiki-graph-layer-contract-2026-08-13.md", + "source_sha256": "2acc8b852dc645901506445c260a07510b27f293ff4f5dac4871b5c93697f61b" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/semantic-artifact-reuse-schema-upgrade-boundary.md", + "sha256": "ced98c016647c22d94ff8dddfb7f79713345bcebcc1b26ff4de21381fcd7ccad", + "bytes": 1851, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/semantic-artifact-reuse-schema-upgrade-boundary.md", + "source_sha256": "ced98c016647c22d94ff8dddfb7f79713345bcebcc1b26ff4de21381fcd7ccad" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/raw-artifact-reuse-rebuild-boundary-2026-09-23.md", + "sha256": "8819ede99354fc445ebe3fb5fdce4f2914b2f0af0fe025ebc54bdaec256fcac6", + "bytes": 2035, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/raw-artifact-reuse-rebuild-boundary-2026-09-23.md", + "source_sha256": "8819ede99354fc445ebe3fb5fdce4f2914b2f0af0fe025ebc54bdaec256fcac6" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/semantic/coordinator-predispatch-only-replay-2026-09-23.md", + "sha256": "d11eaa95e5980ae1c1628ca15e2ada897b391c1d2151f7121ba8c92ba1495f30", + "bytes": 2420, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/semantic/coordinator-predispatch-only-replay-2026-09-23.md", + "source_sha256": "d11eaa95e5980ae1c1628ca15e2ada897b391c1d2151f7121ba8c92ba1495f30" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/large-self-hosting-rebuild-memory-budget-2026-08-28.md", + "sha256": "55a2284660069450bb37c7ba4b9c518a1aca1e731817d80ceaa9daf60eb54bef", + "bytes": 1996, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/large-self-hosting-rebuild-memory-budget-2026-08-28.md", + "source_sha256": "55a2284660069450bb37c7ba4b9c518a1aca1e731817d80ceaa9daf60eb54bef" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/checkout-free-gh-release-repository-context-2026-08-17.md", + "sha256": "00f0df984ad6a6401f38f8d5fa5cd34386274c85cde44b511fee73b200607c43", + "bytes": 1962, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/checkout-free-gh-release-repository-context-2026-08-17.md", + "source_sha256": "00f0df984ad6a6401f38f8d5fa5cd34386274c85cde44b511fee73b200607c43" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/ladybug-release-extension-cache-variants-2026-08-13.md", + "sha256": "adaeb22ab532a90ad3305150b2f6d67c06158561eda9146821f79c694e7d0150", + "bytes": 2453, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/ladybug-release-extension-cache-variants-2026-08-13.md", + "source_sha256": "adaeb22ab532a90ad3305150b2f6d67c06158561eda9146821f79c694e7d0150" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/shared-cargo-target-frozen-binary-provenance-2026-10-21.md", + "sha256": "1cddca5ed7337fb0ffb24626cc21a621fbd5339526e1c1e275a7de88b2594e5b", + "bytes": 3161, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/shared-cargo-target-frozen-binary-provenance-2026-10-21.md", + "source_sha256": "1cddca5ed7337fb0ffb24626cc21a621fbd5339526e1c1e275a7de88b2594e5b" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/cargo-package-ignored-metadata-clean-worktree-2026-08-25.md", + "sha256": "04e53b15116be3d12b57ee66b941d3edfe523075655509561c78eb53761f283b", + "bytes": 1721, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/cargo-package-ignored-metadata-clean-worktree-2026-08-25.md", + "source_sha256": "04e53b15116be3d12b57ee66b941d3edfe523075655509561c78eb53761f283b" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/ladybug-0183-link-and-extension-alignment-2026-08-14.md", + "sha256": "8c12286fb0e657ef282794491fcd7a1dd83f6e8fcd2d2cbb09c3d1a98243cbed", + "bytes": 2544, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/ladybug-0183-link-and-extension-alignment-2026-08-14.md", + "source_sha256": "8c12286fb0e657ef282794491fcd7a1dd83f6e8fcd2d2cbb09c3d1a98243cbed" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/stable-nonblocking-hook-cache-claim-lock-2026-09-24.md", + "sha256": "cd7aeca0b0bbb525bfeb55e0276b7aa981a740b72973e41c9ea731010a35e431", + "bytes": 2406, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/stable-nonblocking-hook-cache-claim-lock-2026-09-24.md", + "source_sha256": "cd7aeca0b0bbb525bfeb55e0276b7aa981a740b72973e41c9ea731010a35e431" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/crates-io-package-size-gate-2026-08-24.md", + "sha256": "3ec14615163e2ed7ebbcd06b8f9e36428a49066f6f501ec2b8bb6233dd8bdd3c", + "bytes": 2601, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/crates-io-package-size-gate-2026-08-24.md", + "source_sha256": "3ec14615163e2ed7ebbcd06b8f9e36428a49066f6f501ec2b8bb6233dd8bdd3c" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/agent-hook-verification-per-event-and-portable-temp-paths-2026-09-22.md", + "sha256": "fc668276ba53c62ea1d5c9bfe29a1dd1f7be5422fbb10ced0f1e00305f4cd248", + "bytes": 1557, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/agent-hook-verification-per-event-and-portable-temp-paths-2026-09-22.md", + "source_sha256": "fc668276ba53c62ea1d5c9bfe29a1dd1f7be5422fbb10ced0f1e00305f4cd248" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/mcp-restart-session-aliasing-regression-2026-09-24.md", + "sha256": "8acf4b3da1f8ba705517e0ee975c3ad3cbcc2101f89c99e8e45b0b6536140215", + "bytes": 2104, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/mcp-restart-session-aliasing-regression-2026-09-24.md", + "source_sha256": "8acf4b3da1f8ba705517e0ee975c3ad3cbcc2101f89c99e8e45b0b6536140215" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/vendored-tree-sitter-scanner-callback-contract-2026-08-25.md", + "sha256": "bf4df0f23f902bede8db064fb0036a9f0199290689dc8601204e9aa2a4b6ea02", + "bytes": 2309, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/vendored-tree-sitter-scanner-callback-contract-2026-08-25.md", + "source_sha256": "bf4df0f23f902bede8db064fb0036a9f0199290689dc8601204e9aa2a4b6ea02" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/release-publication-skipped-ancestor-chain-2026-08-17.md", + "sha256": "57d7a0c9d0b155decf9923158b2604983592aaee0dd0a1b18f806b0396b98b7f", + "bytes": 1886, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/release-publication-skipped-ancestor-chain-2026-08-17.md", + "source_sha256": "57d7a0c9d0b155decf9923158b2604983592aaee0dd0a1b18f806b0396b98b7f" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/rust-modular-code-procedure.md", + "sha256": "01d0bbcab9943839ffe38712fa697b896dc075ac53e533451c190537a635c0d8", + "bytes": 5509, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/rust-modular-code-procedure.md", + "source_sha256": "01d0bbcab9943839ffe38712fa697b896dc075ac53e533451c190537a635c0d8" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/workflow-run-release-exact-run-binding-2026-08-14.md", + "sha256": "9efd65d18f6aac69fdfde9724ff963ff34c2eae61bc59fc6c778bee461c0078f", + "bytes": 2192, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/workflow-run-release-exact-run-binding-2026-08-14.md", + "source_sha256": "9efd65d18f6aac69fdfde9724ff963ff34c2eae61bc59fc6c778bee461c0078f" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/agent-hook-lifecycle-transaction-and-ownership-2026-09-22.md", + "sha256": "5bd6caafd6776e60d03e195882be02b0fd1e410aede45919c8d5abfbf8fe7b63", + "bytes": 2094, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/agent-hook-lifecycle-transaction-and-ownership-2026-09-22.md", + "source_sha256": "5bd6caafd6776e60d03e195882be02b0fd1e410aede45919c8d5abfbf8fe7b63" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/agent-hook-lifecycle-stale-ownership-and-daemon-2026-09-22.md", + "sha256": "a79c40872b1aa67ce79b221325fb2d818d60020a8d49af336ec523004c816a64", + "bytes": 1922, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/agent-hook-lifecycle-stale-ownership-and-daemon-2026-09-22.md", + "source_sha256": "a79c40872b1aa67ce79b221325fb2d818d60020a8d49af336ec523004c816a64" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/refresh-context-metrics-clippy-2026-08-17.md", + "sha256": "4ac71e4ec991f32706563edbca2e798ee686165273fdf520cd1a92a6c4e88352", + "bytes": 1838, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/refresh-context-metrics-clippy-2026-08-17.md", + "source_sha256": "4ac71e4ec991f32706563edbca2e798ee686165273fdf520cd1a92a6c4e88352" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/ladybug-macos-x86-compiler-runtime-2026-08-14.md", + "sha256": "65f3868ea257af1e98d29e5a4f2a92c93e72ad6291ca75904623f0257bbc9a33", + "bytes": 1986, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/ladybug-macos-x86-compiler-runtime-2026-08-14.md", + "source_sha256": "65f3868ea257af1e98d29e5a4f2a92c93e72ad6291ca75904623f0257bbc9a33" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/windows-managed-run-cleanup-disappearance-race-2026-09-22.md", + "sha256": "b4b9fe1959b68837aa11456b6dd7e2e5eac85310e6992d52f1137ec1f527722b", + "bytes": 2007, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/windows-managed-run-cleanup-disappearance-race-2026-09-22.md", + "source_sha256": "b4b9fe1959b68837aa11456b6dd7e2e5eac85310e6992d52f1137ec1f527722b" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/index.md", + "sha256": "f90a5641d854a85107a58e99505c7b01db844cf895a291825c66d85bea760546", + "bytes": 130, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/index.md", + "source_sha256": "f90a5641d854a85107a58e99505c7b01db844cf895a291825c66d85bea760546" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/rust-secure-coding-procedure.md", + "sha256": "7a81bc947232c400fde58e53f77174f8c3d49ba77dd216d9ef4d1fc2ed237495", + "bytes": 5009, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/rust-secure-coding-procedure.md", + "source_sha256": "7a81bc947232c400fde58e53f77174f8c3d49ba77dd216d9ef4d1fc2ed237495" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/windows-ci-bundled-extension-feature-2026-08-13.md", + "sha256": "01460b3810257f59424363ffe017a3811f4865a831e851acf322441c32774afe", + "bytes": 2174, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/windows-ci-bundled-extension-feature-2026-08-13.md", + "source_sha256": "01460b3810257f59424363ffe017a3811f4865a831e851acf322441c32774afe" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/rust-performance-memory-efficiency-procedure.md", + "sha256": "9659831c4a9111858ac326818e94cd2247f97c3b1f0b02efe41fa535d4821291", + "bytes": 3869, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/rust-performance-memory-efficiency-procedure.md", + "source_sha256": "9659831c4a9111858ac326818e94cd2247f97c3b1f0b02efe41fa535d4821291" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/windows-native-lifecycle-test-profile-2026-10-15.md", + "sha256": "9bf1f41d57a7f47ad9eca3c63f13bd9f66e04611aebf0172618a909bac6699e7", + "bytes": 3261, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/windows-native-lifecycle-test-profile-2026-10-15.md", + "source_sha256": "9bf1f41d57a7f47ad9eca3c63f13bd9f66e04611aebf0172618a909bac6699e7" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/crates-io-publish-idempotent-retry-2026-08-24.md", + "sha256": "1185a270b17580ff4f4b2f2efc6e1c90ab063de4d5f8825142fea221c80b4974", + "bytes": 2432, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/crates-io-publish-idempotent-retry-2026-08-24.md", + "source_sha256": "1185a270b17580ff4f4b2f2efc6e1c90ab063de4d5f8825142fea221c80b4974" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/exact-commit-release-publication-and-recovery-2026-09-24.md", + "sha256": "96659e7bb666dcf239dbb18fe49e6aabe514dbae79a6a864a3a65b1db8147a36", + "bytes": 3873, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/exact-commit-release-publication-and-recovery-2026-09-24.md", + "source_sha256": "96659e7bb666dcf239dbb18fe49e6aabe514dbae79a6a864a3a65b1db8147a36" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/shared-http-daemon-upgrade-requires-registration-migration-2026-08-21.md", + "sha256": "bb0d2d4241670411eb82fab1a14aaab667d79c726b9d94a08f55a5ff165debd8", + "bytes": 1588, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/shared-http-daemon-upgrade-requires-registration-migration-2026-08-21.md", + "source_sha256": "bb0d2d4241670411eb82fab1a14aaab667d79c726b9d94a08f55a5ff165debd8" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/memory/procedural/release-please-stale-pending-tag-guard-2026-08-17.md", + "sha256": "2eca810128ef6ba7632cbb7dabb5b7f1f8592685798c498e943d201c38a1c541", + "bytes": 2491, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/memory/procedural/release-please-stale-pending-tag-guard-2026-08-17.md", + "source_sha256": "2eca810128ef6ba7632cbb7dabb5b7f1f8592685798c498e943d201c38a1c541" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/release-verification.md", + "sha256": "099f8dee41675237965ca1d54d61a506de96fc3ffbcd674dfa97a4552d32b649", + "bytes": 8612, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/release-verification.md", + "source_sha256": "099f8dee41675237965ca1d54d61a506de96fc3ffbcd674dfa97a4552d32b649" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/materialization-pipeline.md", + "sha256": "99a5056c059cd9c499897845c26a15a4dbc460729cebed0b0b22f31b22f4ade9", + "bytes": 9226, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/materialization-pipeline.md", + "source_sha256": "99a5056c059cd9c499897845c26a15a4dbc460729cebed0b0b22f31b22f4ade9" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/overview.md", + "sha256": "3710c89eae9a6d3f30899b0f1b9e4948c4b9fb09bae34962f507ef9a85dd7e78", + "bytes": 4775, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/overview.md", + "source_sha256": "3710c89eae9a6d3f30899b0f1b9e4948c4b9fb09bae34962f507ef9a85dd7e78" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/language-html.md", + "sha256": "2e2b1a2a1997b81c735361d8acc72027c1fe919f15ffba443bb92e5eb70ed512", + "bytes": 1132, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/language-html.md", + "source_sha256": "2e2b1a2a1997b81c735361d8acc72027c1fe919f15ffba443bb92e5eb70ed512" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/graph-disk-retention-review-2026-10-20.md", + "sha256": "a3af205590764a958631ee9bbb27e82a056b11648f7278c1fb45eda785e81bff", + "bytes": 20202, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/graph-disk-retention-review-2026-10-20.md", + "source_sha256": "a3af205590764a958631ee9bbb27e82a056b11648f7278c1fb45eda785e81bff" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/operation-paths.md", + "sha256": "f700053df572091aed938c64bfb63c3050f99d312843d5908c22f5518b283657", + "bytes": 11893, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/operation-paths.md", + "source_sha256": "f700053df572091aed938c64bfb63c3050f99d312843d5908c22f5518b283657" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/invariants.md", + "sha256": "778c4fe26697b8947f0b08c7ec70f85ba5c1718656002edb9ac8a77d922b015a", + "bytes": 8762, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/invariants.md", + "source_sha256": "778c4fe26697b8947f0b08c7ec70f85ba5c1718656002edb9ac8a77d922b015a" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/agent-loop-hooks.md", + "sha256": "fa6d63c3202ff1e113028c7a8567293410599ac6b3307bd7a1cd90f2bbe86029", + "bytes": 4183, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/agent-loop-hooks.md", + "source_sha256": "fa6d63c3202ff1e113028c7a8567293410599ac6b3307bd7a1cd90f2bbe86029" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/language-typescript-tsx.md", + "sha256": "d6520c8d3d01e5681c8984f6d610698b4a49c1c88b73b026936ec9dae62bf46c", + "bytes": 1263, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/language-typescript-tsx.md", + "source_sha256": "d6520c8d3d01e5681c8984f6d610698b4a49c1c88b73b026936ec9dae62bf46c" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/graph-disk-cleanup-contract.md", + "sha256": "b8064a83767fee30f1ce7b991ee1e515f7ee340ac2fb826cccac68dd2923dc61", + "bytes": 60795, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/graph-disk-cleanup-contract.md", + "source_sha256": "b8064a83767fee30f1ce7b991ee1e515f7ee340ac2fb826cccac68dd2923dc61" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/graph-runtime.md", + "sha256": "f32d3ac114ee6fbe0f3303ef35ad239d9ee633bbbc8499474a72d4ec65911bd4", + "bytes": 14845, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/graph-runtime.md", + "source_sha256": "f32d3ac114ee6fbe0f3303ef35ad239d9ee633bbbc8499474a72d4ec65911bd4" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/repository-map.md", + "sha256": "ad0273bd362d37346e16b8fd446a65d62462f3ae7b5ce1e502f5e44961d1c0f1", + "bytes": 5479, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/repository-map.md", + "source_sha256": "ad0273bd362d37346e16b8fd446a65d62462f3ae7b5ce1e502f5e44961d1c0f1" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/worker-recovery-regression-contract.md", + "sha256": "ade6d75a85a272e993b3e5752def9307782915a9fd40843f82eee6061f49632c", + "bytes": 15562, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/worker-recovery-regression-contract.md", + "source_sha256": "ade6d75a85a272e993b3e5752def9307782915a9fd40843f82eee6061f49632c" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/language-webassembly.md", + "sha256": "e5f2308d5c7fef6009e335a7b9b3cfa9b77bc8ef8dbb61760d0dedfd5a55b3ca", + "bytes": 2046, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/language-webassembly.md", + "source_sha256": "e5f2308d5c7fef6009e335a7b9b3cfa9b77bc8ef8dbb61760d0dedfd5a55b3ca" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/graph-storage-lifecycle.md", + "sha256": "953eebfcdca3fec7e0d520767f0d09ec6c465d6ba4f4411e255f2d0a6c9a8353", + "bytes": 15819, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/graph-storage-lifecycle.md", + "source_sha256": "953eebfcdca3fec7e0d520767f0d09ec6c465d6ba4f4411e255f2d0a6c9a8353" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/graph-disk-cleanup-spec-reviews-2026-10-20.md", + "sha256": "c47a3e6be27346ed997cc5b43ef7433f4251ef103005fc06af1f8659a1abc9c7", + "bytes": 13787, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/graph-disk-cleanup-spec-reviews-2026-10-20.md", + "source_sha256": "c47a3e6be27346ed997cc5b43ef7433f4251ef103005fc06af1f8659a1abc9c7" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/graph-freshness-recovery.md", + "sha256": "08f20247d9c730a247179a66787bebc9efb540feff4cc6cf3e82cbbf631a6872", + "bytes": 10396, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/graph-freshness-recovery.md", + "source_sha256": "08f20247d9c730a247179a66787bebc9efb540feff4cc6cf3e82cbbf631a6872" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/knowledge-wiki.md", + "sha256": "b1738e07955e3529569852c0a7f7ae1014561ca43a39cc04c92cf6d65c74885e", + "bytes": 7644, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/knowledge-wiki.md", + "source_sha256": "b1738e07955e3529569852c0a7f7ae1014561ca43a39cc04c92cf6d65c74885e" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/agent-memory.md", + "sha256": "54c5a3591a4f96c1ad3f100f8dad1529bccebc437fdc6cedb16118363e141527", + "bytes": 3957, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/agent-memory.md", + "source_sha256": "54c5a3591a4f96c1ad3f100f8dad1529bccebc437fdc6cedb16118363e141527" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/language-javascript.md", + "sha256": "06e7603156ee08348414c0b903dffb000805bb2c868fc3fbb77d974cc33259db", + "bytes": 1112, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/language-javascript.md", + "source_sha256": "06e7603156ee08348414c0b903dffb000805bb2c868fc3fbb77d974cc33259db" + }, + { + "path": "s4-refresh-review/final-repo/knowledge/architecture/language-css.md", + "sha256": "da78f20e76ffe519161cd6d8e0c13df113ad9ee7508ab9c6512026bc82d25a15", + "bytes": 1093, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/knowledge/architecture/language-css.md", + "source_sha256": "da78f20e76ffe519161cd6d8e0c13df113ad9ee7508ab9c6512026bc82d25a15" + }, + { + "path": "s4-refresh-review/final-repo/assets/query_helpers.json", + "sha256": "c91ab54b35de0c26a14cffbb531e0a4374763f4ccb723e6d8d3bac8f2a91b689", + "bytes": 4496, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/assets/query_helpers.json", + "source_sha256": "c91ab54b35de0c26a14cffbb531e0a4374763f4ccb723e6d8d3bac8f2a91b689" + }, + { + "path": "s4-refresh-review/final-repo/assets/graph_schema.json", + "sha256": "244fbce950f51ed5db6b52fa8aadb418fdd96d73bc475b4a1a0d1fb66e34d3bd", + "bytes": 185546, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/assets/graph_schema.json", + "source_sha256": "244fbce950f51ed5db6b52fa8aadb418fdd96d73bc475b4a1a0d1fb66e34d3bd" + }, + { + "path": "s4-refresh-review/final-repo/assets/mcp_tool_specs.json", + "sha256": "b7fd145e4885b4009e6d8ccecb0300eb6286ca7d117a915934478c64c72ab060", + "bytes": 8789, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/assets/mcp_tool_specs.json", + "source_sha256": "b7fd145e4885b4009e6d8ccecb0300eb6286ca7d117a915934478c64c72ab060" + }, + { + "path": "s4-refresh-review/final-repo/assets/architecture_queries.json", + "sha256": "f14a2f43e0f1d1748f39aa0f7a92791e832955cf25a2435357d25bfa1d33a41c", + "bytes": 12164, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/assets/architecture_queries.json", + "source_sha256": "f14a2f43e0f1d1748f39aa0f7a92791e832955cf25a2435357d25bfa1d33a41c" + }, + { + "path": "s4-refresh-review/final-repo/vendor/tree-sitter-wat/UPSTREAM.md", + "sha256": "edb5f2e65b8131e5ac0f6081e4fa7b8e271034cfe1f8f6577f8f70a7a75ce225", + "bytes": 1905, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/vendor/tree-sitter-wat/UPSTREAM.md", + "source_sha256": "edb5f2e65b8131e5ac0f6081e4fa7b8e271034cfe1f8f6577f8f70a7a75ce225" + }, + { + "path": "s4-refresh-review/final-repo/vendor/tree-sitter-wat/src/node-types.json", + "sha256": "b8ee14131666f1553d8948fa661526b2aa5d4bc5f64aa5f28f662fb33ebf453a", + "bytes": 33410, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/vendor/tree-sitter-wat/src/node-types.json", + "source_sha256": "b8ee14131666f1553d8948fa661526b2aa5d4bc5f64aa5f28f662fb33ebf453a" + }, + { + "path": "s4-refresh-review/final-repo/src/protocol.rs", + "sha256": "c56b04d18060e0ae2ce43e4c9d5b59535078d7186bfcac819005d2605ae01b3d", + "bytes": 39320, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/protocol.rs", + "source_sha256": "c56b04d18060e0ae2ce43e4c9d5b59535078d7186bfcac819005d2605ae01b3d" + }, + { + "path": "s4-refresh-review/final-repo/src/normalize.rs", + "sha256": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0", + "bytes": 3338, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/normalize.rs", + "source_sha256": "e5ab94bb1d784601b13172c70228b5a1937672a7812f5336964de2c302914fe0" + }, + { + "path": "s4-refresh-review/final-repo/src/error.rs", + "sha256": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2", + "bytes": 4864, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/error.rs", + "source_sha256": "f2fa15a48961dbedbf92a2dae6b0ee967419a2f5cdef6f0f569fd877cdad44a2" + }, + { + "path": "s4-refresh-review/final-repo/src/lib.rs", + "sha256": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e", + "bytes": 799, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/lib.rs", + "source_sha256": "8102710b8a2b5b7d12d4f07c0368d60f1c25fd659a0aae127b5368d71bb1d64e" + }, + { + "path": "s4-refresh-review/final-repo/src/source_selection.rs", + "sha256": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16", + "bytes": 11012, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/source_selection.rs", + "source_sha256": "c68d19786b47a1e4209d2d8a4bc62e095ddc583a051a47e93400435f0b770e16" + }, + { + "path": "s4-refresh-review/final-repo/src/bootstrap.rs", + "sha256": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645", + "bytes": 3992, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/bootstrap.rs", + "source_sha256": "46029abcc79b8f23a99b630cc3e57cdb94e5cace1be0adc47d55819075ef7645" + }, + { + "path": "s4-refresh-review/final-repo/src/coordinator.rs", + "sha256": "dfd568de1618cd4ec82beaa15bf2d72a7930de79293edc52d248dc74782cfe4b", + "bytes": 55322, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/coordinator.rs", + "source_sha256": "dfd568de1618cd4ec82beaa15bf2d72a7930de79293edc52d248dc74782cfe4b" + }, + { + "path": "s4-refresh-review/final-repo/src/profiles.rs", + "sha256": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37", + "bytes": 15250, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/profiles.rs", + "source_sha256": "7594fcb7cf495f8094ecbafcb269975fdb071cf98be6246a09619cf495efce37" + }, + { + "path": "s4-refresh-review/final-repo/src/artifact_store.rs", + "sha256": "8a248d0f2a9e33b6b22b75548441de661fc8413a346fe0bb5512d85b1cbfa598", + "bytes": 43666, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/artifact_store.rs", + "source_sha256": "8a248d0f2a9e33b6b22b75548441de661fc8413a346fe0bb5512d85b1cbfa598" + }, + { + "path": "s4-refresh-review/final-repo/src/daemon_service.rs", + "sha256": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42", + "bytes": 535, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/daemon_service.rs", + "source_sha256": "354ed57fe7870e9150bd91c1fe047c1186ea972a9191ba4ded882d83bdd66e42" + }, + { + "path": "s4-refresh-review/final-repo/src/partition_builder.rs", + "sha256": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1", + "bytes": 8118, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/partition_builder.rs", + "source_sha256": "3a298f586f8f360b351a54f68215d28a93715cf0a499913674e39a520a2f95e1" + }, + { + "path": "s4-refresh-review/final-repo/src/agent_hooks.rs", + "sha256": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19", + "bytes": 56220, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/agent_hooks.rs", + "source_sha256": "83b867b31a69d80c0794587ce58b118bec4862ad2a5251804c6ffd8e07629c19" + }, + { + "path": "s4-refresh-review/final-repo/src/scan.rs", + "sha256": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e", + "bytes": 31755, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/scan.rs", + "source_sha256": "763ac64e721076ea470246d46e08b50255c755af5b1932004082129a291c8f7e" + }, + { + "path": "s4-refresh-review/final-repo/src/mcp_client.rs", + "sha256": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e", + "bytes": 25488, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/mcp_client.rs", + "source_sha256": "31dcde1ae03101e639747fbe6e92dff683a13a946d49af7d14fa0ca6ab79ae3e" + }, + { + "path": "s4-refresh-review/final-repo/src/materialization_worker.rs", + "sha256": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496", + "bytes": 40619, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/materialization_worker.rs", + "source_sha256": "c04f4d2db9bb4913ddccaad5dc82206a745d1df1d01c7581207f8903e78c4496" + }, + { + "path": "s4-refresh-review/final-repo/src/hash.rs", + "sha256": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb", + "bytes": 4331, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/hash.rs", + "source_sha256": "c8eace50b2831cbde1646b4693e625aacc464553064682b889ab62baaaf0e4fb" + }, + { + "path": "s4-refresh-review/final-repo/src/process_lifecycle/unix.rs", + "sha256": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d", + "bytes": 16863, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/process_lifecycle/unix.rs", + "source_sha256": "0b954b83f3e1250579801243bccac31a2de2300a690f8e7c4fc6d3a3046c7b1d" + }, + { + "path": "s4-refresh-review/final-repo/src/process_lifecycle/mod.rs", + "sha256": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663", + "bytes": 28216, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/process_lifecycle/mod.rs", + "source_sha256": "67c10a29c3c433a8030b2cbda23c30496a73d0a635a6af697bf624bcb7bbe663" + }, + { + "path": "s4-refresh-review/final-repo/src/process_lifecycle/windows.rs", + "sha256": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8", + "bytes": 24069, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/process_lifecycle/windows.rs", + "source_sha256": "8dc6b6c8c873b191bad9cb41b1e6b66a88fe05b2f6c7d898bbca9d36d8af75a8" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/access.rs", + "sha256": "104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d", + "bytes": 11983, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/access.rs", + "source_sha256": "104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/request.rs", + "sha256": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed", + "bytes": 440, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/request.rs", + "source_sha256": "16a1c998b312b5f554148458acda5748d393926ec29ef3232e3f7d80016fdfed" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/write.rs", + "sha256": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d", + "bytes": 14730, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/write.rs", + "source_sha256": "d916186b25d4c0b074b476eaba3225f535987f8590c0019f6453d509e77afb8d" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/phase.rs", + "sha256": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce", + "bytes": 28652, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/phase.rs", + "source_sha256": "24b773599b4757a51ce8a371bed2be00a179ce94ad84f1671d63f12f667582ce" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/rss.rs", + "sha256": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c", + "bytes": 6503, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/rss.rs", + "source_sha256": "be73c087697de048a95fe1935de0b1fc2ee4a3dd6634db02f72f7f627885c41c" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/mod.rs", + "sha256": "519f8c9c5f0482d0ca4d9cea6430fe831ea753b3a13b7ba5af06499f91a0f81e", + "bytes": 719, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/mod.rs", + "source_sha256": "519f8c9c5f0482d0ca4d9cea6430fe831ea753b3a13b7ba5af06499f91a0f81e" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/schema.rs", + "sha256": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e", + "bytes": 3541, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/schema.rs", + "source_sha256": "64636ac494699d00df83233df66c44882c1fc5c94ad5eb3a6c16075cf04c002e" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/tests.rs", + "sha256": "fb00bc867d9aaeeb903c62bae36c6d08bf20726c61efd41b726c053e0f3ff1f2", + "bytes": 20508, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/tests.rs", + "source_sha256": "fb00bc867d9aaeeb903c62bae36c6d08bf20726c61efd41b726c053e0f3ff1f2" + }, + { + "path": "s4-refresh-review/final-repo/src/db_writer/extensions.rs", + "sha256": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0", + "bytes": 6818, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/db_writer/extensions.rs", + "source_sha256": "6d8265bd65c78309f56ed560676cfb5286a827ba6cd00783c15d6be0abc4cda0" + }, + { + "path": "s4-refresh-review/final-repo/src/coordinator/tests.rs", + "sha256": "d22cc457271d4b01005ae232f1a4b9ea9fcdcf05509554323d676920c97db2d6", + "bytes": 47695, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/coordinator/tests.rs", + "source_sha256": "d22cc457271d4b01005ae232f1a4b9ea9fcdcf05509554323d676920c97db2d6" + }, + { + "path": "s4-refresh-review/final-repo/src/bin/codebase-graph.rs", + "sha256": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305", + "bytes": 191, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/bin/codebase-graph.rs", + "source_sha256": "88109d11795b0e7ad653237a2c2e4f29ff4dcf7a6038e046da88423df9c10305" + }, + { + "path": "s4-refresh-review/final-repo/src/search_index/io.rs", + "sha256": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084", + "bytes": 6147, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/search_index/io.rs", + "source_sha256": "77dc0e2950d30963fe669d687c9a58af73d2e033d34fccffff8cd32cebea7084" + }, + { + "path": "s4-refresh-review/final-repo/src/search_index/build.rs", + "sha256": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525", + "bytes": 27575, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/search_index/build.rs", + "source_sha256": "6f83311d7700244ccaa11b7481d7ad160624c61d10e43c4685388b13d434e525" + }, + { + "path": "s4-refresh-review/final-repo/src/search_index/mod.rs", + "sha256": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147", + "bytes": 671, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/search_index/mod.rs", + "source_sha256": "a614572c54f2beed8aab80ee93660919ca5376f43324ac1ffdb9488701a25147" + }, + { + "path": "s4-refresh-review/final-repo/src/search_index/read.rs", + "sha256": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472", + "bytes": 15942, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/search_index/read.rs", + "source_sha256": "29a950fc11f41954ccb58b7a5668df12a014c5d2d056e6eaecc0c43717b41472" + }, + { + "path": "s4-refresh-review/final-repo/src/search_index/tests.rs", + "sha256": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b", + "bytes": 5247, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/search_index/tests.rs", + "source_sha256": "b49dbd37d41fe17fe67c9b582c76cc3c082f741705ea3d461527a937e226053b" + }, + { + "path": "s4-refresh-review/final-repo/src/search_index/model.rs", + "sha256": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f", + "bytes": 3956, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/search_index/model.rs", + "source_sha256": "4b3516ad7dbc128abfb0b9bd93ef18119a7caf8f6f3253fe6affe72c2681ff4f" + }, + { + "path": "s4-refresh-review/final-repo/src/parser/wat.rs", + "sha256": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99", + "bytes": 448, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/parser/wat.rs", + "source_sha256": "ccd06037725d1be1be19431be6fd7d193551f9836b88e4628e17f688cfdbfe99" + }, + { + "path": "s4-refresh-review/final-repo/src/parser/tree_sitter.rs", + "sha256": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa", + "bytes": 5287, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/parser/tree_sitter.rs", + "source_sha256": "b644d631d0de277a91bbeefc6df71e3429c5e2da9f853f27c8242ed0e52aabfa" + }, + { + "path": "s4-refresh-review/final-repo/src/parser/fields.rs", + "sha256": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0", + "bytes": 17538, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/parser/fields.rs", + "source_sha256": "13db15d6ff0c91885ac47332008cf48b0b1621f625e4eacb95d69152a1dcf2f0" + }, + { + "path": "s4-refresh-review/final-repo/src/parser/mod.rs", + "sha256": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578", + "bytes": 14450, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/parser/mod.rs", + "source_sha256": "255b4551bd0f95f571de99ff7fff4bd3a61c0a03635285f5f303b7c7adc4f578" + }, + { + "path": "s4-refresh-review/final-repo/src/parser/markdown.rs", + "sha256": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047", + "bytes": 4939, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/parser/markdown.rs", + "source_sha256": "ae21b299c40cdd223e4d67e647cb5ad1ff75453ba531d76b4534b24a83b72047" + }, + { + "path": "s4-refresh-review/final-repo/src/parser/captures.rs", + "sha256": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6", + "bytes": 831, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/parser/captures.rs", + "source_sha256": "3fcd1f1aad682c733abdd497736f5366b8f2b04ad072cf1a06369e21c47ac0d6" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/relations.rs", + "sha256": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04", + "bytes": 4200, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/relations.rs", + "source_sha256": "9887ea3be912053c62d3034cd7b40459e292d9bfcecdee6c2f1ca8bdc74cac04" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/mod.rs", + "sha256": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112", + "bytes": 623, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/mod.rs", + "source_sha256": "9cada348473a9ac37a13af5915d79f5dd4486fa9c0d62cedfd5b2c6a96508112" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/arena.rs", + "sha256": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b", + "bytes": 2636, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/arena.rs", + "source_sha256": "1986e2b48d5a69d127b7a0f2a92464414790eee36870b3d8873daaf9e819e36b" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/ids.rs", + "sha256": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5", + "bytes": 5141, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/ids.rs", + "source_sha256": "509c260349abefb5ee90cafb5c25844c2f3f18038194635e90648e2454aaf8c5" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/rows.rs", + "sha256": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b", + "bytes": 3459, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/rows.rs", + "source_sha256": "db117c755af4a8b85e50a6ab0042f8265cebc5e14cb480388b39cc57183d559b" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/tests.rs", + "sha256": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d", + "bytes": 7790, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/tests.rs", + "source_sha256": "9c177764e8ffa131578baa140befdf33ae7e6ce5056ef63144421f1be9f89e4d" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/capture.rs", + "sha256": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9", + "bytes": 19260, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/capture.rs", + "source_sha256": "a7f542f23319106ee4eda0632e5e2196b4b62323effe8ac882b78b88309865c9" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/builder/edges.rs", + "sha256": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde", + "bytes": 4426, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/builder/edges.rs", + "source_sha256": "9d7563f152bc5bf747f8f973ccb47c2d2dd84152d7cc18bc24f1fcb4bac54dde" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/builder/traversal.rs", + "sha256": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb", + "bytes": 8856, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/builder/traversal.rs", + "source_sha256": "d5960bbabb5138bdd28ea9f27b39919e7d236b1a44738ec1ad165ebd1b1799cb" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/builder/semantic.rs", + "sha256": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a", + "bytes": 18494, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/builder/semantic.rs", + "source_sha256": "09e80f1e30e8daaec901d9de9dc9bc9b69df5d74274ecaa83351025ff968de7a" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/builder/mod.rs", + "sha256": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952", + "bytes": 2096, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/builder/mod.rs", + "source_sha256": "57496a28a2237bc18eed8c630f3eea99825f9dec616597a86d16dffd9a0c5952" + }, + { + "path": "s4-refresh-review/final-repo/src/syntax_materializer/builder/nodes.rs", + "sha256": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad", + "bytes": 9844, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/syntax_materializer/builder/nodes.rs", + "source_sha256": "b163d96e735a82da0d472041b5b21f8ee3505bad194b1bedab91fe8ccadf47ad" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/locks.rs", + "sha256": "6ff59bbfe40f711b6865d11cd7c9f0eb989ca6fcb74482ddb3a0b13560367ce4", + "bytes": 41947, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/locks.rs", + "source_sha256": "6ff59bbfe40f711b6865d11cd7c9f0eb989ca6fcb74482ddb3a0b13560367ce4" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/layout.rs", + "sha256": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987", + "bytes": 11865, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/layout.rs", + "source_sha256": "30ca57a967b8f3ff5e72e0af6e4edc63e5ad6bb191dc80e3894bb64966038987" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/maintenance.rs", + "sha256": "c5d94f0ac68d9ed81f8dfb060928887f1c2641798236ba60ecb15f9cc9c970a7", + "bytes": 7896, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/maintenance.rs", + "source_sha256": "c5d94f0ac68d9ed81f8dfb060928887f1c2641798236ba60ecb15f9cc9c970a7" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/maintenance_validation.rs", + "sha256": "0318ec6ce269e8570c46b858d30c0a8203ccfcebb88b91887fabc04dcd7072f5", + "bytes": 62018, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/maintenance_validation.rs", + "source_sha256": "0318ec6ce269e8570c46b858d30c0a8203ccfcebb88b91887fabc04dcd7072f5" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/maintenance_journal.rs", + "sha256": "f8e1d644990c827a6f64326348a7f5ca91d0f3e570929052acff14c245bcf41d", + "bytes": 22319, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/maintenance_journal.rs", + "source_sha256": "f8e1d644990c827a6f64326348a7f5ca91d0f3e570929052acff14c245bcf41d" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/test_harness.rs", + "sha256": "e096021d45df2dabd18fad52c11c1a9491f5128b645b69fc251428b2fd88235b", + "bytes": 67836, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/test_harness.rs", + "source_sha256": "e096021d45df2dabd18fad52c11c1a9491f5128b645b69fc251428b2fd88235b" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/maintenance_ownership.rs", + "sha256": "554715ad276f4834727eee438de0f1a220c9a89440914ccbe78f252e2a7653ee", + "bytes": 179048, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/maintenance_ownership.rs", + "source_sha256": "554715ad276f4834727eee438de0f1a220c9a89440914ccbe78f252e2a7653ee" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/run_workspace.rs", + "sha256": "120d4c61691a97aa4c9bb4c29c2487a3a2b53e45a63ce5288e1f5a0e587eb877", + "bytes": 20931, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/run_workspace.rs", + "source_sha256": "120d4c61691a97aa4c9bb4c29c2487a3a2b53e45a63ce5288e1f5a0e587eb877" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/containment.rs", + "sha256": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077", + "bytes": 15920, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/containment.rs", + "source_sha256": "b625c68cdf7beeb98fc0a306c0719498eef968caa9fe620faa9971ed8e56e077" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/mod.rs", + "sha256": "85270643f6e5d17359bd438431efd56cfcdd529fb06a09d0c75a10a2773ed354", + "bytes": 446, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/mod.rs", + "source_sha256": "85270643f6e5d17359bd438431efd56cfcdd529fb06a09d0c75a10a2773ed354" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/inventory.rs", + "sha256": "11e12ad82ccb3c031cb3361bf743f947ed40d7e338542762cfca25c0edb6fe91", + "bytes": 72461, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/inventory.rs", + "source_sha256": "11e12ad82ccb3c031cb3361bf743f947ed40d7e338542762cfca25c0edb6fe91" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/direct.rs", + "sha256": "c40e99468c2ab0a465b7a5b5ee2a8e755c872d450da5e88cd212d91365691e25", + "bytes": 22025, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/direct.rs", + "source_sha256": "c40e99468c2ab0a465b7a5b5ee2a8e755c872d450da5e88cd212d91365691e25" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/maintenance_scheduler.rs", + "sha256": "3795c36621df8b907db3b96cb84c0334c599dcfd00b8c6dc78556358ffd0bf97", + "bytes": 47372, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/maintenance_scheduler.rs", + "source_sha256": "3795c36621df8b907db3b96cb84c0334c599dcfd00b8c6dc78556358ffd0bf97" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/atomic.rs", + "sha256": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f", + "bytes": 3744, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/atomic.rs", + "source_sha256": "ebacf3af507cf0448e8747065d3fdb8e64fc136a598ff7f972c5b65aeba4409f" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/maintenance_json.rs", + "sha256": "e0815a85108e884ff4983b24e752532eb8f18a82ed463f083c61ffc3f1c3190b", + "bytes": 19810, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/maintenance_json.rs", + "source_sha256": "e0815a85108e884ff4983b24e752532eb8f18a82ed463f083c61ffc3f1c3190b" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/maintenance_budget.rs", + "sha256": "75ef50f1592e8fac0ed7c77f722d327fc2d0a861e1e8e4e585b56be9d5149f3e", + "bytes": 12324, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/maintenance_budget.rs", + "source_sha256": "75ef50f1592e8fac0ed7c77f722d327fc2d0a861e1e8e4e585b56be9d5149f3e" + }, + { + "path": "s4-refresh-review/final-repo/src/storage/managed.rs", + "sha256": "aee08a9c6f09640bde3d2d0a3b50690391e294d308561544e94079c9abe2041d", + "bytes": 46332, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/storage/managed.rs", + "source_sha256": "aee08a9c6f09640bde3d2d0a3b50690391e294d308561544e94079c9abe2041d" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mod.rs", + "sha256": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8", + "bytes": 282, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mod.rs", + "source_sha256": "7ac875addaf5c578802e143a7de66420494288222bc430afe19196a611f235d8" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/tools.rs", + "sha256": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b", + "bytes": 9886, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/tools.rs", + "source_sha256": "b70b1c7ecc2933852787d7442cbcfc560d244641a8002afa0f2283de9ee29d6b" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/protocol.rs", + "sha256": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799", + "bytes": 4341, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/protocol.rs", + "source_sha256": "e46b17bc3295f5c1dafcf5c5237cf7f6a5ef4aed5e2000139f978eb7ca43e799" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/options.rs", + "sha256": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805", + "bytes": 13500, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/options.rs", + "source_sha256": "bdb46e6d0be13013d6b7fe359209ed88e8ebfa43d9999925c1b96bc045207805" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/dispatcher.rs", + "sha256": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5", + "bytes": 32506, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/dispatcher.rs", + "source_sha256": "ee9b242632c374921435978f81407c190158d58a5d462fe7c6496e2a358471c5" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/mod.rs", + "sha256": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e", + "bytes": 740, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/mod.rs", + "source_sha256": "c9ce96984e78874aa8a08784cbd6b85cba7dc83c97f0f81c9e1ea356f46ede1e" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/daemon.rs", + "sha256": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359", + "bytes": 76669, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/daemon.rs", + "source_sha256": "67206e7eefac851d292d4f8a48eb0492b004f3df23886820bee17b4ce2f0c359" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/state.rs", + "sha256": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6", + "bytes": 4460, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/state.rs", + "source_sha256": "14aca0cb1fd70a5cf05482583c4d1160da242a0176b00aacb915f8625967fca6" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/block.rs", + "sha256": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f", + "bytes": 884, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/block.rs", + "source_sha256": "f353cba3a29726be0843f188eadebe1f9c56caabd1700ebe0507882b524de23f" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/stdio.rs", + "sha256": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38", + "bytes": 3615, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/stdio.rs", + "source_sha256": "5b305dce4d89bb50c07699605b9114777933943694e9a2eb9cab5a2dabe55b38" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/refresh.rs", + "sha256": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181", + "bytes": 1106, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/refresh.rs", + "source_sha256": "9e2ceb191231e7bd72d1e65619cc5614fd9106eae7975b2b659bd9cf5cdec181" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/mcp/http.rs", + "sha256": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b", + "bytes": 22310, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/mcp/http.rs", + "source_sha256": "12d26405a1d678e4c0b2432eb9b3ea28f0d834d6ba7fc45b91c363ac2ab8657b" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/dispatch.rs", + "sha256": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045", + "bytes": 11665, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/dispatch.rs", + "source_sha256": "2c0d983710e214cbaa7513c6c3815fc61911ba9e0f1b2fadd50e7158cd599045" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/util.rs", + "sha256": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c", + "bytes": 254, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/util.rs", + "source_sha256": "89ba0e9f636551828a969fdc262e0ba89e08f9789755bbc196f0b892e72e429c" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/mcp_command.rs", + "sha256": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239", + "bytes": 2079, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/mcp_command.rs", + "source_sha256": "b74ba5bed6db496ddc9028cb5cdb23396e080300bf99a2bc851cba77722b7239" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/materialization_input.rs", + "sha256": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149", + "bytes": 11295, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/materialization_input.rs", + "source_sha256": "727cd8a5772d2c8f72c246093f669709035ddfb7e556e119b8dc859a87003149" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/reinstall.rs", + "sha256": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff", + "bytes": 1711, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/reinstall.rs", + "source_sha256": "1fa6fe07302384e8187ec466f8b605051e11c7a679643f1147b5268da8f9b2ff" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/mod.rs", + "sha256": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43", + "bytes": 339, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/mod.rs", + "source_sha256": "c574c9f1e30d211fee7ad12d8675c5f52bc87dc2a1d1b2985fe5f030f9f5bd43" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/uninstall.rs", + "sha256": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03", + "bytes": 4508, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/uninstall.rs", + "source_sha256": "981bbdb419a89973a57d3e92c2cd7c6b569eaff9b904bf10bc8e60efb6763c03" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/agent_hooks.rs", + "sha256": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5", + "bytes": 8004, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/agent_hooks.rs", + "source_sha256": "734420d08c51c98ca2a9439bc866bccec8477cbf734ff6e3a3eea375e501dbd5" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/setup.rs", + "sha256": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7", + "bytes": 1619, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/setup.rs", + "source_sha256": "42e00faa6605ffede89da215f71b921ad1974c46b5ec170ad65ace1ff7b56ef7" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/install/command.rs", + "sha256": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94", + "bytes": 1617, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/install/command.rs", + "source_sha256": "1adbd5cbf46ee646d09de8ce2cb41da4f05495c27980cf9a5ae1c5ee209c4f94" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/install/options.rs", + "sha256": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0", + "bytes": 5394, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/install/options.rs", + "source_sha256": "a89014b1ff165f80e2dfa947132fc7a9884d50c0c22ceca4e3f12df9247bf4e0" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/install/mod.rs", + "sha256": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43", + "bytes": 229, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/install/mod.rs", + "source_sha256": "9df3b6b37f535b46a4dbc7c8997cd9d3a123962cc8ad12acb3f5f4daeba4ee43" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/install/verify.rs", + "sha256": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b", + "bytes": 12321, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/install/verify.rs", + "source_sha256": "6666efd44a7462135900ccb89d917b467f48f23e4ac334271f1647b038c5963b" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/graph/options.rs", + "sha256": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212", + "bytes": 21407, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/graph/options.rs", + "source_sha256": "29a70791cb1446b6adaea9ffaa7b5ac631e254eb6ec9bbfd44687a2692a27212" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/graph/mod.rs", + "sha256": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325", + "bytes": 221, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/graph/mod.rs", + "source_sha256": "5120e540053c40aeafd185d3061f2381df94ca81d6ff2386008302d9dbb63325" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/tests/dispatch_materialize.rs", + "sha256": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa", + "bytes": 21272, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/tests/dispatch_materialize.rs", + "source_sha256": "1a46852bd5246483664b5b14e2aeafa490748a566e002b1759e2809191c8f8aa" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/tests/graph.rs", + "sha256": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7", + "bytes": 26400, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/tests/graph.rs", + "source_sha256": "8bc1f1d5e5767a8a2aaad9080c58cda203fc8ed90ed2bbb5310816288566b7e7" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/tests/mod.rs", + "sha256": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d", + "bytes": 421, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/tests/mod.rs", + "source_sha256": "4af78fca9d33196268b95f121bafe908771853e10c29bdac0e1dc3865bf2770d" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/tests/fixtures.rs", + "sha256": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88", + "bytes": 5875, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/tests/fixtures.rs", + "source_sha256": "c09161ae31b612624af451cc248e66d67699ce9e5df4c9c6534f4ae786ea0d88" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/tests/watch.rs", + "sha256": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0", + "bytes": 31181, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/tests/watch.rs", + "source_sha256": "19e24cbf9bf8cd8f58d8eaae1b46c61ed169743536858145cf9d3d68585a89b0" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/tests/install.rs", + "sha256": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d", + "bytes": 31242, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/tests/install.rs", + "source_sha256": "c7a55e7cce8bf8792f24c001be415f64f94c8fb99df79761905ae5513bf1723d" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/tests/mcp.rs", + "sha256": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32", + "bytes": 20317, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/tests/mcp.rs", + "source_sha256": "e2206c19a0df5776396faec8e1cb8d7e7691a3fe0c45d43b1de1520120d10c32" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/materialization/command.rs", + "sha256": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5", + "bytes": 2196, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/materialization/command.rs", + "source_sha256": "d61558d4b8a33151a12d143ac41a8943f57fef31b16c439b43b1911816b6f6a5" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/materialization/mod.rs", + "sha256": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03", + "bytes": 85, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/materialization/mod.rs", + "source_sha256": "3dcc8f7e40a2a48aef8428482eb450076abba32b1e05e54fe34c2824522c1b03" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/format/mod.rs", + "sha256": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f", + "bytes": 409, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/format/mod.rs", + "source_sha256": "93cce74f87ede48515929a7bdd98bbcdca1538de2f43d1a974a616de5f08761f" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/format/help.rs", + "sha256": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e", + "bytes": 18135, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/format/help.rs", + "source_sha256": "1f337bb158dd0a577262e6e69b28113478d3d8b0d25524681a1087ebdb9d890e" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/watch/command.rs", + "sha256": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56", + "bytes": 3328, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/watch/command.rs", + "source_sha256": "d239a949f9a39a85ba37addfbf8ec036d6f9764cb402c5666f41967dbe37ae56" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/watch/options.rs", + "sha256": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8", + "bytes": 9946, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/watch/options.rs", + "source_sha256": "ca40d91b6f36edf0cf0f550ddecef629262dd2600412ddf94ef7c497a835f4b8" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/watch/mod.rs", + "sha256": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183", + "bytes": 445, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/watch/mod.rs", + "source_sha256": "57e8b098f971e6287f487e6f144baa1881c250c73e12bcc9e0431c3777243183" + }, + { + "path": "s4-refresh-review/final-repo/src/adapters/cli/watch/output.rs", + "sha256": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379", + "bytes": 1228, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/adapters/cli/watch/output.rs", + "source_sha256": "112c20c3db88bcaed49dd39a31f937bc378e3aa875d75b05b5a304ac2f6ba379" + }, + { + "path": "s4-refresh-review/final-repo/src/execution/run.rs", + "sha256": "f2b7ef995dd0abdb4a927a3b2a20b9ab39d9f74c238711d1ab7d806c3ad4ed44", + "bytes": 32999, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/execution/run.rs", + "source_sha256": "f2b7ef995dd0abdb4a927a3b2a20b9ab39d9f74c238711d1ab7d806c3ad4ed44" + }, + { + "path": "s4-refresh-review/final-repo/src/execution/plan.rs", + "sha256": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a", + "bytes": 1445, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/execution/plan.rs", + "source_sha256": "f6319948d6b41c4de24a21e88f4df6f826151cfd464e42bd244b7cce0f43430a" + }, + { + "path": "s4-refresh-review/final-repo/src/execution/mod.rs", + "sha256": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724", + "bytes": 129, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/execution/mod.rs", + "source_sha256": "408f98c957a25205a58a1eb20b94b2ae16d538179b9be31d7bde3994b4050724" + }, + { + "path": "s4-refresh-review/final-repo/src/execution/parallel.rs", + "sha256": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0", + "bytes": 25086, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/execution/parallel.rs", + "source_sha256": "eac8a03a73869f8496132953c9e9e3d28e7bf0921d3bd8b88a5b923cbf6434e0" + }, + { + "path": "s4-refresh-review/final-repo/src/execution/timing.rs", + "sha256": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b", + "bytes": 120, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/execution/timing.rs", + "source_sha256": "4525f8043b01d32a3f2b3616bd1830a3038908661355f9b3b0e909237f9a5e9b" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/spill.rs", + "sha256": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff", + "bytes": 30495, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/spill.rs", + "source_sha256": "f1795711c2add02b34ec680a933a9fdcd09a912cdbff366f6798c376d33516ff" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/merge.rs", + "sha256": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1", + "bytes": 3353, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/merge.rs", + "source_sha256": "00d7c54be7376cd77c8ab9656f6fdbf27760cc1d5c839b565ea7b04b714fb4f1" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/files.rs", + "sha256": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871", + "bytes": 1498, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/files.rs", + "source_sha256": "d5a6e4c9f328ca49bc370b03e06ca275141cbc342606eda61605a11aeba69871" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/mod.rs", + "sha256": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909", + "bytes": 237, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/mod.rs", + "source_sha256": "d54bfd219e84640919ccc8ba45721f3b54b0a5dec848213bb1cb581bbbf77909" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/rows.rs", + "sha256": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b", + "bytes": 2997, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/rows.rs", + "source_sha256": "5e5afff195f6707c675a96ac1925d609e22a554156b592b3fa4c5e4e4766cc8b" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/tests.rs", + "sha256": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98", + "bytes": 18023, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/tests.rs", + "source_sha256": "d6aeefe8ef5e4c440d2e90bb7f2507f98547eadb55f58c9d8b5ce5ff8f11cf98" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/writer.rs", + "sha256": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0", + "bytes": 1474, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/writer.rs", + "source_sha256": "10d0980acecb45a9965d7fe2ee03ad123088eed25ec732d3e78662e7659990f0" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/accumulator.rs", + "sha256": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da", + "bytes": 38005, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/accumulator.rs", + "source_sha256": "01f60a106e9cbe8c106a1550f86ed430e084eae76ae843fc2495d56a888164da" + }, + { + "path": "s4-refresh-review/final-repo/src/staging_writer/result.rs", + "sha256": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79", + "bytes": 398, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/staging_writer/result.rs", + "source_sha256": "d5e0ba6946e533e040210f69b6e4fc102d3618e5c2d73e285cc94ddc7bd06b79" + }, + { + "path": "s4-refresh-review/final-repo/src/api/core.rs", + "sha256": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462", + "bytes": 58044, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/core.rs", + "source_sha256": "b60db09be603ec16ab1ee03354c3c1e41607784032cf986fedbb46ee0ceeb462" + }, + { + "path": "s4-refresh-review/final-repo/src/api/catalog_support.rs", + "sha256": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4", + "bytes": 11789, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/catalog_support.rs", + "source_sha256": "221bb3bfc461a15ebeb6a31b518964edbb179b3969f2000f7ef065ac52011ca4" + }, + { + "path": "s4-refresh-review/final-repo/src/api/facade.rs", + "sha256": "4866f0d5b48539c2cac4f42fdd3a343da034c3c06e11289774771217a1df9cc6", + "bytes": 5503, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/facade.rs", + "source_sha256": "4866f0d5b48539c2cac4f42fdd3a343da034c3c06e11289774771217a1df9cc6" + }, + { + "path": "s4-refresh-review/final-repo/src/api/presentation_support.rs", + "sha256": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7", + "bytes": 26198, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/presentation_support.rs", + "source_sha256": "3e088993064b6eb7a5c0d4e6d6aab9e319cd6ebe74225e60495c89fea42d31c7" + }, + { + "path": "s4-refresh-review/final-repo/src/api/catalog.rs", + "sha256": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01", + "bytes": 11645, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/catalog.rs", + "source_sha256": "e8ae3e4c3071a1ba247a6db5f58350110a018fde88f40d608c670b1659efef01" + }, + { + "path": "s4-refresh-review/final-repo/src/api/graph_read.rs", + "sha256": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b", + "bytes": 38231, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/graph_read.rs", + "source_sha256": "759863fd6b8d19b4701fa5357f48404251d9fb3b603a62d3bc0be8b9333c778b" + }, + { + "path": "s4-refresh-review/final-repo/src/api/presenter.rs", + "sha256": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52", + "bytes": 6891, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/presenter.rs", + "source_sha256": "e15068cb63fd78b7a79b958efe140eb8a4bcfaf0a08dd9baf09d4fe51d726e52" + }, + { + "path": "s4-refresh-review/final-repo/src/api/mod.rs", + "sha256": "51501058ec785635a5149eaaa3522a4e7b74ba037950af4c6495d8e704f4df19", + "bytes": 2236, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/mod.rs", + "source_sha256": "51501058ec785635a5149eaaa3522a4e7b74ba037950af4c6495d8e704f4df19" + }, + { + "path": "s4-refresh-review/final-repo/src/api/normalization.rs", + "sha256": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d", + "bytes": 30069, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/normalization.rs", + "source_sha256": "7c2c3968fdd48ca6a8335f132a2e7044160c094a6da8267af27b243f92551f6d" + }, + { + "path": "s4-refresh-review/final-repo/src/api/refresh.rs", + "sha256": "0a7fc8aefa8b512d52d93aa565dbc78029c934c4aedc2c136714db422abf882a", + "bytes": 140749, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/refresh.rs", + "source_sha256": "0a7fc8aefa8b512d52d93aa565dbc78029c934c4aedc2c136714db422abf882a" + }, + { + "path": "s4-refresh-review/final-repo/src/api/materialization.rs", + "sha256": "446e276c15cab8f5a35ad48e2df77ba52475812c904ef04caa545ef216622651", + "bytes": 74482, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/materialization.rs", + "source_sha256": "446e276c15cab8f5a35ad48e2df77ba52475812c904ef04caa545ef216622651" + }, + { + "path": "s4-refresh-review/final-repo/src/api/lifecycle.rs", + "sha256": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd", + "bytes": 221223, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/lifecycle.rs", + "source_sha256": "6704a451b019e87af9742151cc8feaf627dc08a0c05664ff0f6ba783d7fd35cd" + }, + { + "path": "s4-refresh-review/final-repo/src/api/execution_context.rs", + "sha256": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2", + "bytes": 1753, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/execution_context.rs", + "source_sha256": "1d0ae6f7f54a28ecc84735045825a870673a0d1d257f867711613bcb584332d2" + }, + { + "path": "s4-refresh-review/final-repo/src/api/contracts.rs", + "sha256": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223", + "bytes": 20871, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/contracts.rs", + "source_sha256": "f1a6ae3c42040f4f691feec254cd076c2f701c2a906789e6de88b49762080223" + }, + { + "path": "s4-refresh-review/final-repo/src/api/boundary_tests.rs", + "sha256": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642", + "bytes": 9540, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/boundary_tests.rs", + "source_sha256": "e6756a457ce5f5ce02e700ba7af9207709d68b1eb4e458ebc435b2929ba2d642" + }, + { + "path": "s4-refresh-review/final-repo/src/api/context.rs", + "sha256": "b038192e4cc8258c94c7c524f9ed565017e45d7e28092b09e5490d4836db0eed", + "bytes": 62558, + "source": "/private/tmp/cleanup-s4-refresh-review-b9owzhuh/final-repo/src/api/context.rs", + "source_sha256": "b038192e4cc8258c94c7c524f9ed565017e45d7e28092b09e5490d4836db0eed" + }, + { + "path": "s4-refresh-history/v2/tracked-inputs-after.json", + "sha256": "12aaa9152fbdc395221423cb07e573dc56d23a0da88d29c89969ba557c2b3df2", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/tracked-inputs-after.json", + "source_sha256": "12aaa9152fbdc395221423cb07e573dc56d23a0da88d29c89969ba557c2b3df2" + }, + { + "path": "s4-refresh-history/v2/baseline.txt", + "sha256": "43e0520c6d1fe72112184e30d9b1be8d4e8bc567c6ac60fb8e282307b10974a8", + "bytes": 41, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/baseline.txt", + "source_sha256": "43e0520c6d1fe72112184e30d9b1be8d4e8bc567c6ac60fb8e282307b10974a8" + }, + { + "path": "s4-refresh-history/v2/owned-source-manifest.json", + "sha256": "012501895fccf0130129156d24f1bf87b878c382aa62d3d71c864abe8915b63c", + "bytes": 504, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/owned-source-manifest.json", + "source_sha256": "012501895fccf0130129156d24f1bf87b878c382aa62d3d71c864abe8915b63c" + }, + { + "path": "s4-refresh-history/v2/evidence-failed.json", + "sha256": "aca2982ceb7f995d5a20f8138b1f99458be99b407d8ab0cdb880f124beeafcd6", + "bytes": 443, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/evidence-failed.json", + "source_sha256": "aca2982ceb7f995d5a20f8138b1f99458be99b407d8ab0cdb880f124beeafcd6" + }, + { + "path": "s4-refresh-history/v2/tracked-inputs-before.json", + "sha256": "12aaa9152fbdc395221423cb07e573dc56d23a0da88d29c89969ba557c2b3df2", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/tracked-inputs-before.json", + "source_sha256": "12aaa9152fbdc395221423cb07e573dc56d23a0da88d29c89969ba557c2b3df2" + }, + { + "path": "s4-refresh-history/v2/compile-test.log.gz", + "sha256": "b74300b4c6be1dc01861bab7357ec6bb71c8b0829937c9e75562ca42ffd06775", + "bytes": 2775, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/compile-test.log", + "source_sha256": "889443aeb919a7d0b7bdd532d72d024d700ace56e44971a70e7e0e311108a2a0" + }, + { + "path": "s4-refresh-history/v2/owned.diff.gz", + "sha256": "848cd4761b8824f9c49052944135927f8a0f51957c0e6134c735d6719f9b032c", + "bytes": 11895, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/owned.diff", + "source_sha256": "3bd878433b4ce9939b5afb976b20587a2c506e7f4b71d425827fe8fdbc3f0456" + }, + { + "path": "s4-refresh-history/v2/rustfmt-check.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/rustfmt-check.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-history/v2/fresh-root.txt", + "sha256": "ce4dbf35560e7881e2b55680c78ef5429b619bd77c2c144a90fb9b1978ebd897", + "bytes": 44, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v2/fresh-root.txt", + "source_sha256": "ce4dbf35560e7881e2b55680c78ef5429b619bd77c2c144a90fb9b1978ebd897" + }, + { + "path": "s4-refresh-history/v3/tracked-inputs-after.json", + "sha256": "fad53bef7f7ead785cdceba8870dc5c3991c5f1c1179291d48b399b46f78bb3d", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/tracked-inputs-after.json", + "source_sha256": "fad53bef7f7ead785cdceba8870dc5c3991c5f1c1179291d48b399b46f78bb3d" + }, + { + "path": "s4-refresh-history/v3/baseline.txt", + "sha256": "43e0520c6d1fe72112184e30d9b1be8d4e8bc567c6ac60fb8e282307b10974a8", + "bytes": 41, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/baseline.txt", + "source_sha256": "43e0520c6d1fe72112184e30d9b1be8d4e8bc567c6ac60fb8e282307b10974a8" + }, + { + "path": "s4-refresh-history/v3/owned-source-manifest.json", + "sha256": "6cc4ae27e64d3ccce6be008fd45aa394dfd787f03f452b191bde00b5df6959d7", + "bytes": 624, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/owned-source-manifest.json", + "source_sha256": "6cc4ae27e64d3ccce6be008fd45aa394dfd787f03f452b191bde00b5df6959d7" + }, + { + "path": "s4-refresh-history/v3/evidence-failed.json", + "sha256": "635a6ed7f284d00a56b3afc397e83ba3b8303a9c2ac94d09fed8d1e497f18604", + "bytes": 552, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/evidence-failed.json", + "source_sha256": "635a6ed7f284d00a56b3afc397e83ba3b8303a9c2ac94d09fed8d1e497f18604" + }, + { + "path": "s4-refresh-history/v3/tracked-inputs-before.json", + "sha256": "fad53bef7f7ead785cdceba8870dc5c3991c5f1c1179291d48b399b46f78bb3d", + "bytes": 72196, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/tracked-inputs-before.json", + "source_sha256": "fad53bef7f7ead785cdceba8870dc5c3991c5f1c1179291d48b399b46f78bb3d" + }, + { + "path": "s4-refresh-history/v3/compile-test.log.gz", + "sha256": "47c35230dd08ba9622b0d49cf03d397fbcceef0b1c3f539fa8382e840dc8c580", + "bytes": 6290, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/compile-test.log", + "source_sha256": "588a491475ea62ed221e83b65f8ad84ee897c59b3b2c4f57c5e5a2fdfa779d42" + }, + { + "path": "s4-refresh-history/v3/owned.diff.gz", + "sha256": "717d557e5d1d65fec013e89445e52243bc2ee86e0b52c056fe798109147f139d", + "bytes": 12093, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/owned.diff", + "source_sha256": "729f8c2ef8b8b001b3c2f2ea652e2b12056d2ae9e8123b5ea6c6a786fcf92dfe" + }, + { + "path": "s4-refresh-history/v3/test-inventory.txt", + "sha256": "d3dc735c1f8b99a0e084d424bce0195f67612ba77bba6fa1c5fc47e2dbdb5458", + "bytes": 56873, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/test-inventory.txt", + "source_sha256": "d3dc735c1f8b99a0e084d424bce0195f67612ba77bba6fa1c5fc47e2dbdb5458" + }, + { + "path": "s4-refresh-history/v3/rustfmt-check.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/rustfmt-check.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "s4-refresh-history/v3/binary-manifest.json", + "sha256": "562bf60e4b21f84f2e99ccb6542df40a6d6f169331aaada63ab8acdd2c2ea650", + "bytes": 276, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/binary-manifest.json", + "source_sha256": "562bf60e4b21f84f2e99ccb6542df40a6d6f169331aaada63ab8acdd2c2ea650" + }, + { + "path": "s4-refresh-history/v3/fresh-root.txt", + "sha256": "52c91f9afb5a5e00535b561cd7d17054474f3effe4491320e08d046ce1adecba", + "bytes": 44, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/fresh-root.txt", + "source_sha256": "52c91f9afb5a5e00535b561cd7d17054474f3effe4491320e08d046ce1adecba" + }, + { + "path": "s4-refresh-history/v3/nextest/cleanup-s4-refresh/junit.xml", + "sha256": "f00a7ac508e48360985a7586e902ac8a0597112f4697197de7367f0d71eddd91", + "bytes": 15246, + "source": "/private/tmp/cleanup-s4-refresh-ownership-v3/nextest/cleanup-s4-refresh/junit.xml", + "source_sha256": "f00a7ac508e48360985a7586e902ac8a0597112f4697197de7367f0d71eddd91" + } + ] +} diff --git a/docs/testing/graph-disk-cleanup-v2-evidence/windows-and-native-facts-manifest.json b/docs/testing/graph-disk-cleanup-v2-evidence/windows-and-native-facts-manifest.json new file mode 100644 index 00000000..fd209ab2 --- /dev/null +++ b/docs/testing/graph-disk-cleanup-v2-evidence/windows-and-native-facts-manifest.json @@ -0,0 +1,1471 @@ +{ + "scope": "Reviewed Windows path successor and native header/logical-key facts; synthetic public-producer/oracle provenance separate from parser qualification. No native-Windows/UNC-OS/fullvisitor/origin/collector closure.", + "code_commits": { + "windows": "bc43136", + "native_facts": "61c9d26d52547bb52730fca3305b7e11863044d5" + }, + "review_input_role": "Immutable historical review inputs/evidence only; current detailed execution briefs/ownership/prerequisites remain solely in Scryer.", + "files": [ + { + "path": "windows-prefix-first-author/inputs-after.json", + "sha256": "5714b95ac0812f7569b01ea10afd224eb3c606b573385c7e4d4d49f37a9ca6c9", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/inputs-after.json", + "source_sha256": "5714b95ac0812f7569b01ea10afd224eb3c606b573385c7e4d4d49f37a9ca6c9" + }, + { + "path": "windows-prefix-first-author/evidence.json", + "sha256": "a2f22b8d611257bc09040aced5f141c162e9a0714586f7cfcb126b7acaaa3296", + "bytes": 16099, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/evidence.json", + "source_sha256": "a2f22b8d611257bc09040aced5f141c162e9a0714586f7cfcb126b7acaaa3296" + }, + { + "path": "windows-prefix-first-author/junit-lib-133.xml", + "sha256": "5115d0c589f77a4a09ac096db88c24abc137da66c6bc0ac9ff60323ee1f86e28", + "bytes": 30511, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/junit-lib-133.xml", + "source_sha256": "5115d0c589f77a4a09ac096db88c24abc137da66c6bc0ac9ff60323ee1f86e28" + }, + { + "path": "windows-prefix-first-author/fmt.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/fmt.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "windows-prefix-first-author/run.log.gz", + "sha256": "cb4c180c52446a40887515b16bd677c7be8c767a06aca85cf171e7487016878c", + "bytes": 4203, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/run.log", + "source_sha256": "45c9caf0e6fa85e2ed8d6d83c182c501dd38b6aa22e47ca1bb93391d2c2eec6a" + }, + { + "path": "windows-prefix-first-author/binary-after.json", + "sha256": "bbc784eedffdc4de89fb3c9b9891a7c483ca964af6217d0a9308a847efadb496", + "bytes": 14609, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/binary-after.json", + "source_sha256": "bbc784eedffdc4de89fb3c9b9891a7c483ca964af6217d0a9308a847efadb496" + }, + { + "path": "windows-prefix-first-author/snapshot.json", + "sha256": "547723cc72ba61be9e22bf33330402186b8f54f6880b8cf84f1f60c529fc820a", + "bytes": 667, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/snapshot.json", + "source_sha256": "547723cc72ba61be9e22bf33330402186b8f54f6880b8cf84f1f60c529fc820a" + }, + { + "path": "windows-prefix-first-author/inputs-before.json", + "sha256": "5714b95ac0812f7569b01ea10afd224eb3c606b573385c7e4d4d49f37a9ca6c9", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/inputs-before.json", + "source_sha256": "5714b95ac0812f7569b01ea10afd224eb3c606b573385c7e4d4d49f37a9ca6c9" + }, + { + "path": "windows-prefix-first-author/binary-before.json", + "sha256": "bbc784eedffdc4de89fb3c9b9891a7c483ca964af6217d0a9308a847efadb496", + "bytes": 14609, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/binary-before.json", + "source_sha256": "bbc784eedffdc4de89fb3c9b9891a7c483ca964af6217d0a9308a847efadb496" + }, + { + "path": "windows-prefix-first-author/inventory.json", + "sha256": "b30bdc16a132046c4df6ec8d0be006e23eccf00f81c9dc4195e1f42da8d68c1f", + "bytes": 109871, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/inventory.json", + "source_sha256": "b30bdc16a132046c4df6ec8d0be006e23eccf00f81c9dc4195e1f42da8d68c1f" + }, + { + "path": "windows-prefix-first-author/check.log.gz", + "sha256": "9b94c66d9647a69cf809611956d2706915fef33a27fc194d8251c50f5adb9a3a", + "bytes": 10034, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/check.log", + "source_sha256": "ef01cd29587bd4f6d83493811b594bcc4a7fb5c419ce1cbed5648383f09da924" + }, + { + "path": "windows-prefix-first-author/build.log.gz", + "sha256": "99ac39ed52f6d7a37635d0598951c3392705feebd8e34830752c336340c80f26", + "bytes": 161, + "source": "/private/tmp/cleanup-s2-windows-prefix-186-amegfv9k/build.log", + "source_sha256": "80b46d921f28ab61d3989281973ff3cca22e5460a770de8e02c9d78a9bdf7bc4" + }, + { + "path": "windows-prefix-first-review/inputs-after.json", + "sha256": "c7d1e80caee9457ac749fc33a595d01f9713f338fdabdfe02c7ff7f6df4a0d93", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/inputs-after.json", + "source_sha256": "c7d1e80caee9457ac749fc33a595d01f9713f338fdabdfe02c7ff7f6df4a0d93" + }, + { + "path": "windows-prefix-first-review/prefix_only_probe.log.gz", + "sha256": "3812557432e949b903f0ea3ea2b51e421629fa0f66741d4725e4202a9732c816", + "bytes": 251, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/prefix_only_probe.log", + "source_sha256": "5efae07d8b097acf11b322f8c128a92d756729832aabd691ee203bb9d66f6d4f" + }, + { + "path": "windows-prefix-first-review/real_path_extracted.rs", + "sha256": "724eb819fecbc86b056598c0f516a92a0d785c97af5e5adb37b2f930060febc3", + "bytes": 1526, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/real_path_extracted.rs", + "source_sha256": "724eb819fecbc86b056598c0f516a92a0d785c97af5e5adb37b2f930060febc3" + }, + { + "path": "windows-prefix-first-review/review-report.md", + "sha256": "7e97aa6875329d2ac7ebbf22771272dfb33e04b9e9ca81c4ebe6f9276cf13146", + "bytes": 8696, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/review-report.md", + "source_sha256": "7e97aa6875329d2ac7ebbf22771272dfb33e04b9e9ca81c4ebe6f9276cf13146" + }, + { + "path": "windows-prefix-first-review/prefix_only_probe.rs", + "sha256": "2271e0cb421d44ed818854eb9218042578c42e08041769b0fe16b4697b6c5186", + "bytes": 3384, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/prefix_only_probe.rs", + "source_sha256": "2271e0cb421d44ed818854eb9218042578c42e08041769b0fe16b4697b6c5186" + }, + { + "path": "windows-prefix-first-review/review-report.json", + "sha256": "59487f4e01d3a9c5efd2148c11a2ae69f0fe8f6af331dab19279bff62523ae04", + "bytes": 4770, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/review-report.json", + "source_sha256": "59487f4e01d3a9c5efd2148c11a2ae69f0fe8f6af331dab19279bff62523ae04" + }, + { + "path": "windows-prefix-first-review/original-ci-selected-excerpts.log.gz", + "sha256": "43dd33cd516ed8186129e2eb205e272c067dd199724d3d0d59148e122ea75bc2", + "bytes": 2321, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/original-ci-selected-excerpts.log", + "source_sha256": "0ec25437d3d6b367154056875063b23200ccdf5f305f94d07cbdbcc433f1be24" + }, + { + "path": "windows-prefix-first-review/private-probe-fixture-correction.txt", + "sha256": "3b878fd4d7ef0b75a515b33c3e4db2c5a1f65bc1a73bcd8c9c459a53297dd911", + "bytes": 314, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/private-probe-fixture-correction.txt", + "source_sha256": "3b878fd4d7ef0b75a515b33c3e4db2c5a1f65bc1a73bcd8c9c459a53297dd911" + }, + { + "path": "windows-prefix-first-review/focused-darwin-results.json", + "sha256": "94f8ebc8aa2c6bccbdf87d23170de8a4bb01a414cecdc218dd7379e91708c579", + "bytes": 1027, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/focused-darwin-results.json", + "source_sha256": "94f8ebc8aa2c6bccbdf87d23170de8a4bb01a414cecdc218dd7379e91708c579" + }, + { + "path": "windows-prefix-first-review/snapshot.json", + "sha256": "322fea3119cc6ee064f20bf8a78b2ce0c3ea502c185463f4e10db3eee2f9f044", + "bytes": 825, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/snapshot.json", + "source_sha256": "322fea3119cc6ee064f20bf8a78b2ce0c3ea502c185463f4e10db3eee2f9f044" + }, + { + "path": "windows-prefix-first-review/checks.json", + "sha256": "37bd8befef94572eab961c5082079feebf54e167d830e433573683c4e63ede0c", + "bytes": 2635, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/checks.json", + "source_sha256": "37bd8befef94572eab961c5082079feebf54e167d830e433573683c4e63ede0c" + }, + { + "path": "windows-prefix-first-review/focused-darwin.log.gz", + "sha256": "d6902ed8c1dbd46b7e3858b251c892d57d465f309983384c2e742ca295b12b5b", + "bytes": 439, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/focused-darwin.log", + "source_sha256": "4e9faed8eb071055d1f2b67fd7c8c6b1d13b1c042371aea173e2e931a18ecbce" + }, + { + "path": "windows-prefix-first-review/inputs-before.json", + "sha256": "c7d1e80caee9457ac749fc33a595d01f9713f338fdabdfe02c7ff7f6df4a0d93", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/inputs-before.json", + "source_sha256": "c7d1e80caee9457ac749fc33a595d01f9713f338fdabdfe02c7ff7f6df4a0d93" + }, + { + "path": "windows-prefix-first-review/junit-focused-darwin.xml", + "sha256": "c526d8c6b50f52f47eda7d1d0417b87759fec92fd5960deb0e70907f6ad26154", + "bytes": 2415, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/junit-focused-darwin.xml", + "source_sha256": "c526d8c6b50f52f47eda7d1d0417b87759fec92fd5960deb0e70907f6ad26154" + }, + { + "path": "windows-prefix-first-review/unix_real_path_probe.rs", + "sha256": "c07d260917b4dff6bc3457722eece541076d63bc9b1132649c52fa2fca76d199", + "bytes": 1973, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/unix_real_path_probe.rs", + "source_sha256": "c07d260917b4dff6bc3457722eece541076d63bc9b1132649c52fa2fca76d199" + }, + { + "path": "windows-prefix-first-review/unix_real_path_probe_initial.rs", + "sha256": "168af0aca72a0de3b0f6307c15b3e461a7fc56ded76a0b0dfa3bb13ef67474ca", + "bytes": 1946, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/unix_real_path_probe_initial.rs", + "source_sha256": "168af0aca72a0de3b0f6307c15b3e461a7fc56ded76a0b0dfa3bb13ef67474ca" + }, + { + "path": "windows-prefix-first-review/binary-provenance.json", + "sha256": "fa540634faea9958b6ec23bc5220a926417f70e15ce6948f93effb8bbe72dded", + "bytes": 562, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/binary-provenance.json", + "source_sha256": "fa540634faea9958b6ec23bc5220a926417f70e15ce6948f93effb8bbe72dded" + }, + { + "path": "windows-prefix-first-review/unix_real_path_probe_initial.log.gz", + "sha256": "5fa3945c1d3d9f2a2e133a525b2a3fd13efd378d68c3fd0f2804c1a2699edc66", + "bytes": 472, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/unix_real_path_probe_initial.log", + "source_sha256": "0f32076e2360074d79bf319eab7ca05c40916141aaf37927c9325785571b0ef1" + }, + { + "path": "windows-prefix-first-review/stdlib-provenance.json", + "sha256": "77678ce2f222884de3150a2478452bd8cd5008b80445c985d1b7d427712fe094", + "bytes": 568, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/stdlib-provenance.json", + "source_sha256": "77678ce2f222884de3150a2478452bd8cd5008b80445c985d1b7d427712fe094" + }, + { + "path": "windows-prefix-first-review/unix_real_path_probe.log.gz", + "sha256": "26c91c157fb8bedbea114856ec3ac9d16d43d45f3dfff00320b0a00183dc7749", + "bytes": 207, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/unix_real_path_probe.log", + "source_sha256": "6a36417202b52e540cd7bc36930af09c41e994c5296c4411da4c52e888553d9c" + }, + { + "path": "windows-prefix-first-review/stdlib-source/path.rs", + "sha256": "e55591a7330684003bf66cfbae966eb0811a972f3390c0abcca4f7fa8bf5f437", + "bytes": 137675, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/stdlib-source/path.rs", + "source_sha256": "e55591a7330684003bf66cfbae966eb0811a972f3390c0abcca4f7fa8bf5f437" + }, + { + "path": "windows-prefix-first-review/stdlib-source/sys/path/windows.rs", + "sha256": "52479e81fd268561ab141251f850de1aff2fd742505b22f966aff5bb1e5a9998", + "bytes": 9764, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/stdlib-source/sys/path/windows.rs", + "source_sha256": "52479e81fd268561ab141251f850de1aff2fd742505b22f966aff5bb1e5a9998" + }, + { + "path": "windows-prefix-first-review/stdlib-source/sys/path/windows_prefix.rs", + "sha256": "03ab849915aab993f312de477d95399520ee52cef6506d92cba4e1ea0f091302", + "bytes": 6529, + "source": "/private/tmp/cleanup-s2-windows-prefix-review-19szolv5/stdlib-source/sys/path/windows_prefix.rs", + "source_sha256": "03ab849915aab993f312de477d95399520ee52cef6506d92cba4e1ea0f091302" + }, + { + "path": "windows-prefix-author/inputs-after.json", + "sha256": "c34ff0591eb5f68eab712bd1180256eed5fbbaec12af28d0ff56556e99f99a72", + "bytes": 57361, + "source": "/private/tmp/cleanup-windows-path-successor-190/inputs-after.json", + "source_sha256": "c34ff0591eb5f68eab712bd1180256eed5fbbaec12af28d0ff56556e99f99a72" + }, + { + "path": "windows-prefix-author/evidence.json", + "sha256": "446a577166d0d61293da73ac32ce4fa4e9a89d1fb9a5790c6f0e0a5dadab663c", + "bytes": 3914, + "source": "/private/tmp/cleanup-windows-path-successor-190/evidence.json", + "source_sha256": "446a577166d0d61293da73ac32ce4fa4e9a89d1fb9a5790c6f0e0a5dadab663c" + }, + { + "path": "windows-prefix-author/all-inputs-before.json", + "sha256": "c34ff0591eb5f68eab712bd1180256eed5fbbaec12af28d0ff56556e99f99a72", + "bytes": 57361, + "source": "/private/tmp/cleanup-windows-path-successor-190/all-inputs-before.json", + "source_sha256": "c34ff0591eb5f68eab712bd1180256eed5fbbaec12af28d0ff56556e99f99a72" + }, + { + "path": "windows-prefix-author/junit-lib-133.xml", + "sha256": "5ae27ed86eb931d05805a5ba43c0e65750c9a5ea8763c0b4909e31c245aab09d", + "bytes": 22432, + "source": "/private/tmp/cleanup-windows-path-successor-190/junit-lib-133.xml", + "source_sha256": "5ae27ed86eb931d05805a5ba43c0e65750c9a5ea8763c0b4909e31c245aab09d" + }, + { + "path": "windows-prefix-author/fmt.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-windows-path-successor-190/fmt.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "windows-prefix-author/run.log.gz", + "sha256": "c8cfde67e102b3303ad625ee323234a3d1e6cb28974ef0d5f7ae044abce0c1c6", + "bytes": 3512, + "source": "/private/tmp/cleanup-windows-path-successor-190/run.log", + "source_sha256": "553b9395e945c897bada56e2f5bc761384602247e1547bcc1b496b65c878e01a" + }, + { + "path": "windows-prefix-author/selected.json", + "sha256": "364b2a97d08f7967b108cecfb960e439f8746bb24c5b6b38c992292c3a8b89ec", + "bytes": 13831, + "source": "/private/tmp/cleanup-windows-path-successor-190/selected.json", + "source_sha256": "364b2a97d08f7967b108cecfb960e439f8746bb24c5b6b38c992292c3a8b89ec" + }, + { + "path": "windows-prefix-author/binary-after.json", + "sha256": "45740109d91fda0e508f203f3ced0deedc4137f33d355a5748169234fc178c6c", + "bytes": 1096, + "source": "/private/tmp/cleanup-windows-path-successor-190/binary-after.json", + "source_sha256": "45740109d91fda0e508f203f3ced0deedc4137f33d355a5748169234fc178c6c" + }, + { + "path": "windows-prefix-author/inventory.txt", + "sha256": "1e4ebca036a317b84c6f7a9da1aa5fe7b63be38004505ffa9225f46a5e86d48f", + "bytes": 57150, + "source": "/private/tmp/cleanup-windows-path-successor-190/inventory.txt", + "source_sha256": "1e4ebca036a317b84c6f7a9da1aa5fe7b63be38004505ffa9225f46a5e86d48f" + }, + { + "path": "windows-prefix-author/inputs-before.json", + "sha256": "19104e2e77b57e9fa88d5a1638b9a8cc2c5b011afd5d699c33c97966ad06c560", + "bytes": 21111, + "source": "/private/tmp/cleanup-windows-path-successor-190/inputs-before.json", + "source_sha256": "19104e2e77b57e9fa88d5a1638b9a8cc2c5b011afd5d699c33c97966ad06c560" + }, + { + "path": "windows-prefix-author/baseline.json", + "sha256": "75395878de303778e2e6037b6cc4b579b95468e14d97a74bd2df3c0617b2411a", + "bytes": 463, + "source": "/private/tmp/cleanup-windows-path-successor-190/baseline.json", + "source_sha256": "75395878de303778e2e6037b6cc4b579b95468e14d97a74bd2df3c0617b2411a" + }, + { + "path": "windows-prefix-author/binary-before.json", + "sha256": "45740109d91fda0e508f203f3ced0deedc4137f33d355a5748169234fc178c6c", + "bytes": 1096, + "source": "/private/tmp/cleanup-windows-path-successor-190/binary-before.json", + "source_sha256": "45740109d91fda0e508f203f3ced0deedc4137f33d355a5748169234fc178c6c" + }, + { + "path": "windows-prefix-author/unix_probe.rs", + "sha256": "bf82894fb56d27f2385b0dc1b7f5db332447e7bb8f706e4502062cc2d6f32854", + "bytes": 2721, + "source": "/private/tmp/cleanup-windows-path-successor-190/unix_probe.rs", + "source_sha256": "bf82894fb56d27f2385b0dc1b7f5db332447e7bb8f706e4502062cc2d6f32854" + }, + { + "path": "windows-prefix-author/run-summary.json", + "sha256": "f13cf3817578d934cd479dd1ea5de55bff54ef107402b13f97d5d8f82d088a4c", + "bytes": 82, + "source": "/private/tmp/cleanup-windows-path-successor-190/run-summary.json", + "source_sha256": "f13cf3817578d934cd479dd1ea5de55bff54ef107402b13f97d5d8f82d088a4c" + }, + { + "path": "windows-prefix-author/build.log.gz", + "sha256": "160afb2bd6fb0e581267f13d494034f67412ba6e70855be60501eb94237bb27f", + "bytes": 157, + "source": "/private/tmp/cleanup-windows-path-successor-190/build.log", + "source_sha256": "c718498548abdcc3950d4b235b07527418cc285ab41e7769357a70ca276cae55" + }, + { + "path": "windows-prefix-review/inputs-after.json", + "sha256": "ab9bd71a9e23887c80046d3dca9376d3b63c2ae9e49c3386ec4608f70fd21a38", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/inputs-after.json", + "source_sha256": "ab9bd71a9e23887c80046d3dca9376d3b63c2ae9e49c3386ec4608f70fd21a38" + }, + { + "path": "windows-prefix-review/real_path_extracted.rs", + "sha256": "f4ca3a939a1b74e2a7f63d63e6e762fcf8a912a6874dbc0b9180cd57e561b406", + "bytes": 2174, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/real_path_extracted.rs", + "source_sha256": "f4ca3a939a1b74e2a7f63d63e6e762fcf8a912a6874dbc0b9180cd57e561b406" + }, + { + "path": "windows-prefix-review/review-report.md", + "sha256": "1edaac16297784f96ae449020c958c57db8cf8347c25a50712c11b1aca13086c", + "bytes": 7894, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/review-report.md", + "source_sha256": "1edaac16297784f96ae449020c958c57db8cf8347c25a50712c11b1aca13086c" + }, + { + "path": "windows-prefix-review/anchors.json", + "sha256": "40021d9dc0847528935111dc7bed28d0d43733d0d05edc073b405370419a7a5c", + "bytes": 159, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/anchors.json", + "source_sha256": "40021d9dc0847528935111dc7bed28d0d43733d0d05edc073b405370419a7a5c" + }, + { + "path": "windows-prefix-review/review-report.json", + "sha256": "157ea1666e94d67b1dee47fdf1396b08505b2eb8c6aabfac2ee5f837d29ef0d2", + "bytes": 4660, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/review-report.json", + "source_sha256": "157ea1666e94d67b1dee47fdf1396b08505b2eb8c6aabfac2ee5f837d29ef0d2" + }, + { + "path": "windows-prefix-review/extracted-prefix-regions.rs", + "sha256": "77ff213713220e30fb188ffca484f89caf0a5cf9677bfe9a0ef471dc03126ea5", + "bytes": 606, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/extracted-prefix-regions.rs", + "source_sha256": "77ff213713220e30fb188ffca484f89caf0a5cf9677bfe9a0ef471dc03126ea5" + }, + { + "path": "windows-prefix-review/focused-darwin-results.json", + "sha256": "583852befdbaae3873d109e941852160e390c591737c40ff785e0b460175f17e", + "bytes": 1037, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/focused-darwin-results.json", + "source_sha256": "583852befdbaae3873d109e941852160e390c591737c40ff785e0b460175f17e" + }, + { + "path": "windows-prefix-review/snapshot.json", + "sha256": "bf11ac056a95c7fcd97c3a1d40d89aa364760b2f51ca761b5e81b6b669b5b3a0", + "bytes": 776, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/snapshot.json", + "source_sha256": "bf11ac056a95c7fcd97c3a1d40d89aa364760b2f51ca761b5e81b6b669b5b3a0" + }, + { + "path": "windows-prefix-review/prefix_decision_probe.log.gz", + "sha256": "8640915ebb4ac1db7b4643268a7b82bd3d79af3997cb7752f6414ef71f853f4a", + "bytes": 242, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/prefix_decision_probe.log", + "source_sha256": "eac842b4556ce1f6fc1971b012149e53c259b97db1a4b2b778e40ffbf6f6eafe" + }, + { + "path": "windows-prefix-review/unix_probe_initial_compile.log.gz", + "sha256": "9edf84fc0e066c30e0441e383b91511c8828098e03fbd6d4b2adc4bdd1dfd3d0", + "bytes": 184, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/unix_probe_initial_compile.log", + "source_sha256": "7fcbfcff759ef7aa90f89ab6900ecabafe017c6c877e6fc5731c25d5be5b5a6c" + }, + { + "path": "windows-prefix-review/checks.json", + "sha256": "4d17a929d8b69444e4b414257473f13dbaff971fe5a8784cf10d52b80ab325c8", + "bytes": 3119, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/checks.json", + "source_sha256": "4d17a929d8b69444e4b414257473f13dbaff971fe5a8784cf10d52b80ab325c8" + }, + { + "path": "windows-prefix-review/focused-darwin.log.gz", + "sha256": "8bd48d21fb8e36449cd90838e7b8b21cbdd734996a0741bdb22be7cdee87445c", + "bytes": 439, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/focused-darwin.log", + "source_sha256": "06e2263f46f659c15d00af3827d438632a082cd7ad31a00f43ea1a916dc24c6e" + }, + { + "path": "windows-prefix-review/inputs-before.json", + "sha256": "ab9bd71a9e23887c80046d3dca9376d3b63c2ae9e49c3386ec4608f70fd21a38", + "bytes": 57361, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/inputs-before.json", + "source_sha256": "ab9bd71a9e23887c80046d3dca9376d3b63c2ae9e49c3386ec4608f70fd21a38" + }, + { + "path": "windows-prefix-review/real_path_extracted_initial.rs", + "sha256": "131b12a0a1743a4e48fcec1467db6cf01127c557b62adeeced8f3d4ba0401a3e", + "bytes": 2157, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/real_path_extracted_initial.rs", + "source_sha256": "131b12a0a1743a4e48fcec1467db6cf01127c557b62adeeced8f3d4ba0401a3e" + }, + { + "path": "windows-prefix-review/junit-focused-darwin.xml", + "sha256": "4e00390e5d2f4147749f096b5dddaa99426eec0a04b0175e92b9f9566fdd4e04", + "bytes": 2415, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/junit-focused-darwin.xml", + "source_sha256": "4e00390e5d2f4147749f096b5dddaa99426eec0a04b0175e92b9f9566fdd4e04" + }, + { + "path": "windows-prefix-review/unix_real_path_probe.rs", + "sha256": "c07d260917b4dff6bc3457722eece541076d63bc9b1132649c52fa2fca76d199", + "bytes": 1973, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/unix_real_path_probe.rs", + "source_sha256": "c07d260917b4dff6bc3457722eece541076d63bc9b1132649c52fa2fca76d199" + }, + { + "path": "windows-prefix-review/prefix_decision_probe.rs", + "sha256": "7c782ba00e534a18e2d8acf1e6bd1a1f86585b3766fe108fba81b76081db23bc", + "bytes": 4611, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/prefix_decision_probe.rs", + "source_sha256": "7c782ba00e534a18e2d8acf1e6bd1a1f86585b3766fe108fba81b76081db23bc" + }, + { + "path": "windows-prefix-review/binary-provenance.json", + "sha256": "af2ea17fb3b5ac96fb3de7769365e22f354c537c087e460267a4f0832efcb8b2", + "bytes": 1372, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/binary-provenance.json", + "source_sha256": "af2ea17fb3b5ac96fb3de7769365e22f354c537c087e460267a4f0832efcb8b2" + }, + { + "path": "windows-prefix-review/reconstruction.log.gz", + "sha256": "e3101c78dc9d2d913b5440226b237840c16351dd9e1e9a0503ea1d673a10ec66", + "bytes": 59, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/reconstruction.log", + "source_sha256": "c70d80e0b76f21ea58b2d700126423b4b143b8efca5befea356ebd39e4037859" + }, + { + "path": "windows-prefix-review/stdlib-provenance.json", + "sha256": "3afee0f1dc32b9a691fde5b6994d2b3a66e4d39221fa168d29d7c14d068476fd", + "bytes": 534, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/stdlib-provenance.json", + "source_sha256": "3afee0f1dc32b9a691fde5b6994d2b3a66e4d39221fa168d29d7c14d068476fd" + }, + { + "path": "windows-prefix-review/unix_real_path_probe.log.gz", + "sha256": "7c4f7a04fc995d32a9710f7645967583116c5a06e4a372ee3d4d9f973d88d391", + "bytes": 207, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/unix_real_path_probe.log", + "source_sha256": "b1ef2d0d5d255a24f87c8d6aefd85c9b73da085aa6eb451f7ef71c722531dfb1" + }, + { + "path": "windows-prefix-review/stdlib-source/path.rs", + "sha256": "e55591a7330684003bf66cfbae966eb0811a972f3390c0abcca4f7fa8bf5f437", + "bytes": 137675, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/stdlib-source/path.rs", + "source_sha256": "e55591a7330684003bf66cfbae966eb0811a972f3390c0abcca4f7fa8bf5f437" + }, + { + "path": "windows-prefix-review/stdlib-source/fs.rs", + "sha256": "3129ef6b313b88d9cb203a98383094e2e4aa8386d79e5c52dd1c50974c8cdc57", + "bytes": 123392, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/stdlib-source/fs.rs", + "source_sha256": "3129ef6b313b88d9cb203a98383094e2e4aa8386d79e5c52dd1c50974c8cdc57" + }, + { + "path": "windows-prefix-review/stdlib-source/sys/path/windows.rs", + "sha256": "52479e81fd268561ab141251f850de1aff2fd742505b22f966aff5bb1e5a9998", + "bytes": 9764, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/stdlib-source/sys/path/windows.rs", + "source_sha256": "52479e81fd268561ab141251f850de1aff2fd742505b22f966aff5bb1e5a9998" + }, + { + "path": "windows-prefix-review/stdlib-source/sys/path/windows_prefix.rs", + "sha256": "03ab849915aab993f312de477d95399520ee52cef6506d92cba4e1ea0f091302", + "bytes": 6529, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/stdlib-source/sys/path/windows_prefix.rs", + "source_sha256": "03ab849915aab993f312de477d95399520ee52cef6506d92cba4e1ea0f091302" + }, + { + "path": "windows-prefix-review/stdlib-source/sys/fs/mod.rs", + "sha256": "f1137a6a7ec3f048c29a6709fae1d4c1d20d4496ff163e555fa66acabbfcbf60", + "bytes": 5233, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/stdlib-source/sys/fs/mod.rs", + "source_sha256": "f1137a6a7ec3f048c29a6709fae1d4c1d20d4496ff163e555fa66acabbfcbf60" + }, + { + "path": "windows-prefix-review/stdlib-source/sys/fs/windows.rs", + "sha256": "e26633635161814b3e9bd75a50cf0bbe2d90d55c9342e3adcbced16d6b024099", + "bytes": 64227, + "source": "/private/tmp/cleanup-s2-windows-prefix-fix-review-fp3c5gr5/stdlib-source/sys/fs/windows.rs", + "source_sha256": "e26633635161814b3e9bd75a50cf0bbe2d90d55c9342e3adcbced16d6b024099" + }, + { + "path": "native-facts-author/source-manifest.json", + "sha256": "68691435956b13c3c26219b1f1afbb0e88159636fbcfc0debc649ff6cd4f12ad", + "bytes": 769, + "source": "/private/tmp/cleanup-s3-native-header-189/source-manifest.json", + "source_sha256": "68691435956b13c3c26219b1f1afbb0e88159636fbcfc0debc649ff6cd4f12ad" + }, + { + "path": "native-facts-author/build-list.log.gz", + "sha256": "796239760cb1e421b33855a5977cfdffa4c58e61f24542fb2d0f81e3fa868e8d", + "bytes": 153, + "source": "/private/tmp/cleanup-s3-native-header-189/build-list.log", + "source_sha256": "94371e10c2626b06f9df2a1763e2336dd5747be98a9f3a691626dbc0086880f0" + }, + { + "path": "native-facts-author/binaries-after.json", + "sha256": "b60848ee78a16fab62a00823f3e222d0f5f9f7b3402628bb23d60d17786e7db3", + "bytes": 456, + "source": "/private/tmp/cleanup-s3-native-header-189/binaries-after.json", + "source_sha256": "b60848ee78a16fab62a00823f3e222d0f5f9f7b3402628bb23d60d17786e7db3" + }, + { + "path": "native-facts-author/all-inputs-before.json", + "sha256": "397f1d3da265654b1e1505390a5d32f747903ddb802f1552e402b462e935f470", + "bytes": 81482, + "source": "/private/tmp/cleanup-s3-native-header-189/all-inputs-before.json", + "source_sha256": "397f1d3da265654b1e1505390a5d32f747903ddb802f1552e402b462e935f470" + }, + { + "path": "native-facts-author/fmt.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s3-native-header-189/fmt.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "native-facts-author/run.log.gz", + "sha256": "c98686d808c517b6b618d46462b30c70dcdf13ff83b4c96de04fda7fa850c081", + "bytes": 19424, + "source": "/private/tmp/cleanup-s3-native-header-189/run.log", + "source_sha256": "8ecdfb507262a27c56b1785e6b65e5aef12f52a310a45d7fba6fde606e15e64c" + }, + { + "path": "native-facts-author/all-inputs-after.json", + "sha256": "397f1d3da265654b1e1505390a5d32f747903ddb802f1552e402b462e935f470", + "bytes": 81482, + "source": "/private/tmp/cleanup-s3-native-header-189/all-inputs-after.json", + "source_sha256": "397f1d3da265654b1e1505390a5d32f747903ddb802f1552e402b462e935f470" + }, + { + "path": "native-facts-author/binaries-before.json", + "sha256": "e6b06acb0fdcf36e33f5a50fe78d876f7196049a40c1bbc31fde912326185a44", + "bytes": 456, + "source": "/private/tmp/cleanup-s3-native-header-189/binaries-before.json", + "source_sha256": "e6b06acb0fdcf36e33f5a50fe78d876f7196049a40c1bbc31fde912326185a44" + }, + { + "path": "native-facts-author/baseline.json", + "sha256": "22ff8150b6c0e47ccbab88cae82b8c4044759e0c8ff5a40550c11f707af1d255", + "bytes": 185, + "source": "/private/tmp/cleanup-s3-native-header-189/baseline.json", + "source_sha256": "22ff8150b6c0e47ccbab88cae82b8c4044759e0c8ff5a40550c11f707af1d255" + }, + { + "path": "native-facts-author/evidence-summary.json", + "sha256": "91b6810ccda20f53a8e26d671dc19ad90aaa21e8eb8a0e4b103e4791bc36d9b8", + "bytes": 908, + "source": "/private/tmp/cleanup-s3-native-header-189/evidence-summary.json", + "source_sha256": "91b6810ccda20f53a8e26d671dc19ad90aaa21e8eb8a0e4b103e4791bc36d9b8" + }, + { + "path": "native-facts-author/sdk-native-attribution.json", + "sha256": "3d39555f58c08e59f43dd1f550f8b92afeb9f0363a15e78aa5dd0949949bda31", + "bytes": 1108, + "source": "/private/tmp/cleanup-s3-native-header-189/sdk-native-attribution.json", + "source_sha256": "3d39555f58c08e59f43dd1f550f8b92afeb9f0363a15e78aa5dd0949949bda31" + }, + { + "path": "native-facts-author/selected-tests.txt", + "sha256": "2cfdd3c31a9371e449cee90599bde2305b5385a0f0c09a3dae1f62f46abab889", + "bytes": 12898, + "source": "/private/tmp/cleanup-s3-native-header-189/selected-tests.txt", + "source_sha256": "2cfdd3c31a9371e449cee90599bde2305b5385a0f0c09a3dae1f62f46abab889" + }, + { + "path": "native-facts-author/nextest-list.json", + "sha256": "84e89632b40feb6b65efc750ce407912f305a26e0e84abcf6352cd9700453b77", + "bytes": 112858, + "source": "/private/tmp/cleanup-s3-native-header-189/nextest-list.json", + "source_sha256": "84e89632b40feb6b65efc750ce407912f305a26e0e84abcf6352cd9700453b77" + }, + { + "path": "native-facts-author/junit.xml", + "sha256": "f10e09b8bae87a011475174b6651e249d02aaa15d9abd0606439c20b4b3d1325", + "bytes": 29094, + "source": "/private/tmp/cleanup-s3-native-header-189/junit.xml", + "source_sha256": "f10e09b8bae87a011475174b6651e249d02aaa15d9abd0606439c20b4b3d1325" + }, + { + "path": "native-facts-review/header-probe-result.txt", + "sha256": "ea84ce3f7a1d9656ea0af3dd45cbdc311612c5ceb8b8800da06421a5bff627a6", + "bytes": 772, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/header-probe-result.txt", + "source_sha256": "ea84ce3f7a1d9656ea0af3dd45cbdc311612c5ceb8b8800da06421a5bff627a6" + }, + { + "path": "native-facts-review/source-manifest.json", + "sha256": "68691435956b13c3c26219b1f1afbb0e88159636fbcfc0debc649ff6cd4f12ad", + "bytes": 769, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/source-manifest.json", + "source_sha256": "68691435956b13c3c26219b1f1afbb0e88159636fbcfc0debc649ff6cd4f12ad" + }, + { + "path": "native-facts-review/native-module-test-build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-module-test-build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "native-facts-review/build-list.log.gz", + "sha256": "796239760cb1e421b33855a5977cfdffa4c58e61f24542fb2d0f81e3fa868e8d", + "bytes": 153, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/build-list.log", + "source_sha256": "94371e10c2626b06f9df2a1763e2336dd5747be98a9f3a691626dbc0086880f0" + }, + { + "path": "native-facts-review/binaries-after.json", + "sha256": "b60848ee78a16fab62a00823f3e222d0f5f9f7b3402628bb23d60d17786e7db3", + "bytes": 456, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/binaries-after.json", + "source_sha256": "b60848ee78a16fab62a00823f3e222d0f5f9f7b3402628bb23d60d17786e7db3" + }, + { + "path": "native-facts-review/all-inputs-before.json", + "sha256": "397f1d3da265654b1e1505390a5d32f747903ddb802f1552e402b462e935f470", + "bytes": 81482, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/all-inputs-before.json", + "source_sha256": "397f1d3da265654b1e1505390a5d32f747903ddb802f1552e402b462e935f470" + }, + { + "path": "native-facts-review/native-module-test-result.txt", + "sha256": "691ba1fed07e0c449ebe08ecf3b37b045617f1ec610057d84c5c43c8edf21ec6", + "bytes": 3386, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-module-test-result.txt", + "source_sha256": "691ba1fed07e0c449ebe08ecf3b37b045617f1ec610057d84c5c43c8edf21ec6" + }, + { + "path": "native-facts-review/run.log.gz", + "sha256": "c98686d808c517b6b618d46462b30c70dcdf13ff83b4c96de04fda7fa850c081", + "bytes": 19424, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/run.log", + "source_sha256": "8ecdfb507262a27c56b1785e6b65e5aef12f52a310a45d7fba6fde606e15e64c" + }, + { + "path": "native-facts-review/native-boundary-probe-result.stderr", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-boundary-probe-result.stderr", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "native-facts-review/runtime-dependency-match.json", + "sha256": "ab8f33171f7dc2703fdd96013b8d094b8c87797372b540b3e5ec4c36c6bc0344", + "bytes": 254, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/runtime-dependency-match.json", + "source_sha256": "ab8f33171f7dc2703fdd96013b8d094b8c87797372b540b3e5ec4c36c6bc0344" + }, + { + "path": "native-facts-review/rustfmt-owned.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/rustfmt-owned.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "native-facts-review/probe-externs.json", + "sha256": "6347ec72915c181b0e1e8ab8a765e699424efbf9e4b651245c813a10a444ea72", + "bytes": 341, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/probe-externs.json", + "source_sha256": "6347ec72915c181b0e1e8ab8a765e699424efbf9e4b651245c813a10a444ea72" + }, + { + "path": "native-facts-review/native-boundary-probe-build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-boundary-probe-build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "native-facts-review/native-module-tests.rs", + "sha256": "46a54373cb0af342e9fd21701209630b10bbc8b48d3394fbc7501fa2832193aa", + "bytes": 1094, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-module-tests.rs", + "source_sha256": "46a54373cb0af342e9fd21701209630b10bbc8b48d3394fbc7501fa2832193aa" + }, + { + "path": "native-facts-review/binaries-before.json", + "sha256": "e6b06acb0fdcf36e33f5a50fe78d876f7196049a40c1bbc31fde912326185a44", + "bytes": 456, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/binaries-before.json", + "source_sha256": "e6b06acb0fdcf36e33f5a50fe78d876f7196049a40c1bbc31fde912326185a44" + }, + { + "path": "native-facts-review/evidence-summary.json", + "sha256": "91b6810ccda20f53a8e26d671dc19ad90aaa21e8eb8a0e4b103e4791bc36d9b8", + "bytes": 908, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/evidence-summary.json", + "source_sha256": "91b6810ccda20f53a8e26d671dc19ad90aaa21e8eb8a0e4b103e4791bc36d9b8" + }, + { + "path": "native-facts-review/header-probe.rs", + "sha256": "3906a8a2efa6890addca38a03b7dabf3767c14f292d02013e0d8107668d54cbd", + "bytes": 1230, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/header-probe.rs", + "source_sha256": "3906a8a2efa6890addca38a03b7dabf3767c14f292d02013e0d8107668d54cbd" + }, + { + "path": "native-facts-review/native-foundation.diff.gz", + "sha256": "1ebc7037eef61b54de5e3eefedf8e971d661ea93b2f5e0f9c4d50e21f3e9ca89", + "bytes": 4200, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-foundation.diff", + "source_sha256": "3527615d0402f0b4f74813cf3217f3d3480bd96736cb0ab07bb1cbbce9ace4e8" + }, + { + "path": "native-facts-review/review-artifact-sha256.json", + "sha256": "fc84bbf3bb1549ef7a90deb85f2dd1481585316a7b311abf8f4fde88b25b67b9", + "bytes": 2977, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/review-artifact-sha256.json", + "source_sha256": "fc84bbf3bb1549ef7a90deb85f2dd1481585316a7b311abf8f4fde88b25b67b9" + }, + { + "path": "native-facts-review/probe-dependencies.json", + "sha256": "e54ab7f930cba28739fd159f01096c3fd64b8b96179405fd47c2ce9923507956", + "bytes": 11307, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/probe-dependencies.json", + "source_sha256": "e54ab7f930cba28739fd159f01096c3fd64b8b96179405fd47c2ce9923507956" + }, + { + "path": "native-facts-review/sdk-native-attribution.json", + "sha256": "3d39555f58c08e59f43dd1f550f8b92afeb9f0363a15e78aa5dd0949949bda31", + "bytes": 1108, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/sdk-native-attribution.json", + "source_sha256": "3d39555f58c08e59f43dd1f550f8b92afeb9f0363a15e78aa5dd0949949bda31" + }, + { + "path": "native-facts-review/selected-tests.txt", + "sha256": "2cfdd3c31a9371e449cee90599bde2305b5385a0f0c09a3dae1f62f46abab889", + "bytes": 12898, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/selected-tests.txt", + "source_sha256": "2cfdd3c31a9371e449cee90599bde2305b5385a0f0c09a3dae1f62f46abab889" + }, + { + "path": "native-facts-review/verification.json", + "sha256": "44d7026846b62ed96a3f3f49570976400fc9444cb915f1d32154b7d1578433da", + "bytes": 3702, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/verification.json", + "source_sha256": "44d7026846b62ed96a3f3f49570976400fc9444cb915f1d32154b7d1578433da" + }, + { + "path": "native-facts-review/sdk-primary-source.json", + "sha256": "f901e53974b759b9ce83622f80bddc9d432e860f723791de430c6cff1b286542", + "bytes": 2231, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/sdk-primary-source.json", + "source_sha256": "f901e53974b759b9ce83622f80bddc9d432e860f723791de430c6cff1b286542" + }, + { + "path": "native-facts-review/native-module-test-command.json", + "sha256": "da8cdd300c4dbdb13cca0d24a7ea4fce6f649613f1fd1830a63161905f8d8ed8", + "bytes": 675, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-module-test-command.json", + "source_sha256": "da8cdd300c4dbdb13cca0d24a7ea4fce6f649613f1fd1830a63161905f8d8ed8" + }, + { + "path": "native-facts-review/review.md", + "sha256": "83fe6b3d3d8b884c08c8364a7411517bff6ebbbe4fc8cf51d90be7ace025d988", + "bytes": 11357, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/review.md", + "source_sha256": "83fe6b3d3d8b884c08c8364a7411517bff6ebbbe4fc8cf51d90be7ace025d988" + }, + { + "path": "native-facts-review/nextest-list.json", + "sha256": "84e89632b40feb6b65efc750ce407912f305a26e0e84abcf6352cd9700453b77", + "bytes": 112858, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/nextest-list.json", + "source_sha256": "84e89632b40feb6b65efc750ce407912f305a26e0e84abcf6352cd9700453b77" + }, + { + "path": "native-facts-review/junit.xml", + "sha256": "f10e09b8bae87a011475174b6651e249d02aaa15d9abd0606439c20b4b3d1325", + "bytes": 29094, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/junit.xml", + "source_sha256": "f10e09b8bae87a011475174b6651e249d02aaa15d9abd0606439c20b4b3d1325" + }, + { + "path": "native-facts-review/native-boundary-probe-result.json", + "sha256": "a8b41fe578e3663dc2b65730b3207f4112bdb8b2ba86d51b0e5055440e847358", + "bytes": 54, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-boundary-probe-result.json", + "source_sha256": "a8b41fe578e3663dc2b65730b3207f4112bdb8b2ba86d51b0e5055440e847358" + }, + { + "path": "native-facts-review/header-probe-build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/header-probe-build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "native-facts-review/native-boundary-probe-command.json", + "sha256": "43dc378ad1efdf889279dbb5021841959051d77b751792800ccc261e6b93367a", + "bytes": 656, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/native-boundary-probe-command.json", + "source_sha256": "43dc378ad1efdf889279dbb5021841959051d77b751792800ccc261e6b93367a" + }, + { + "path": "native-facts-review/probe/graph_partition.rs", + "sha256": "9472624122052611290346122edf4b27c1bb8ce0a3e51313d65eb99301c00e6a", + "bytes": 2315, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/probe/graph_partition.rs", + "source_sha256": "9472624122052611290346122edf4b27c1bb8ce0a3e51313d65eb99301c00e6a" + }, + { + "path": "native-facts-review/probe/manifest_entry.rs", + "sha256": "2878253b3cddf6e04da68ba68cda9125d0e7b2d52817c11da8d9234756eff334", + "bytes": 863, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/probe/manifest_entry.rs", + "source_sha256": "2878253b3cddf6e04da68ba68cda9125d0e7b2d52817c11da8d9234756eff334" + }, + { + "path": "native-facts-review/probe/main.rs", + "sha256": "a34096c7f8195954186b0ca7e02a51de5e3cc3c2af366f6f9384af8d6c25d359", + "bytes": 7885, + "source": "/private/tmp/cleanup-s3-native-header-review-yb6v6y2e/probe/main.rs", + "source_sha256": "a34096c7f8195954186b0ca7e02a51de5e3cc3c2af366f6f9384af8d6c25d359" + }, + { + "path": "native-synthetic-oracle/baseline-inputs.json", + "sha256": "52077bd7ab64215fdc5883e919647fd8f48b4b4f5adaa65ca433816013718e8e", + "bytes": 21019, + "source": "/private/tmp/cleanup-s3-native-oracle-187/baseline-inputs.json", + "source_sha256": "52077bd7ab64215fdc5883e919647fd8f48b4b4f5adaa65ca433816013718e8e" + }, + { + "path": "native-synthetic-oracle/source-manifest.json", + "sha256": "13cf5270f2044c482e918a110ab2eae4130e865733295330d9eb28521ec3439b", + "bytes": 615, + "source": "/private/tmp/cleanup-s3-native-oracle-187/source-manifest.json", + "source_sha256": "13cf5270f2044c482e918a110ab2eae4130e865733295330d9eb28521ec3439b" + }, + { + "path": "native-synthetic-oracle/all-inputs-before.json", + "sha256": "33b30994ee8e44bee324143dda441ff76126d866e8c7dab38445251886267330", + "bytes": 81469, + "source": "/private/tmp/cleanup-s3-native-oracle-187/all-inputs-before.json", + "source_sha256": "33b30994ee8e44bee324143dda441ff76126d866e8c7dab38445251886267330" + }, + { + "path": "native-synthetic-oracle/compiled-inputs-after.json", + "sha256": "4a39d94318d40ba655dac98cace03b1f2f257868a8aa56c87b90c01d5bdce0ea", + "bytes": 48340, + "source": "/private/tmp/cleanup-s3-native-oracle-187/compiled-inputs-after.json", + "source_sha256": "4a39d94318d40ba655dac98cace03b1f2f257868a8aa56c87b90c01d5bdce0ea" + }, + { + "path": "native-synthetic-oracle/oracle-run.log.gz", + "sha256": "78ef8db9e11d732893330cfd0d72510a8cdbda419d9aa3a64c66a76e3bf9383f", + "bytes": 91, + "source": "/private/tmp/cleanup-s3-native-oracle-187/oracle-run.log", + "source_sha256": "ffcc81cb4652f56f86b2a3384af1b74470dad4a472bb3a7dd533c90a711b1e85" + }, + { + "path": "native-synthetic-oracle/fmt.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/cleanup-s3-native-oracle-187/fmt.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "native-synthetic-oracle/compiled-inputs-before.json", + "sha256": "4a39d94318d40ba655dac98cace03b1f2f257868a8aa56c87b90c01d5bdce0ea", + "bytes": 48340, + "source": "/private/tmp/cleanup-s3-native-oracle-187/compiled-inputs-before.json", + "source_sha256": "4a39d94318d40ba655dac98cace03b1f2f257868a8aa56c87b90c01d5bdce0ea" + }, + { + "path": "native-synthetic-oracle/all-inputs-after.json", + "sha256": "33b30994ee8e44bee324143dda441ff76126d866e8c7dab38445251886267330", + "bytes": 81469, + "source": "/private/tmp/cleanup-s3-native-oracle-187/all-inputs-after.json", + "source_sha256": "33b30994ee8e44bee324143dda441ff76126d866e8c7dab38445251886267330" + }, + { + "path": "native-synthetic-oracle/baseline.json", + "sha256": "c34d7fbcc8bf1f2ce6ed5fe04c305c1de36f96447f31a5f352bbb5b3e837c083", + "bytes": 262, + "source": "/private/tmp/cleanup-s3-native-oracle-187/baseline.json", + "source_sha256": "c34d7fbcc8bf1f2ce6ed5fe04c305c1de36f96447f31a5f352bbb5b3e837c083" + }, + { + "path": "native-synthetic-oracle/evidence-summary.json", + "sha256": "2c9795b8d84469713938503503c206cc806be95f802ac89a735d291a59c81a46", + "bytes": 1648, + "source": "/private/tmp/cleanup-s3-native-oracle-187/evidence-summary.json", + "source_sha256": "2c9795b8d84469713938503503c206cc806be95f802ac89a735d291a59c81a46" + }, + { + "path": "native-synthetic-oracle/native-build-provenance.json", + "sha256": "02884247cfe9c559fc451c05d2c1daa459a26df7b99d7030fa1aab59c409599b", + "bytes": 1024, + "source": "/private/tmp/cleanup-s3-native-oracle-187/native-build-provenance.json", + "source_sha256": "02884247cfe9c559fc451c05d2c1daa459a26df7b99d7030fa1aab59c409599b" + }, + { + "path": "native-synthetic-oracle/build.log.gz", + "sha256": "31644db970ebd8d64958520fb04ed82761aa6fe2cfe19046353a29601b54ff21", + "bytes": 152, + "source": "/private/tmp/cleanup-s3-native-oracle-187/build.log", + "source_sha256": "a18c025d837a99e8cbe0a682d4d13fc382ad710e595a7c35de06329e93b0d5b7" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/oracle-results.json", + "sha256": "4eb7b882ae7716371093f47ded9c49795859d55ed2e87fe28fbf9049f570e972", + "bytes": 86998, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/oracle-results.json", + "source_sha256": "4eb7b882ae7716371093f47ded9c49795859d55ed2e87fe28fbf9049f570e972" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/response.json", + "sha256": "04fd93a9fae763018961f17d8e8830c1881dcb9dbabdaca84e16b2544d510cc6", + "bytes": 19350, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/response.json", + "source_sha256": "04fd93a9fae763018961f17d8e8830c1881dcb9dbabdaca84e16b2544d510cc6" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/manifest.json", + "sha256": "699a986fb18086c36af4d10da937d7ddb35a4f98417af4a49a9422ba33408be1", + "bytes": 1012, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/manifest.json", + "source_sha256": "699a986fb18086c36af4d10da937d7ddb35a4f98417af4a49a9422ba33408be1" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/artifacts/.use-cohort.json", + "sha256": "40558bf35f56d1b00c35f4d3cf0b35b70b70496144c57585b74b069c88018647", + "bytes": 282, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/artifacts/.use-cohort.json", + "source_sha256": "40558bf35f56d1b00c35f4d3cf0b35b70b70496144c57585b74b069c88018647" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/artifacts/d3/d3d4b4a0d0f832e85dd2cd181cebf491aa981aa9613ca916df5ba4c428064ace/partition.json", + "sha256": "52ce0e3eaca0b3294b93bfea03d9151035067e74c15d9767c6673b0e16b74c66", + "bytes": 37750, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/artifacts/d3/d3d4b4a0d0f832e85dd2cd181cebf491aa981aa9613ca916df5ba4c428064ace/partition.json", + "source_sha256": "52ce0e3eaca0b3294b93bfea03d9151035067e74c15d9767c6673b0e16b74c66" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/artifacts/1c/1c334c8296bd42a65f39c6150a08ba1762e632635b7f3ed6bfc36bcb680187c8/partition.json", + "sha256": "35668b9ef496faca038629ae58b8fa734a267cb9508d31b7c4e8d3889da104d5", + "bytes": 46395, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/artifacts/1c/1c334c8296bd42a65f39c6150a08ba1762e632635b7f3ed6bfc36bcb680187c8/partition.json", + "source_sha256": "35668b9ef496faca038629ae58b8fa734a267cb9508d31b7c4e8d3889da104d5" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/declares.json", + "sha256": "571664b397d6325e7cd62b3ed85b55b0c99b12e76634e139de7f33b25a6fed6c", + "bytes": 688, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/declares.json", + "source_sha256": "571664b397d6325e7cd62b3ed85b55b0c99b12e76634e139de7f33b25a6fed6c" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/function.json", + "sha256": "93fa18781827e22ecf7d6a1dedcb73b2a077b4f44293bb5a11b95536f0a461c9", + "bytes": 451, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/function.json", + "source_sha256": "93fa18781827e22ecf7d6a1dedcb73b2a077b4f44293bb5a11b95536f0a461c9" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/resolvesto.json", + "sha256": "e928d85a458a75309a37717e677a92b54af9acd89e9053eace528665340fc82a", + "bytes": 1988, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/resolvesto.json", + "source_sha256": "e928d85a458a75309a37717e677a92b54af9acd89e9053eace528665340fc82a" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/references.json", + "sha256": "15e9e14b8544c09c0f08fdb27a7c1245f45ce4cf2fee2de84fcdad0872a6b350", + "bytes": 1978, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/references.json", + "source_sha256": "15e9e14b8544c09c0f08fdb27a7c1245f45ce4cf2fee2de84fcdad0872a6b350" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/sourceroot.json", + "sha256": "cf527ae03721028e5f79ab399d1c514917f461782ac54b90aa1191846a504768", + "bytes": 715, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/sourceroot.json", + "source_sha256": "cf527ae03721028e5f79ab399d1c514917f461782ac54b90aa1191846a504768" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/syntaxcapture.json", + "sha256": "ee3307fbdffa61a853280f1ec461e1e73e5664483c7ca7d8367911cb84e5aae4", + "bytes": 12499, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/syntaxcapture.json", + "source_sha256": "ee3307fbdffa61a853280f1ec461e1e73e5664483c7ca7d8367911cb84e5aae4" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/scope.json", + "sha256": "8374d7e83b880132c300baa0115cc36024aa396f4dc8a9a47189b941364a9671", + "bytes": 1810, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/scope.json", + "source_sha256": "8374d7e83b880132c300baa0115cc36024aa396f4dc8a9a47189b941364a9671" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/symbol.json", + "sha256": "d6e411209644d10310caa36b828d1379219bed634984e387e40249e203f9a032", + "bytes": 778, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/symbol.json", + "source_sha256": "d6e411209644d10310caa36b828d1379219bed634984e387e40249e203f9a032" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/file.json", + "sha256": "e6858f4fed9e200693ac6f9898c731675f8c9c16680b010a1bf6ff883e17bfe0", + "bytes": 859, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/file.json", + "source_sha256": "e6858f4fed9e200693ac6f9898c731675f8c9c16680b010a1bf6ff883e17bfe0" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/repository.json", + "sha256": "b13dc2d1669b844dab9fe1cae02667685180508a8226f5a85338a6e0791d8cbd", + "bytes": 388, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/repository.json", + "source_sha256": "b13dc2d1669b844dab9fe1cae02667685180508a8226f5a85338a6e0791d8cbd" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/reference.json", + "sha256": "9668f1623be89926ad7f8bbcc038b096ce0867e10b15608cc7334e2b1b02d7cf", + "bytes": 1763, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/reference.json", + "source_sha256": "9668f1623be89926ad7f8bbcc038b096ce0867e10b15608cc7334e2b1b02d7cf" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/class.json", + "sha256": "224d01f3c6a80e95bef1d2aa4c7ca79eac0744827382dd2b9e789f3cdd29f35c", + "bytes": 456, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/class.json", + "source_sha256": "224d01f3c6a80e95bef1d2aa4c7ca79eac0744827382dd2b9e789f3cdd29f35c" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/defines.json", + "sha256": "f3e2ae6fff48a9f1a0ff4f6a4e87540532c6fb44ea2f5b8738dfe84a55ed37a3", + "bytes": 682, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/defines.json", + "source_sha256": "f3e2ae6fff48a9f1a0ff4f6a4e87540532c6fb44ea2f5b8738dfe84a55ed37a3" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/typeannotation.json", + "sha256": "c771e9eceda0f5d9b3245465abc24ec7972db37199ea0285cb9c010ea4287766", + "bytes": 488, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/typeannotation.json", + "source_sha256": "c771e9eceda0f5d9b3245465abc24ec7972db37199ea0285cb9c010ea4287766" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/module.json", + "sha256": "3c52fe058994eccdc2179b2da25d54c5b603632c1e263bb06cc51a44ba078de9", + "bytes": 896, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/module.json", + "source_sha256": "3c52fe058994eccdc2179b2da25d54c5b603632c1e263bb06cc51a44ba078de9" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/syntaxchild.json", + "sha256": "e1e4ec9719daf8d304abf698fe2d60bd8f2b2336e00f11a30eea9e7d1ed1503a", + "bytes": 7899, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/syntaxchild.json", + "source_sha256": "e1e4ec9719daf8d304abf698fe2d60bd8f2b2336e00f11a30eea9e7d1ed1503a" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/derivedfrom.json", + "sha256": "762bb92dbf9f6de0d5acfb550a9f4aaad0ecd2a0bea4e725b1914ea6b83b483e", + "bytes": 2536, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/derivedfrom.json", + "source_sha256": "762bb92dbf9f6de0d5acfb550a9f4aaad0ecd2a0bea4e725b1914ea6b83b483e" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/evidencedby.json", + "sha256": "08add8480e33dd36014006b2f9ef419c3fdd4a0c09d7bcb882957030cbadbc9b", + "bytes": 694, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/evidencedby.json", + "source_sha256": "08add8480e33dd36014006b2f9ef419c3fdd4a0c09d7bcb882957030cbadbc9b" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/hasscope.json", + "sha256": "00372c3fb509ad83640df2abf9425a8c6aabab93938a59b87eca2a801eabe6e3", + "bytes": 1338, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/hasscope.json", + "source_sha256": "00372c3fb509ad83640df2abf9425a8c6aabab93938a59b87eca2a801eabe6e3" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/contains.json", + "sha256": "49fb818677814cb9ee370b84509ff5b4111ae093c8d2b819980d93d84992a902", + "bytes": 8153, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/.codebaseGraph/native-staging/contains.json", + "source_sha256": "49fb818677814cb9ee370b84509ff5b4111ae093c8d2b819980d93d84992a902" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/src/unicode_\u00e9.rs", + "sha256": "3ef5ef3e668c3ba4062e4e1bbd924e183edf5d9a2063be07d2df5cb448657318", + "bytes": 36, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/src/unicode_\u00e9.rs", + "source_sha256": "3ef5ef3e668c3ba4062e4e1bbd924e183edf5d9a2063be07d2df5cb448657318" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_plain/src/alpha.rs", + "sha256": "698d4d390396111ee4db41439b4d07f689669219109bb7666ee2dac50bc75260", + "bytes": 38, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_plain/src/alpha.rs", + "source_sha256": "698d4d390396111ee4db41439b4d07f689669219109bb7666ee2dac50bc75260" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/response.json", + "sha256": "0ca06f7bb4fa789597821c71607f442f6fc048cc3867ea1d43937a48ef0c731a", + "bytes": 20275, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/response.json", + "source_sha256": "0ca06f7bb4fa789597821c71607f442f6fc048cc3867ea1d43937a48ef0c731a" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/synthetic_native_search_graph.ldb.search.meta.json", + "sha256": "9da0d68560b886b99c36f6f64df39d074f8d55a678cd3d7cdb4e8ea2c75db18e", + "bytes": 122, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/synthetic_native_search_graph.ldb.search.meta.json", + "source_sha256": "9da0d68560b886b99c36f6f64df39d074f8d55a678cd3d7cdb4e8ea2c75db18e" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/manifest.json", + "sha256": "ecfd708500697cb332948b9d4a67d9681e12539674b73ae10ae0a7159a24d645", + "bytes": 1778, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/manifest.json", + "source_sha256": "ecfd708500697cb332948b9d4a67d9681e12539674b73ae10ae0a7159a24d645" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/artifacts/.use-cohort.json", + "sha256": "11c068acfec87899d99824a6061af614432bf73dd8a0cd37099b43c383406e6c", + "bytes": 280, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/artifacts/.use-cohort.json", + "source_sha256": "11c068acfec87899d99824a6061af614432bf73dd8a0cd37099b43c383406e6c" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/artifacts/0a/0a7647dade44175c2d88aba2e0436296acd99bb3a325b701c55b9cd2bbf39735/partition.json", + "sha256": "83dcce66ab4938fcae8f208eed8f65112dcc0a401dd461043b714019987a680d", + "bytes": 46402, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/artifacts/0a/0a7647dade44175c2d88aba2e0436296acd99bb3a325b701c55b9cd2bbf39735/partition.json", + "source_sha256": "83dcce66ab4938fcae8f208eed8f65112dcc0a401dd461043b714019987a680d" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/artifacts/a9/a9f450042c187d052a88ce18969d5131f31f14bcc6f7bad4a6ae9407d94b6a61/partition.json", + "sha256": "b5131a0203490ae60011884d2e444b3deba190fbebc6ef280518272d1296da9e", + "bytes": 37757, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/artifacts/a9/a9f450042c187d052a88ce18969d5131f31f14bcc6f7bad4a6ae9407d94b6a61/partition.json", + "source_sha256": "b5131a0203490ae60011884d2e444b3deba190fbebc6ef280518272d1296da9e" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/declares.json", + "sha256": "571664b397d6325e7cd62b3ed85b55b0c99b12e76634e139de7f33b25a6fed6c", + "bytes": 688, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/declares.json", + "source_sha256": "571664b397d6325e7cd62b3ed85b55b0c99b12e76634e139de7f33b25a6fed6c" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/function.json", + "sha256": "93fa18781827e22ecf7d6a1dedcb73b2a077b4f44293bb5a11b95536f0a461c9", + "bytes": 451, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/function.json", + "source_sha256": "93fa18781827e22ecf7d6a1dedcb73b2a077b4f44293bb5a11b95536f0a461c9" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/resolvesto.json", + "sha256": "e928d85a458a75309a37717e677a92b54af9acd89e9053eace528665340fc82a", + "bytes": 1988, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/resolvesto.json", + "source_sha256": "e928d85a458a75309a37717e677a92b54af9acd89e9053eace528665340fc82a" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/references.json", + "sha256": "15e9e14b8544c09c0f08fdb27a7c1245f45ce4cf2fee2de84fcdad0872a6b350", + "bytes": 1978, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/references.json", + "source_sha256": "15e9e14b8544c09c0f08fdb27a7c1245f45ce4cf2fee2de84fcdad0872a6b350" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/sourceroot.json", + "sha256": "ebdd432c7884ed58cd4777e5f92b0e1ee2870d31d24ac3ef791b9fff47c04dac", + "bytes": 720, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/sourceroot.json", + "source_sha256": "ebdd432c7884ed58cd4777e5f92b0e1ee2870d31d24ac3ef791b9fff47c04dac" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/syntaxcapture.json", + "sha256": "ee3307fbdffa61a853280f1ec461e1e73e5664483c7ca7d8367911cb84e5aae4", + "bytes": 12499, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/syntaxcapture.json", + "source_sha256": "ee3307fbdffa61a853280f1ec461e1e73e5664483c7ca7d8367911cb84e5aae4" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/scope.json", + "sha256": "8374d7e83b880132c300baa0115cc36024aa396f4dc8a9a47189b941364a9671", + "bytes": 1810, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/scope.json", + "source_sha256": "8374d7e83b880132c300baa0115cc36024aa396f4dc8a9a47189b941364a9671" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/symbol.json", + "sha256": "d6e411209644d10310caa36b828d1379219bed634984e387e40249e203f9a032", + "bytes": 778, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/symbol.json", + "source_sha256": "d6e411209644d10310caa36b828d1379219bed634984e387e40249e203f9a032" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/file.json", + "sha256": "e6858f4fed9e200693ac6f9898c731675f8c9c16680b010a1bf6ff883e17bfe0", + "bytes": 859, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/file.json", + "source_sha256": "e6858f4fed9e200693ac6f9898c731675f8c9c16680b010a1bf6ff883e17bfe0" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/repository.json", + "sha256": "90ec8739590896f33b576317ead13e6087b5b40fae0c9681b63c70d77c42f22a", + "bytes": 392, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/repository.json", + "source_sha256": "90ec8739590896f33b576317ead13e6087b5b40fae0c9681b63c70d77c42f22a" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/reference.json", + "sha256": "9668f1623be89926ad7f8bbcc038b096ce0867e10b15608cc7334e2b1b02d7cf", + "bytes": 1763, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/reference.json", + "source_sha256": "9668f1623be89926ad7f8bbcc038b096ce0867e10b15608cc7334e2b1b02d7cf" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/class.json", + "sha256": "224d01f3c6a80e95bef1d2aa4c7ca79eac0744827382dd2b9e789f3cdd29f35c", + "bytes": 456, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/class.json", + "source_sha256": "224d01f3c6a80e95bef1d2aa4c7ca79eac0744827382dd2b9e789f3cdd29f35c" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/defines.json", + "sha256": "f3e2ae6fff48a9f1a0ff4f6a4e87540532c6fb44ea2f5b8738dfe84a55ed37a3", + "bytes": 682, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/defines.json", + "source_sha256": "f3e2ae6fff48a9f1a0ff4f6a4e87540532c6fb44ea2f5b8738dfe84a55ed37a3" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/typeannotation.json", + "sha256": "c771e9eceda0f5d9b3245465abc24ec7972db37199ea0285cb9c010ea4287766", + "bytes": 488, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/typeannotation.json", + "source_sha256": "c771e9eceda0f5d9b3245465abc24ec7972db37199ea0285cb9c010ea4287766" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/module.json", + "sha256": "3c52fe058994eccdc2179b2da25d54c5b603632c1e263bb06cc51a44ba078de9", + "bytes": 896, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/module.json", + "source_sha256": "3c52fe058994eccdc2179b2da25d54c5b603632c1e263bb06cc51a44ba078de9" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/syntaxchild.json", + "sha256": "e1e4ec9719daf8d304abf698fe2d60bd8f2b2336e00f11a30eea9e7d1ed1503a", + "bytes": 7899, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/syntaxchild.json", + "source_sha256": "e1e4ec9719daf8d304abf698fe2d60bd8f2b2336e00f11a30eea9e7d1ed1503a" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/derivedfrom.json", + "sha256": "762bb92dbf9f6de0d5acfb550a9f4aaad0ecd2a0bea4e725b1914ea6b83b483e", + "bytes": 2536, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/derivedfrom.json", + "source_sha256": "762bb92dbf9f6de0d5acfb550a9f4aaad0ecd2a0bea4e725b1914ea6b83b483e" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/evidencedby.json", + "sha256": "08add8480e33dd36014006b2f9ef419c3fdd4a0c09d7bcb882957030cbadbc9b", + "bytes": 694, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/evidencedby.json", + "source_sha256": "08add8480e33dd36014006b2f9ef419c3fdd4a0c09d7bcb882957030cbadbc9b" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/hasscope.json", + "sha256": "00372c3fb509ad83640df2abf9425a8c6aabab93938a59b87eca2a801eabe6e3", + "bytes": 1338, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/hasscope.json", + "source_sha256": "00372c3fb509ad83640df2abf9425a8c6aabab93938a59b87eca2a801eabe6e3" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/contains.json", + "sha256": "fc0519b84c29d8410c2902ee5f0608190927f0d12ce65690fffaf22dca3672e7", + "bytes": 8153, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/.codebaseGraph/native-staging/contains.json", + "source_sha256": "fc0519b84c29d8410c2902ee5f0608190927f0d12ce65690fffaf22dca3672e7" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/src/unicode_\u00e9.rs", + "sha256": "3ef5ef3e668c3ba4062e4e1bbd924e183edf5d9a2063be07d2df5cb448657318", + "bytes": 36, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/src/unicode_\u00e9.rs", + "source_sha256": "3ef5ef3e668c3ba4062e4e1bbd924e183edf5d9a2063be07d2df5cb448657318" + }, + { + "path": "native-synthetic-oracle/synthetic-fixtures/synthetic_native_search/src/alpha.rs", + "sha256": "698d4d390396111ee4db41439b4d07f689669219109bb7666ee2dac50bc75260", + "bytes": 38, + "source": "/private/tmp/cleanup-s3-native-oracle-187/synthetic-fixtures/synthetic_native_search/src/alpha.rs", + "source_sha256": "698d4d390396111ee4db41439b4d07f689669219109bb7666ee2dac50bc75260" + }, + { + "path": "native-synthetic-introspection/compiled-inputs-after.json", + "sha256": "c0c0fee48fbf638b9af6c0f4a7c6ac59ddcceb783e68ba37a7c7150d048505d1", + "bytes": 43907, + "source": "/private/tmp/dc-s3-native-introspection/compiled-inputs-after.json", + "source_sha256": "c0c0fee48fbf638b9af6c0f4a7c6ac59ddcceb783e68ba37a7c7150d048505d1" + }, + { + "path": "native-synthetic-introspection/run.log.gz", + "sha256": "d46c407518f39eb7581679492dc436da74dc34dce0e886351f28d8081df0367e", + "bytes": 1085, + "source": "/private/tmp/dc-s3-native-introspection/run.log", + "source_sha256": "c3feb67409ff3b2b35b0327509567c4f497e5cd97b5310dbe15e0848c623097e" + }, + { + "path": "native-synthetic-introspection/database-bytes-before.json", + "sha256": "8d754acd9961de7ccdaa8387e3d7ed4a9455f0c8586a0c3ad8d7995b889dd0af", + "bytes": 479, + "source": "/private/tmp/dc-s3-native-introspection/database-bytes-before.json", + "source_sha256": "8d754acd9961de7ccdaa8387e3d7ed4a9455f0c8586a0c3ad8d7995b889dd0af" + }, + { + "path": "native-synthetic-introspection/compiled-inputs-before.json", + "sha256": "c0c0fee48fbf638b9af6c0f4a7c6ac59ddcceb783e68ba37a7c7150d048505d1", + "bytes": 43907, + "source": "/private/tmp/dc-s3-native-introspection/compiled-inputs-before.json", + "source_sha256": "c0c0fee48fbf638b9af6c0f4a7c6ac59ddcceb783e68ba37a7c7150d048505d1" + }, + { + "path": "native-synthetic-introspection/evidence-summary.json", + "sha256": "1372d883f39e965ee60e8bcfcaf8e05f31eff766d03595d77d46b6229451d99b", + "bytes": 919, + "source": "/private/tmp/dc-s3-native-introspection/evidence-summary.json", + "source_sha256": "1372d883f39e965ee60e8bcfcaf8e05f31eff766d03595d77d46b6229451d99b" + }, + { + "path": "native-synthetic-introspection/probe.rs", + "sha256": "fa95aa79e14013ad3a626469470f31ebf76504bb913ed6bd76ebb748820f9b16", + "bytes": 706, + "source": "/private/tmp/dc-s3-native-introspection/probe.rs", + "source_sha256": "fa95aa79e14013ad3a626469470f31ebf76504bb913ed6bd76ebb748820f9b16" + }, + { + "path": "native-synthetic-introspection/database-bytes-after.json", + "sha256": "8d754acd9961de7ccdaa8387e3d7ed4a9455f0c8586a0c3ad8d7995b889dd0af", + "bytes": 479, + "source": "/private/tmp/dc-s3-native-introspection/database-bytes-after.json", + "source_sha256": "8d754acd9961de7ccdaa8387e3d7ed4a9455f0c8586a0c3ad8d7995b889dd0af" + }, + { + "path": "native-synthetic-introspection/build.log.gz", + "sha256": "9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64", + "bytes": 20, + "source": "/private/tmp/dc-s3-native-introspection/build.log", + "source_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "proposal-v2/dc-s3-native-selected-grammar-addendum-20261021.md", + "sha256": "718c64e4b6929b27786fb820d57eff527ef414ecfac4778eea344f35b2df17bb", + "bytes": 7547, + "source": "/private/tmp/dc-s3-native-selected-grammar-addendum-20261021.md" + }, + { + "path": "proposal-v2/dc-s3-native-header-facts-boundary-20261021.md", + "sha256": "543b38f739b4293710abb83be97137d780e90f81f255e2b24e9c25564d81782d", + "bytes": 3090, + "source": "/private/tmp/dc-s3-native-header-facts-boundary-20261021.md" + }, + { + "path": "proposal-v2/dc-s3-native-selected-grammar-20261021.md", + "sha256": "14dc15f873db82b0b645f358763b51d5a5e487dadd34ca470c9f272d57c20b0b", + "bytes": 14122, + "source": "/private/tmp/dc-s3-native-selected-grammar-20261021.md" + }, + { + "path": "proposal-v2/dc-s3-native-selected-layout-20261021.md", + "sha256": "890894d186eeec1728d9fba2b5849c065c9533ab6413289188a4b73655a36875", + "bytes": 11609, + "source": "/private/tmp/dc-s3-native-selected-layout-20261021.md" + }, + { + "path": "proposal-v2/s4_managed_primary_result_review.md", + "sha256": "2a991ae1d2bea8fa8327566ab8b54d5553457e62c4fe834d0585a3dff8d6712c", + "bytes": 12212, + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s4_managed_primary_result_review.md" + }, + { + "path": "proposal-v2/s4_managed_addendum_verification.md", + "sha256": "2ccb21a7f0e2dc1f2bd28c96380aaa1bd5f3ead03053ce61dbb28935bb70a10e", + "bytes": 2600, + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s4_managed_addendum_verification.md" + }, + { + "path": "proposal-v2/s3_full_native_facts_visitor_review.md", + "sha256": "486ac8db98f62bbc1fe840b3aeeded3668ff1fc3e69a134eed03dc2d7af45c22", + "bytes": 12919, + "source": "/private/tmp/cleanup-v2-proposal-review-Ox0SyW/s3_full_native_facts_visitor_review.md" + }, + { + "path": "proposal-v2/dc-s4-managed-primary-result-addendum-20261021.md", + "sha256": "84e6c1f6f5a3e70bb1d0d3db721efe9de2cd46cca80562439328153ed038ab0c", + "bytes": 9168, + "source": "/private/tmp/dc-s4-managed-primary-result-addendum-20261021.md" + }, + { + "path": "proposal-v2/dc-s4-managed-primary-result-proposal-20261021.md", + "sha256": "6753f5d586f719ec39e894c1cbdcdacd19c273cfad3503b5a8f77c3d335de085", + "bytes": 18894, + "source": "/private/tmp/dc-s4-managed-primary-result-proposal-20261021.md" + } + ] +} diff --git a/docs/testing/pr153-review-fixes/README.md b/docs/testing/pr153-review-fixes/README.md new file mode 100644 index 00000000..cffc60bf --- /dev/null +++ b/docs/testing/pr153-review-fixes/README.md @@ -0,0 +1,54 @@ +# PR #153 review follow-up + +Baseline: `6e1543b038961261555d35e55a4095afae02392e`. Scope: DC-S3/DC-S4, RAB-8/RAB-9; canonical wiki `architecture/graph-disk-cleanup-contract`, approved revision 0.2. Scryer bugfix ledger `chg-ndq0yv`. User request: address PR #153 comments. + +## Changes and acceptance + +- discussion_r4372938440: inapplicable Direct legacy confirmation is discarded at request/startup admission before source handles are captured; it no longer blocks modern candidate selection/recovery. A real interrupted Direct metadata transaction recovers through startup, request(true/false), and confirm-and-wait; Managed confirmation remains deferred until enrollment. +- discussion_r4372938443: advisory legacy-generation discovery completes beyond 100 entries, retaining fixed scratch and one selected name. The regression scans 153 entries twice, excludes active/origin-marked/unknown entries, preserves their bytes, and verifies per-entry cancellation. +- Removed the advisory literal 100-entry cutoff only. Authoritative 100-entry/100-ms mutation budget, ownership checks, retention rules and public contracts are unchanged. Full advisory scanning is permitted by approved revision 0.2. +- Independent review found the expanded scan needed cancellation checks and the initial candidate-only bypass could retain unnecessary confirmation payload handles. Both were fixed: per-entry cancellation and ingress filtering with the original candidate guard restored. No shutdown deadline is claimed. + +## Executed evidence + +Darwin ARM64, rustc/cargo 1.96.0, native-tests profile, shared Cargo target with fresh compilation from this worktree. Source SHA-256: + +- `src/storage/maintenance.rs`: `061191affdacb910abe21070c25ae3cc2979db2f87ea0127274dce61e1e19c9e` +- `src/storage/maintenance_candidate.rs`: `41917895967128bce8c7a5a3b4b6ad4ba55a2398232b8a3239c27e46795f47ba` +- `src/storage/maintenance_legacy_generations_engine.rs`: `35fd9ff8a8c198226844664605b3dba54038b95d908554912200ef238e17642a` + +Both new regressions failed against the original behavior; baseline logs are retained here. The earlier Direct fixture setup attempt lacked tracked fresh ownership; that fixture was corrected before the recorded meaningful baseline failure. + +- Intermediate pre-ingress snapshot: narrow 7/7 and broad 30/30 passed. These overlapping selections do not qualify the final ingress revision. Final results are recorded below. +- `cargo fmt --all -- --check`: passed. +- `cargo clippy --offline --locked --workspace --all-targets -- -D warnings`: passed after final source edits. +- `git diff --check`: passed. + +Commands: + +```text +CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo nextest run --config-file /private/tmp/pr153-nextest.toml --profile pr153 --cargo-profile native-tests -p codebase-graph --lib -E 'test(storage::maintenance_candidate::tests) or test(storage::maintenance_legacy_generations_engine::tests)' --no-fail-fast +CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo nextest run --config-file /private/tmp/pr153-nextest-broad.toml --profile pr153 --cargo-profile native-tests -p codebase-graph --lib -E 'test(storage::maintenance_candidate::tests) or test(storage::maintenance_legacy_)' --test-threads 1 --no-fail-fast +``` + +JUnit, baseline/final logs and Nextest configs are adjacent. Independent reviewer: fresh GPT-6.1 Sol, Max effort; see `review.md` for final snapshot and outcome. + +## Limits and handoff + +No full-suite, native Windows, live migration or rollout qualification claimed. Legacy regression proves shipping discovery selection/nonmutation, not complete large-backlog retirement. CI remains separate. Maintainer owns aggregate closure. + +Linear unavailable; RAB-8/RAB-9 synchronization pending. Graph discovery was attempted through configured MCP and local CLI; configured graph is older and worktree graph DB is absent. Correct original implementation Scryer project was read; claims remain unchanged. Final report ingested: verdicts recorded for two existing claims; 45 cases had no attached test match. Empty bugfix ledger closed with rationale; no model intent changed. Its removed/mismatched source checkout has preexisting broken anchors/stale verdicts; global Scryer reconciliation is not claimed. No unrelated state was changed to hide those limits. + +Reusable-memory assessment: no new memory warranted. Existing approved contract already covers fair progress, advisory budgeting and cancellation; task-specific evidence belongs here. + +## Final ingress snapshot verification + +Final serial native Nextest: **50/50 passed**, zero failures/errors; 790 tests excluded by filter. Run `f58f802c-25fd-401e-b79e-ec18806a307f`, 95.603s. Includes real Direct ingress/recovery, Managed pending confirmation, large legacy discovery/cancellation, cursor and scheduler/source-capture tests. The ingress test includes one existing 60s scheduler cooldown; no product interval changed. + +```text +CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo nextest run --config-file /private/tmp/pr153-nextest-final.toml --profile pr153 --cargo-profile native-tests -p codebase-graph --lib -E 'test(storage::maintenance::tests) or test(storage::maintenance_candidate::tests) or test(storage::maintenance_legacy_) or test(storage::maintenance_scheduler::)' --test-threads 1 --no-fail-fast +``` + +`pr153-review-final-junit.xml`, `pr153-review-final.log`, `pr153-nextest-final.toml` identify final executed evidence. `pr153-ingress-clippy.log` is the final workspace/all-targets warnings-denied check. `pr153-ingress-baseline.log` reproduces the actual request-path failure with only the three ingress behaviors restored; final source was restored before execution. Earlier candidate-only baseline and 7/30-case logs are intermediate evidence, not final-source qualification. + +Text logs have trailing whitespace and final blank lines normalized for Git; JUnit bytes remain unchanged. diff --git a/docs/testing/pr153-review-fixes/pr153-direct-baseline.log b/docs/testing/pr153-review-fixes/pr153-direct-baseline.log new file mode 100644 index 00000000..36cc65b4 --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-direct-baseline.log @@ -0,0 +1,22 @@ + Compiling codebase-graph v3.0.4 (/private/tmp/codebasegraph-pr153-review) + Finished `native-tests` profile [optimized] target(s) in 47.89s + Running unittests src/lib.rs (/Users/rabii/Projects/Repositories/codebaseGraph/target/native-tests/deps/codebase_graph-35351ef9068f9494) + +running 1 test +test storage::maintenance_candidate::tests::direct_confirmation_does_not_block_modern_metadata_recovery ... +thread 'storage::maintenance_candidate::tests::direct_confirmation_does_not_block_modern_metadata_recovery' (33055054) panicked at src/storage/maintenance_admission.rs:1990:21: +fixture durable metadata write cut +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +thread 'storage::maintenance_candidate::tests::direct_confirmation_does_not_block_modern_metadata_recovery' (33055054) panicked at src/storage/maintenance_candidate.rs:2021:24: +inapplicable confirmation must not starve Direct recovery +FAILED + +failures: + +failures: + storage::maintenance_candidate::tests::direct_confirmation_does_not_block_modern_metadata_recovery + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 838 filtered out; finished in 0.20s + +error: test failed, to rerun pass `-p codebase-graph --lib` diff --git a/docs/testing/pr153-review-fixes/pr153-ingress-baseline.log b/docs/testing/pr153-review-fixes/pr153-ingress-baseline.log new file mode 100644 index 00000000..c84be468 --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-ingress-baseline.log @@ -0,0 +1,24 @@ + Compiling codebase-graph v3.0.4 (/private/tmp/codebasegraph-pr153-review) + Finished `native-tests` profile [optimized] target(s) in 54.29s + Running unittests src/lib.rs (/Users/rabii/Projects/Repositories/codebaseGraph/target/native-tests/deps/codebase_graph-35351ef9068f9494) + +running 1 test +test storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery ... +thread 'storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery' (33070128) panicked at src/storage/maintenance_admission.rs:1990:21: +fixture durable metadata write cut +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +thread 'storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery' (33070128) panicked at src/storage/maintenance.rs:257:9: +assertion `left == right` failed + left: Some(CommitFact { generation: 0, active: 0, slot_digest: [177, 149, 157, 20, 48, 168, 234, 253, 68, 252, 151, 173, 191, 204, 88, 206, 86, 95, 43, 189, 72, 206, 66, 13, 45, 126, 136, 149, 140, 207, 198, 56] }) + right: Some(CommitFact { generation: 1, active: 1, slot_digest: [172, 98, 129, 185, 164, 202, 219, 152, 13, 200, 76, 119, 82, 179, 78, 98, 72, 106, 15, 202, 170, 173, 234, 25, 31, 151, 152, 1, 246, 243, 245, 213] }) +FAILED + +failures: + +failures: + storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 839 filtered out; finished in 0.20s + +error: test failed, to rerun pass `-p codebase-graph --lib` diff --git a/docs/testing/pr153-review-fixes/pr153-ingress-clippy.log b/docs/testing/pr153-review-fixes/pr153-ingress-clippy.log new file mode 100644 index 00000000..74e17afd --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-ingress-clippy.log @@ -0,0 +1,3 @@ + Checking codebase-graph v3.0.4 (/private/tmp/codebasegraph-pr153-review) + Checking k-wiki v3.0.4 (/private/tmp/codebasegraph-pr153-review/crates/k-wiki) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 18.41s diff --git a/docs/testing/pr153-review-fixes/pr153-ingress-positive.log b/docs/testing/pr153-review-fixes/pr153-ingress-positive.log new file mode 100644 index 00000000..4bafa52c --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-ingress-positive.log @@ -0,0 +1,15 @@ + Compiling codebase-graph v3.0.4 (/private/tmp/codebasegraph-pr153-review) + Finished `native-tests` profile [optimized] target(s) in 53.27s + Running unittests src/lib.rs (/Users/rabii/Projects/Repositories/codebaseGraph/target/native-tests/deps/codebase_graph-35351ef9068f9494) + +running 1 test +test storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery ... +thread 'storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery' (33067449) panicked at src/storage/maintenance_admission.rs:1990:21: +fixture durable metadata write cut +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +thread 'storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery' (33067449) panicked at src/storage/maintenance_admission.rs:1990:21: +fixture durable metadata write cut +ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 839 filtered out; finished in 120.26s diff --git a/docs/testing/pr153-review-fixes/pr153-legacy-baseline.log b/docs/testing/pr153-review-fixes/pr153-legacy-baseline.log new file mode 100644 index 00000000..ace1736b --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-legacy-baseline.log @@ -0,0 +1,18 @@ + Finished `native-tests` profile [optimized] target(s) in 0.08s + Running unittests src/lib.rs (/Users/rabii/Projects/Repositories/codebaseGraph/target/native-tests/deps/codebase_graph-35351ef9068f9494) + +running 1 test +test storage::maintenance_legacy_generations_engine::tests::legacy_generation_advisory_scan_exceeds_one_hundred_entries ... +thread 'storage::maintenance_legacy_generations_engine::tests::legacy_generation_advisory_scan_exceeds_one_hundred_entries' (33053250) panicked at src/storage/maintenance_legacy_generations_engine.rs:2304:14: +called `Result::unwrap()` on an `Err` value: Kind(WouldBlock) +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +FAILED + +failures: + +failures: + storage::maintenance_legacy_generations_engine::tests::legacy_generation_advisory_scan_exceeds_one_hundred_entries + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 838 filtered out; finished in 0.02s + +error: test failed, to rerun pass `-p codebase-graph --lib` diff --git a/docs/testing/pr153-review-fixes/pr153-nextest-broad.toml b/docs/testing/pr153-review-fixes/pr153-nextest-broad.toml new file mode 100644 index 00000000..d07fa4f2 --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-nextest-broad.toml @@ -0,0 +1,4 @@ +[profile.pr153] +inherits = "default" +[profile.pr153.junit] +path = "/private/tmp/pr153-review-broad-junit.xml" diff --git a/docs/testing/pr153-review-fixes/pr153-nextest-final.toml b/docs/testing/pr153-review-fixes/pr153-nextest-final.toml new file mode 100644 index 00000000..4a5cab5a --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-nextest-final.toml @@ -0,0 +1,4 @@ +[profile.pr153] +inherits = "default" +[profile.pr153.junit] +path = "/private/tmp/pr153-review-final-junit.xml" diff --git a/docs/testing/pr153-review-fixes/pr153-nextest.toml b/docs/testing/pr153-review-fixes/pr153-nextest.toml new file mode 100644 index 00000000..ef972365 --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-nextest.toml @@ -0,0 +1,4 @@ +[profile.pr153] +inherits = "default" +[profile.pr153.junit] +path = "/private/tmp/pr153-review-junit.xml" diff --git a/docs/testing/pr153-review-fixes/pr153-review-broad-junit.xml b/docs/testing/pr153-review-fixes/pr153-review-broad-junit.xml new file mode 100644 index 00000000..66cc71d8 --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-review-broad-junit.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/pr153-review-fixes/pr153-review-broad.log b/docs/testing/pr153-review-fixes/pr153-review-broad.log new file mode 100644 index 00000000..8b3ea7dd --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-review-broad.log @@ -0,0 +1,36 @@ + Finished `native-tests` profile [optimized] target(s) in 0.12s +──────────── + Nextest run ID 5722dd8f-be72-4df1-85a8-631ebc8c57f0 with nextest profile: pr153 + Starting 30 tests across 1 binary (810 tests skipped) + PASS [ 0.018s] ( 1/30) codebase-graph storage::maintenance_candidate::tests::candidate_reason_codes_are_fixed_and_reports_do_not_invent_measurements + PASS [ 0.280s] ( 2/30) codebase-graph storage::maintenance_candidate::tests::candidate_recovers_interrupted_role_control_before_parsing_legacy_payload + PASS [ 0.252s] ( 3/30) codebase-graph storage::maintenance_candidate::tests::direct_confirmation_does_not_block_modern_metadata_recovery + PASS [ 0.109s] ( 4/30) codebase-graph storage::maintenance_candidate::tests::managed_confirmation_remains_pending_until_legacy_enrollment + PASS [ 0.019s] ( 5/30) codebase-graph storage::maintenance_candidate::tests::prepared_candidate_crossings_keep_large_state_indirect + PASS [ 0.016s] ( 6/30) codebase-graph storage::maintenance_candidate::tests::retirement_control_roundtrip_does_not_certify_whole_pass + PASS [ 0.019s] ( 7/30) codebase-graph storage::maintenance_legacy_association::checkpoint_tests::dc_legacy_fixed_certificate_rejects_reserved_and_torn_frames + PASS [ 0.018s] ( 8/30) codebase-graph storage::maintenance_legacy_association::checkpoint_tests::dc_legacy_streaming_native_schema_matches_current_assets + PASS [ 4.501s] ( 9/30) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_actual_native_association_restart_and_backup_keep_three_payload_fds + PASS [ 3.470s] (10/30) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_cache_change_after_certification_invalidates_final_join + PASS [ 6.607s] (11/30) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_database_catalog_and_source_root_substitution_are_unknown + PASS [ 3.459s] (12/30) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_durable_checkpoint_resumes_after_one_payload_without_relabeling_epoch + PASS [ 3.122s] (13/30) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_manifest_and_cache_substitution_never_mint_association + PASS [ 3.240s] (14/30) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_original_epoch_cancellation_and_cap_fail_closed + PASS [ 3.366s] (15/30) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_real_public_producer_without_receipts_has_complete_native_association + PASS [ 0.018s] (16/30) codebase-graph storage::maintenance_legacy_bootstrap::cohort_preparation_layout_tests::accepted_administrative_cohort_keeps_binding_image_out_of_fixed_owner_layout + PASS [ 0.016s] (17/30) codebase-graph storage::maintenance_legacy_bootstrap::retained_source_observation_layout_tests::retained_source_observer_keeps_snapshot_image_out_of_fixed_owner_layout + PASS [ 0.118s] (18/30) codebase-graph storage::maintenance_legacy_bootstrap::source_recovery_tests::dc_legacy_staging_owned_commit_recovers_without_another_confirmation + PASS [ 0.098s] (19/30) codebase-graph storage::maintenance_legacy_bootstrap::source_recovery_tests::dc_legacy_staging_owned_intent_recovers_without_another_confirmation + PASS [ 0.099s] (20/30) codebase-graph storage::maintenance_legacy_bootstrap::source_recovery_tests::dc_legacy_staging_owned_slot_recovers_without_another_confirmation + PASS [ 0.017s] (21/30) codebase-graph storage::maintenance_legacy_bootstrap::staging_scope_layout_tests::prepared_staging_scope_keeps_binding_and_scratch_images_boxed + PASS [ 0.097s] (22/30) codebase-graph storage::maintenance_legacy_bootstrap::tests::dc_legacy_real_dispatcher_claim_closes_and_aborts_only_staging_scope + PASS [ 0.101s] (23/30) codebase-graph storage::maintenance_legacy_bootstrap::tests::dc_legacy_real_dispatcher_claim_enrolls_and_explicit_rebuild_invalidates_original_owner + PASS [ 0.043s] (24/30) codebase-graph storage::maintenance_legacy_generations_engine::tests::legacy_generation_advisory_scan_exceeds_one_hundred_entries + PASS [ 0.016s] (25/30) codebase-graph storage::maintenance_legacy_guard::source_observation_layout_tests::prepared_source_observation_keeps_snapshot_and_recovery_images_boxed + PASS [ 0.035s] (26/30) codebase-graph storage::maintenance_legacy_guard::tests::dc_legacy_staging_closed_record_refuses_new_actual_producer + PASS [ 0.016s] (27/30) codebase-graph storage::maintenance_legacy_guard::tests::dc_legacy_staging_custom_root_is_not_enrolled_or_created + PASS [ 0.021s] (28/30) codebase-graph storage::maintenance_legacy_guard::tests::dc_legacy_staging_retains_actual_shared_gate_through_arc_result_lifetime + PASS [ 5.554s] (29/30) codebase-graph storage::maintenance_legacy_staging::tests::dc_staging_actual_public_copy_json_csv_profiles_and_streamed_certificates + PASS [ 0.019s] (30/30) codebase-graph storage::maintenance_legacy_staging::tests::dc_staging_filenames_select_only_exact_known_profiles +──────────── + Summary [ 34.768s] 30 tests run: 30 passed, 810 skipped diff --git a/docs/testing/pr153-review-fixes/pr153-review-clippy-final.log b/docs/testing/pr153-review-fixes/pr153-review-clippy-final.log new file mode 100644 index 00000000..1b8426ab --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-review-clippy-final.log @@ -0,0 +1,3 @@ + Checking codebase-graph v3.0.4 (/private/tmp/codebasegraph-pr153-review) + Checking k-wiki v3.0.4 (/private/tmp/codebasegraph-pr153-review/crates/k-wiki) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 18.32s diff --git a/docs/testing/pr153-review-fixes/pr153-review-final-junit.xml b/docs/testing/pr153-review-fixes/pr153-review-final-junit.xml new file mode 100644 index 00000000..0034a128 --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-review-final-junit.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/pr153-review-fixes/pr153-review-final.log b/docs/testing/pr153-review-fixes/pr153-review-final.log new file mode 100644 index 00000000..3c1e9419 --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-review-final.log @@ -0,0 +1,58 @@ + Compiling codebase-graph v3.0.4 (/private/tmp/codebasegraph-pr153-review) + Finished `native-tests` profile [optimized] target(s) in 59.32s +──────────── + Nextest run ID f58f802c-25fd-401e-b79e-ec18806a307f with nextest profile: pr153 + Starting 50 tests across 1 binary (790 tests skipped) + PASS [ 0.020s] ( 1/50) codebase-graph storage::maintenance::tests::dc_cursor_codec_corruption_and_recreated_root + PASS [ 0.017s] ( 2/50) codebase-graph storage::maintenance::tests::dc_cursor_full_generation_address_and_short_adapter + PASS [ 0.023s] ( 3/50) codebase-graph storage::maintenance::tests::dc_cursor_restart_blocked_first_and_category_rotation + PASS [ 0.017s] ( 4/50) codebase-graph storage::maintenance::tests::dc_cursor_wrap_and_all_categories + PASS [ 0.021s] ( 5/50) codebase-graph storage::maintenance::tests::dc_measurement_foreign_json_is_unknown_not_retained_coordination + PASS [ 0.019s] ( 6/50) codebase-graph storage::maintenance::tests::dc_measurement_untraversed_subtree_has_unknown_sizes + SLOW [> 60.000s] (─────) codebase-graph storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery + PASS [ 60.230s] ( 7/50) codebase-graph storage::maintenance::tests::direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery + PASS [ 0.023s] ( 8/50) codebase-graph storage::maintenance_candidate::tests::candidate_reason_codes_are_fixed_and_reports_do_not_invent_measurements + PASS [ 0.252s] ( 9/50) codebase-graph storage::maintenance_candidate::tests::candidate_recovers_interrupted_role_control_before_parsing_legacy_payload + PASS [ 0.112s] (10/50) codebase-graph storage::maintenance_candidate::tests::managed_confirmation_remains_pending_until_legacy_enrollment + PASS [ 0.020s] (11/50) codebase-graph storage::maintenance_candidate::tests::prepared_candidate_crossings_keep_large_state_indirect + PASS [ 0.017s] (12/50) codebase-graph storage::maintenance_candidate::tests::retirement_control_roundtrip_does_not_certify_whole_pass + PASS [ 0.022s] (13/50) codebase-graph storage::maintenance_legacy_association::checkpoint_tests::dc_legacy_fixed_certificate_rejects_reserved_and_torn_frames + PASS [ 0.018s] (14/50) codebase-graph storage::maintenance_legacy_association::checkpoint_tests::dc_legacy_streaming_native_schema_matches_current_assets + PASS [ 4.175s] (15/50) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_actual_native_association_restart_and_backup_keep_three_payload_fds + PASS [ 3.482s] (16/50) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_cache_change_after_certification_invalidates_final_join + PASS [ 6.898s] (17/50) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_database_catalog_and_source_root_substitution_are_unknown + PASS [ 3.470s] (18/50) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_durable_checkpoint_resumes_after_one_payload_without_relabeling_epoch + PASS [ 3.298s] (19/50) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_manifest_and_cache_substitution_never_mint_association + PASS [ 3.414s] (20/50) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_original_epoch_cancellation_and_cap_fail_closed + PASS [ 3.357s] (21/50) codebase-graph storage::maintenance_legacy_association::tests::dc_legacy_real_public_producer_without_receipts_has_complete_native_association + PASS [ 0.020s] (22/50) codebase-graph storage::maintenance_legacy_bootstrap::cohort_preparation_layout_tests::accepted_administrative_cohort_keeps_binding_image_out_of_fixed_owner_layout + PASS [ 0.017s] (23/50) codebase-graph storage::maintenance_legacy_bootstrap::retained_source_observation_layout_tests::retained_source_observer_keeps_snapshot_image_out_of_fixed_owner_layout + PASS [ 0.119s] (24/50) codebase-graph storage::maintenance_legacy_bootstrap::source_recovery_tests::dc_legacy_staging_owned_commit_recovers_without_another_confirmation + PASS [ 0.100s] (25/50) codebase-graph storage::maintenance_legacy_bootstrap::source_recovery_tests::dc_legacy_staging_owned_intent_recovers_without_another_confirmation + PASS [ 0.103s] (26/50) codebase-graph storage::maintenance_legacy_bootstrap::source_recovery_tests::dc_legacy_staging_owned_slot_recovers_without_another_confirmation + PASS [ 0.019s] (27/50) codebase-graph storage::maintenance_legacy_bootstrap::staging_scope_layout_tests::prepared_staging_scope_keeps_binding_and_scratch_images_boxed + PASS [ 0.098s] (28/50) codebase-graph storage::maintenance_legacy_bootstrap::tests::dc_legacy_real_dispatcher_claim_closes_and_aborts_only_staging_scope + PASS [ 0.105s] (29/50) codebase-graph storage::maintenance_legacy_bootstrap::tests::dc_legacy_real_dispatcher_claim_enrolls_and_explicit_rebuild_invalidates_original_owner + PASS [ 0.045s] (30/50) codebase-graph storage::maintenance_legacy_generations_engine::tests::legacy_generation_advisory_scan_exceeds_one_hundred_entries + PASS [ 0.018s] (31/50) codebase-graph storage::maintenance_legacy_guard::source_observation_layout_tests::prepared_source_observation_keeps_snapshot_and_recovery_images_boxed + PASS [ 0.032s] (32/50) codebase-graph storage::maintenance_legacy_guard::tests::dc_legacy_staging_closed_record_refuses_new_actual_producer + PASS [ 0.018s] (33/50) codebase-graph storage::maintenance_legacy_guard::tests::dc_legacy_staging_custom_root_is_not_enrolled_or_created + PASS [ 0.022s] (34/50) codebase-graph storage::maintenance_legacy_guard::tests::dc_legacy_staging_retains_actual_shared_gate_through_arc_result_lifetime + PASS [ 5.587s] (35/50) codebase-graph storage::maintenance_legacy_staging::tests::dc_staging_actual_public_copy_json_csv_profiles_and_streamed_certificates + PASS [ 0.019s] (36/50) codebase-graph storage::maintenance_legacy_staging::tests::dc_staging_filenames_select_only_exact_known_profiles + PASS [ 0.029s] (37/50) codebase-graph storage::maintenance_scheduler::allocation_boundary_test::dc_driver_real_requested_allocations_fit_tight_ceiling_and_aligned_factory + PASS [ 0.017s] (38/50) codebase-graph storage::maintenance_scheduler::independent_review_probes::dc_driver_destructor_panic_preserves_success_and_runtime_drop_can_join_while_unwinding + PASS [ 0.018s] (39/50) codebase-graph storage::maintenance_scheduler::independent_review_probes::dc_driver_factory_panic_records_failure_without_inventing_attempt + PASS [ 0.019s] (40/50) codebase-graph storage::maintenance_scheduler::independent_review_probes::independent_outer_panic_preserves_prior_success + PASS [ 0.017s] (41/50) codebase-graph storage::maintenance_scheduler::independent_review_probes::independent_stopping_lasts_until_driver_disposal_finishes + PASS [ 0.017s] (42/50) codebase-graph storage::maintenance_scheduler::panic_payload_probe::independent_panicking_factory_payload_publishes_terminal_failure + PASS [ 0.018s] (43/50) codebase-graph storage::maintenance_scheduler::spawn_failure_test::dc_driver_spawn_failure_releases_all_startup_reservations_without_factory + PASS [ 0.021s] (44/50) codebase-graph storage::maintenance_scheduler::tests::dc_driver_confirmation_arriving_inflight_survives_prior_completion + PASS [ 0.019s] (45/50) codebase-graph storage::maintenance_scheduler::tests::dc_driver_one_account_covers_rebind_snapshots_and_retained_handle_lifetime + PASS [ 0.123s] (46/50) codebase-graph storage::maintenance_scheduler::tests::dc_driver_resolved_startup_binding_runs_actual_dispatcher_without_default_cooldown + PASS [ 0.018s] (47/50) codebase-graph storage::maintenance_scheduler::tests::dc_driver_transient_wait_observes_completed_disposal_and_factory_failure + PASS [ 0.017s] (48/50) codebase-graph storage::maintenance_scheduler::tests::dc_scheduler_idle_busy_error_and_continuation_are_paced + PASS [ 0.016s] (49/50) codebase-graph storage::maintenance_scheduler::tests::dc_scheduler_sustained_requests_do_not_slide_due_or_lose_inflight_demand + PASS [ 0.023s] (50/50) codebase-graph storage::maintenance_scheduler::tests::queued_confirmation_keeps_request_time_source_after_pair_replacement +──────────── + Summary [ 95.603s] 50 tests run: 50 passed (1 slow), 790 skipped diff --git a/docs/testing/pr153-review-fixes/pr153-review-narrow-junit.xml b/docs/testing/pr153-review-fixes/pr153-review-narrow-junit.xml new file mode 100644 index 00000000..b33f2d6c --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-review-narrow-junit.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/testing/pr153-review-fixes/pr153-review-narrow.log b/docs/testing/pr153-review-fixes/pr153-review-narrow.log new file mode 100644 index 00000000..b7e87aeb --- /dev/null +++ b/docs/testing/pr153-review-fixes/pr153-review-narrow.log @@ -0,0 +1,14 @@ + Compiling codebase-graph v3.0.4 (/private/tmp/codebasegraph-pr153-review) + Finished `native-tests` profile [optimized] target(s) in 1m 01s +──────────── + Nextest run ID fa216d78-5b4d-40ed-bab7-71aa0c0e3fa0 with nextest profile: pr153 + Starting 7 tests across 1 binary (833 tests skipped) + PASS [ 0.027s] (1/7) codebase-graph storage::maintenance_candidate::tests::retirement_control_roundtrip_does_not_certify_whole_pass + PASS [ 0.026s] (2/7) codebase-graph storage::maintenance_candidate::tests::prepared_candidate_crossings_keep_large_state_indirect + PASS [ 0.027s] (3/7) codebase-graph storage::maintenance_candidate::tests::candidate_reason_codes_are_fixed_and_reports_do_not_invent_measurements + PASS [ 0.337s] (4/7) codebase-graph storage::maintenance_candidate::tests::managed_confirmation_remains_pending_until_legacy_enrollment + PASS [ 0.534s] (5/7) codebase-graph storage::maintenance_legacy_generations_engine::tests::legacy_generation_advisory_scan_exceeds_one_hundred_entries + PASS [ 0.534s] (6/7) codebase-graph storage::maintenance_candidate::tests::direct_confirmation_does_not_block_modern_metadata_recovery + PASS [ 0.579s] (7/7) codebase-graph storage::maintenance_candidate::tests::candidate_recovers_interrupted_role_control_before_parsing_legacy_payload +──────────── + Summary [ 0.582s] 7 tests run: 7 passed, 833 skipped diff --git a/docs/testing/pr153-review-fixes/review.md b/docs/testing/pr153-review-fixes/review.md new file mode 100644 index 00000000..2d275728 --- /dev/null +++ b/docs/testing/pr153-review-fixes/review.md @@ -0,0 +1,60 @@ +# PR 153 scoped independent review + +**Outcome:** No remaining confirmed P0–P3 defects in the final reviewed snapshot. Both P2 issues found during review are corrected and verified. Proceed within this scoped review; platform and aggregate closure remain separate. + +## Snapshot and reviewer + +- Baseline: `6e1543b038961261555d35e55a4095afae02392e`. +- Worktree: `/private/tmp/codebasegraph-pr153-review`; review limited to the two PR comment fixes and their regressions in three source files. +- Reviewer: `openai-codex/gpt-6.1-sol`, `thinking=max` (parent-confirmed actual spawn metadata); fresh independent child, no implementation or source/Git writes. Read `adversarial-review-engineer/SKILL.md` and `references/engineering-lenses.md`. +- Canonical authority: wiki `architecture/graph-disk-cleanup-contract` revision 0.2; DC-S3-R3/R6/R7 and DC-S4-R3/R4, RAB-8/RAB-9, Scryer bugfix ledger `chg-ndq0yv`. Platform/aggregate/maintainer closure is separate. + +Final source SHA-256: + +| File | SHA-256 | +| --- | --- | +| `src/storage/maintenance.rs` | `061191affdacb910abe21070c25ae3cc2979db2f87ea0127274dce61e1e19c9e` | +| `src/storage/maintenance_candidate.rs` | `41917895967128bce8c7a5a3b4b6ad4ba55a2398232b8a3239c27e46795f47ba` | +| `src/storage/maintenance_legacy_generations_engine.rs` | `35fd9ff8a8c198226844664605b3dba54038b95d908554912200ef238e17642a` | + +## Confirmed findings corrected during review + +### P2 — Full advisory scan ignored cancellation + +The first reviewed `select_generation_name` enumerated arbitrarily many entries with no cancellation callback after removing the old 100-entry stop. `prepare_work` checked cancellation only before that scan. Once stop/rebind happened, the joined worker would still start every remaining per-generation open. Revision 0.2 permits long preparation and no completed-shutdown deadline, but expressly requires cancellation to stop new work and check available interruption points. + +**Correction verified:** `maintenance_legacy_generations_engine.rs:350–394` now accepts `cancel`; its per-entry check returns `Interrupted` before new directory/payload probes. `select_job` and `prepare_work` forward the real callback. The fourth-entry regression stops at exactly four callbacks and leaves all 153 entries intact. No shutdown deadline is imposed. + +### P2 — Direct bypass exceeded the existing payload-descriptor cap + +The first Direct fix bypassed `state.legacy_interest` in candidate preparation. Real Health confirmation can retain old default Direct DB/manifest descriptors through `PreparedLegacyConfirmationSource` (`maintenance_admission.rs:1355–1359,1435–1447`). The scheduler retains them until confirmation-consuming work claims them (`maintenance_scheduler.rs:393–396,881–886`). Modern Direct work does not consume confirmation. The bypass therefore let those two pins overlap artifact payload plus named recheck (`maintenance_candidate.rs:1404`, `maintenance_origin.rs:787–789`) or manifest scan/recheck (`maintenance_references.rs:702,716`), exceeding the unchanged three-payload-descriptor cap. These are payload files, not stable coordination handles. The original guard prevented this overlap. The initial interest-only test did not construct real request pins. + +**Correction verified:** final `maintenance.rs:612–614,862–896,914–926,1199–1217` rejects inapplicable Direct confirmation before source capture/enqueue at all three production paths: ordinary request, transient confirm-and-wait, and controller startup. The candidate pending guard is restored byte-for-byte. Managed confirmation handling is unchanged. `RepositoryIdentity::maintenance_target` explicitly supplies a Direct pair for real Direct selections. The new real-ingress regression creates old default DB/manifest payloads and exercises startup confirmation, later `request(true)`, default-false request, `confirm_and_wait`, and interrupted modern metadata recovery. The baseline comparison fails the actual first committed-recovery assertion, not a helper-predicate assertion. + +## Coverage and checks + +Source trace covered public Health ingress, actual target selection, request-time pin ownership/scheduler lifetime, candidate metadata recovery, fixed-buffer generation discovery, current/modern/shape filters, downstream eligibility guards, authority boundaries and cancellation. Full advisory discovery retains a 4096-byte stream buffer and one fixed 128-byte selected name, rather than a catalog. It stays off authority; the 100-entry/100-ms mutation grant is unchanged. + +An apparent ingress-test hang was traced to the existing scheduler's 60-second cooldown. The sampled worker was in `run_driver -> Condvar::wait_timeout`, while the request waited for a new sequence; the unchanged two-wait positive passed in 120.26 seconds. The final test removes only the redundant second wait. This is not a new product defect and no invocation deadline is claimed. + +Final execution evidence inspected: + +- Serial native Nextest run `f58f802c-25fd-401e-b79e-ec18806a307f`: **50/50 passed**, zero failures/errors/skipped cases; 790 tests excluded by the filter. Actual run duration 95.603 s. The Direct ingress regression passed in 60.230 s, Managed pending confirmation in 0.112 s, and generation discovery/cancellation in 0.045 s. Scheduler/source-pin regressions also passed. +- Command (parent-executed, Darwin ARM64, native-tests profile): + ```text + CARGO_TARGET_DIR=/Users/rabii/Projects/Repositories/codebaseGraph/target cargo nextest run --config-file /private/tmp/pr153-nextest-final.toml --profile pr153 --cargo-profile native-tests -p codebase-graph --lib -E 'test(storage::maintenance::tests) or test(storage::maintenance_candidate::tests) or test(storage::maintenance_legacy_) or test(storage::maintenance_scheduler::)' --test-threads 1 --no-fail-fast + ``` +- Final JUnit: `/private/tmp/pr153-review-final-junit.xml`, SHA-256 `429f5c9a889766dd8122d19f8a898eff63ea11ead38b2a90c79369154db055db`; log `/private/tmp/pr153-review-final.log`. Durable copies are under `docs/testing/pr153-review-fixes/`. +- Final workspace/all-targets `cargo clippy --offline --locked --workspace --all-targets -- -D warnings`: passed; reviewer inspected `/private/tmp/pr153-ingress-clippy.log`. Final `cargo fmt --all -- --check`: parent-reported pass. Reviewer-executed `git diff --check`: exit 0. +- Final three source hashes above independently rechecked after the final run and match the frozen inputs. +- Baseline evidence inspected: `pr153-ingress-baseline.log` fails actual committed recovery (generation 0 versus expected 1); `pr153-legacy-baseline.log` fails with `WouldBlock`. These behavior-restoration probes were parent/implementer-executed, not reviewer-executed. The 7/7 and 30/30 intermediate results are from the earlier candidate-only snapshot and are not substituted for final-source qualification. + +No constants changed by the final fix: only the erroneous advisory 100-entry cutoff is removed. The 60-second scheduler interval, authoritative 100-entry/100-ms grant, aggregate memory accounting and three-payload-descriptor cap remain unchanged. + +## Risks and limitations + +- No remaining source-based conditional defect or open behavioral question is identified in the scoped final source. +- Configured code graph lacks the PR cleanup modules; the review worktree has no local graph DB. Exact reviewed source was used as an explicit read-only discovery fallback, not another project's implementation. Scryer intent was read only from the parent-designated same-project model; no global drift reconciliation was performed. +- Reviewer ran no Cargo command to avoid shared-cache contention. Parent/implementer command logs and JUnit reports are independently inspected; they are not reviewer-executed tests. +- This review does not rerun the full repository suite, native Linux/Windows qualification or whole >100-generation retirement. The >100 fixture proves deterministic read-only selection and cancellation, not whole retirement convergence. No platform, release, milestone or aggregate cleanup closure is inferred. +- Reusable-knowledge assessment: the source-pin/advisory-preparation interaction is a useful invariant already stated in the governing Scryer brief and contract; this read-only review creates no duplicate wiki memory or external state. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 253babe6..5d147892 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -37,7 +37,7 @@ are documented in the [security policy](../SECURITY.md). | Missing LadyBugDB | Install `codebase-graph` from crates.io, a release archive, or this checkout. | | Stale graph | Run `codebase-graph mcp daemon status --config .codebaseGraph/config.json`. Use `watch` for an explicit foreground watcher or `build --mode full` for a manual rebuild. | | MCP HTTP transport send error | Run `codebase-graph mcp daemon status --config .codebaseGraph/config.json`. Inspect `service`, `latest_failure`, and `recommended_action`, then run the reported `start_daemon` command. | -| Daemon service unavailable | Ensure launchd, the systemd user manager, or Task Scheduler is available, then run `codebase-graph mcp daemon start --config .codebaseGraph/config.json`. Setup fails closed instead of silently creating stdio registrations. | +| Daemon service unavailable | Ensure launchd on macOS ARM64 or Task Scheduler on Windows x86_64 is available, then run `codebase-graph mcp daemon start --config .codebaseGraph/config.json`. Setup fails closed instead of silently creating stdio registrations. | | Broken setup state | Run `codebase-graph reinstall` to recreate `.codebaseGraph/` and refresh the selected registration. | | Broken client configuration only | Run `codebase-graph mcp install --client --verify`. | | Binary not found | Ensure the native `codebase-graph` binary is on `PATH`. | diff --git a/knowledge/architecture/graph-disk-cleanup-contract.md b/knowledge/architecture/graph-disk-cleanup-contract.md new file mode 100644 index 00000000..f6500278 --- /dev/null +++ b/knowledge/architecture/graph-disk-cleanup-contract.md @@ -0,0 +1,382 @@ +--- +description: Five individually validated cleanup specifications, with retention rules, failure behavior, requirements, acceptance checks and explicit approval status. +tags: +- acceptance +- cleanup +- contract +- graph-storage +- specifications +title: Automatic graph disk cleanup — five-step specification +type: contract +--- +# Automatic graph disk cleanup — five-step specification + +## Identity, authority and validation status + +Canonical specification for the five steps proposed in the [disk retention review](./graph-disk-retention-review-2026-10-20.md). Base specification revision **0.1**, prepared 2026-10-20; affected DC-S2–S4 contracts amended and approved as **revision 0.2** on 2026-10-21. Preparation ledger: Scryer `chg-cn9ja4`; it records specification work, not implementation approval. Stable step IDs are **DC-S1** through **DC-S5**, preserving the review's five-step order. Requirement and acceptance IDs below remain stable through revisions. + +The wiki owns the user-facing scope, contracts, decisions, acceptance gates and evidence references in this page. Scryer remains the sole architectural authority; it owns changed architecture claims, detailed implementation briefs, prerequisites, file ownership and test assignments. This document maps existing claims and identifies where future Scryer deltas are required. It creates no competing component topology, invented implementation anchors or claims of implemented behavior. + +The user requested on 2026-10-20: “for each step of the plan define its specs, validate each one of them with me before implementation.” That sequential validation instruction is preserved as history and was superseded by the explicit all-step implementation approval below. Material changes beyond the approved scope still require user validation. Specification validation is distinct from executed acceptance evidence, supported-platform qualification and deployment. + +An unanswered prompt, a preselected answer, approval of another step, or earlier agreement with the overall cleanup direction is not approval of a step. Changes affecting an approved step's behavior, scope, safety or acceptance criteria require fresh validation of that affected step. Routine implementation details remain subject to existing approved contracts and Scryer rules. + +| Step | Revision | Status | User decision reference | Implementation/evidence | +| --- | --- | --- | --- | --- | +| DC-S1 — Inventory and retention | 0.1 | Locally implemented and independently reviewed | Explicit all-step instruction, 2026-10-21 | 29 passing inventory tests; platform and aggregate qualification pending | +| DC-S2 — Ownership and recovery authority | 0.2 | Locally reviewed foundations; authority integration continuing | Explicit all-step instruction and amendment approval, 2026-10-21 | Guarded native wrapper, fixed epoch transition and nonce restoration reviewed; complete admission pending | +| DC-S3 — Collection and migration | 0.2 | Bounded facts/codecs reviewed; production collection pending | Explicit all-step instruction and amendment approval, 2026-10-21 | 78 independently passing pure-facts tests plus differential probes; origin, publication and retirement effects pending | +| DC-S4 — Automatic operation and reporting | 0.2 | Joined driver reviewed; lifecycle integration underway | Explicit all-step instruction and amendment approval, 2026-10-21 | 31 driver/coordinator tests plus independent failure/allocation probes; actual collection wiring pending | +| DC-S5 — Qualification and rollout gates | 0.1 | Qualification harness reviewed; full acceptance pending | Explicit all-step instruction, 2026-10-21 | Fresh-compiler and debug-assertion counterexamples corrected and independently verified; churn/native/full matrix pending | + +Closure owner remains the repository maintainer/user. The later implementation task owns aggregate closure and must retain these IDs rather than renumbering them. + +## Implementation approval — 2026-10-21 + +The user explicitly instructed: “start the implementation of all steps. Each spec should be implemented by a sub-agent in fresh session. Each spec should be reviewed by another sub-agent in a fresh session.” This approves implementation of DC-S1–DC-S5 revision 0.1 together and supersedes the earlier queued one-at-a-time validation sequence. It includes the proposed 60-second interval, 100-entry/100 ms cooperative defaults and existing CLI/MCP reporting surfaces. It does not satisfy executed evidence, platform qualification or deployment gates. + +The user separately selected **Support controlled migration** for pre-lease legacy state: a one-time controlled shutdown/upgrade/quiescence procedure is supported; until that condition is explicitly established, preserve state and report the reason. No actual daemon shutdown, installation upgrade or live graph deletion is part of this implementation task. + +Initial implementation baseline: `512b5dc197adf5590afa5d2f7b44550dfa32d8a2` (main recovery fixes); rebased onto current main `018df35e72447fc3e014e950d1d3c6648bbd4820` before DC-S2, isolated checkout `/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/repo`, branch `codex/automatic-graph-disk-cleanup`. Architecture and detailed execution briefs: Scryer change `chg-6vx5tj`; the approved same-repository recovery model was imported unchanged, preserving identities. The original preparation ledger `chg-726xze` records coordination only. Each step receives a new implementation agent and a separate new reviewer with no inherited conversation context; implementation uses GPT-6.1 Sol Medium, required reviews use GPT-6.1 Sol Max. The pre-implementation proposal review remains a starting gate. + +Implementation clarifications within these contracts include whole-operation producer-use leases for low-level/native paths, exact Direct incarnation-bound retirement, incremental validation outside reader-facing state locks, and explicit default-false `confirm_legacy_quiescence` input on the existing health/MCP/CLI surface. Operator confirmation records the controlled-cohort precondition; timestamps/PIDs or ordinary health calls cannot manufacture it. No new standalone command or GUI is introduced. + +## Settled scope and shared constraints + +The user already selected this retention policy in the review conversation: **keep the current managed graph and its active parsing cache; retire older repository-local Direct graphs once safe ownership checks pass.** A later explicit use of a retired Direct destination will require rebuilding. This decision is reused, not reopened. + +The intended result is eventual removal of unnecessary program-owned disk state while preserving graph correctness, cache reuse, active work, recoverability and supported interfaces. Approximately 50 GiB recoverable and 3.5 GiB idle usage were estimates for the observed workload. They are not a fixed quota, an upper bound for future repositories, or a promise to delete unresolved state. + +Shared constraints for every step: + +- Keep the active database, search sidecars, compact manifest, generation metadata, readiness marker and stable coordination/lock files. +- Preserve active source-cache keys, leased generations, live runs and unresolved publication/worker/phase evidence until their corresponding safe transitions complete. +- Do not use directory age, recorded numeric PIDs, file-name patterns, or configuration non-reference alone as deletion authority. +- Confine cleanup to registered program-owned state. Foreign directories, caller-owned low-level staging, custom external Direct destinations, source files, `target/`, `knowledge/` and generated wiki state are outside the collector's scope. +- Reject path traversal and symlink escapes. An unknown entry or uncertain identity is preserved and explained. +- Preserve existing graph/manifest formats and Direct destination identity unless a later individually validated specification expressly changes them. This proposal does not canonicalize existing destination-derived lock/journal keys. +- Keep graph queries usable during deferred cleanup. Temporary space during materialization remains legitimate. +- Separate configuration approval, local implementation, executed tests, native-platform qualification and deployment. No GUI work is proposed. + +The initial review examined `6aa5ba8bd99c3071e106a8bfc7a408652a8e4bb5`. The [worker recovery contract](./worker-recovery-regression-contract.md) identifies merged PR #145 and test/specification work targeting `origin/main` revision `512b5dc197adf5590afa5d2f7b44550dfa32d8a2`. The older checked-out branch does not establish that those fixes exist in its source or installed daemon. DC-S2 must settle the implementation baseline and reuse the existing work; no second containment architecture is authorized here. + +## DC-S1 — Inventory and retention, revision 0.1 + +### Problem and observable outcome + +A directory total currently mixes the active graph, useful parsing cache, live build space and historical debris. Produce an inspectable inventory that explains every in-scope entry and estimates potential reclamation without changing stored state. + +The inventory is a description of observations. It is not a reusable deletion permit. DC-S2 and DC-S3 must revalidate ownership and reachability at the time of any mutation. + +### Scope and requirements + +| ID | Specification | +| --- | --- | +| DC-S1-R1 | Observe only the current repository's configured managed root and recognized repository-local historical/Direct state whose association can be established. Do not scan other repositories or arbitrary siblings. | +| DC-S1-R2 | Inventory performs no materialization, publication replay, repair, deletion, configuration update or metadata creation. A missing installation is reported without creating directories or lock files. | +| DC-S1-R3 | Categorize entries as active graph, active parsing cache, leased generation, live build, recovery evidence, artifact temporary/replacement state, historical staging, retired-Direct candidate, coordination metadata or unknown. Each physical file is accounted for once in totals. | +| DC-S1-R4 | Give each entry a disposition: retain, potential reclamation candidate, deferred, or unknown, with a concrete reason. Active-manifest non-reference alone cannot override a live run, lease or recovery reference. | +| DC-S1-R5 | Retain the active graph bundle, active parsing cache and stable locks. Treat recognized old repository-local Direct destinations as retirement candidates under the already selected policy, subject to their own ownership checks. | +| DC-S1-R6 | Report logical bytes, filesystem-reported allocated bytes where available, file/directory counts, measurement time, scope identity, active-generation identity and scan completeness. Unknown size remains unknown rather than being reported as zero. Allocated-byte estimates do not promise an equal increase in free disk space. | +| DC-S1-R7 | If state changes during scanning, report a moving/partial observation or bounded retry; never label inconsistent totals as a coherent exact snapshot. A collector cannot delete using a stale inventory. | +| DC-S1-R8 | Do not follow symlinks. Preserve unrecognized entries. Invalid roots, unreadable metadata, missing/corrupt active pointers, ambiguous Direct mappings and permissions failures produce explicit incomplete/blocked reasons; they do not make all files reclaimable. | + +Public CLI/MCP presentation belongs to DC-S4. This step defines the information contract, not a new command or GUI. + +### Failure and recovery + +Inventory can return a complete observation, a partial/moving observation with per-entry reasons, a busy/unavailable observation, or an invalid/missing installation result. No outcome modifies the filesystem. An interrupted scan may be retried from current state; earlier results have no deletion authority. + +### Acceptance and planned verification + +| ID | Given / When / Then | Existing Scryer mapping | Verification/evidence | +| --- | --- | --- | --- | +| DC-S1-A1 | Given active artifacts, old staging and a live run, when inventory runs, then every item is attributed once and active/live state is retained or deferred. | Graph Store resp-109, resp-232, resp-233, resp-234; additive inventory claim to be planned if needed | Isolated fixture and independent size/count oracle; planned | +| DC-S1-A2 | Given a missing root or a valid installation, when inventory runs or is interrupted, then the file tree, configuration and active pointer remain unchanged. | Existing coordination/confinement contracts; no invented runtime claim | Before/after tree and metadata comparison; planned | +| DC-S1-A3 | Given a pointer change, inaccessible subtree, symlink or ambiguous Direct pair, when inventory runs, then it exposes incomplete/unknown state without traversing or authorizing removal. | Graph Store resp-109; Runtime Resolver resp-144 | Deterministic mutation/error fixtures; planned | +| DC-S1-A4 | Given two unchanged observations with the same options, when inventory runs twice, then categories, reasons and totals agree apart from observation metadata. | Existing identity and deterministic behavior contracts | Repeat-fixture comparison; planned | + +**DC-S1 validation requested:** Approve the read-only inventory and conservative retention behavior above, or identify changes. Recommendation: approve revision 0.1. This approval will not approve DC-S2–DC-S5. + +## DC-S2 — Ownership and recovery authority, revision 0.1 + +### Problem and observable outcome + +The program must distinguish obsolete files from files still used by a reader, writer or surviving worker. Before any destructive collection, establish verifiable authority over each destination and preserve recovery evidence whenever authority is uncertain. + +### Scope and requirements + +| ID | Specification | +| --- | --- | +| DC-S2-R1 | Reuse the approved process-containment and worker-recovery contracts from PR #145 and their qualification work; verify their presence on the exact selected implementation baseline. Do not duplicate the older repair or treat wiki claims as proof of deployed code. | +| DC-S2-R2 | Require the destination's exclusive writer ownership before deciding destructive eligibility. Direct retirement uses that exact Direct destination's ownership protocol; managed ownership alone is insufficient. | +| DC-S2-R3 | Respect generation reader leases, live run leases, worker/phase containment and interrupted-publication journals. Missing or unverifiable evidence beside possible execution state remains unknown. | +| DC-S2-R4 | Recover or resolve interrupted publication through its established protocol before computing authoritative references. Preserve the last committed graph while recovery cannot safely proceed. | +| DC-S2-R5 | Keep a consistent writer-to-state acquisition order. A caller that already owns the writer lease uses that ownership without reacquiring it. Contention defers maintenance instead of bypassing ownership. | +| DC-S2-R6 | Preserve stable lock files/inodes and evidence while authority is unknown. Never kill a process or authorize deletion solely from a numeric PID or elapsed age. | +| DC-S2-R7 | Return explicit proven-idle, in-use or unknown outcomes with destination-specific reasons. Recheck the relevant authority immediately before detaching/deleting state. | +| DC-S2-R8 | Historical helpers that predate current ownership protocols require a verified quiescent migration procedure. Current-lock acquisition by itself cannot authorize deletion against those helpers. If proof is unavailable, preserve state and report what is required. | + +### Failure and recovery + +Busy ownership defers the attempt. Unknown containment blocks conflicting writes and destructive cleanup according to the existing recovery contract; it does not erase evidence to force progress. Attempts follow the already approved bounded recovery behavior and allow paced retries. The exact procedure for legacy quiescence remains an explicit DC-S2 decision. + +### Acceptance and planned verification + +| ID | Given / When / Then | Existing Scryer mapping | Verification/evidence | +| --- | --- | --- | --- | +| DC-S2-A1 | Given a paused owned loader, when its outer worker dies and cleanup/writing is requested, then evidence remains until teardown is verified and no conflicting publication occurs. | Worker node-208, approved Guardian claims from PR #145, Graph Store resp-109/232 | Reuse/extend native recovery regressions on the selected baseline; planned | +| DC-S2-A2 | Given an active managed reader or Direct reader/writer, when maintenance tries retirement, then protected files survive and the attempt is deferred. | Graph Store resp-109, resp-233 | Separate-process lease fixture; planned | +| DC-S2-A3 | Given unverifiable evidence or a reused unrelated PID, when maintenance runs, then it preserves unknown state and never signals the unrelated process. | Existing approved worker/Guardian legacy and unknown-containment contracts | Existing native cases plus collector integration; planned | +| DC-S2-A4 | Given a caller already holding writer ownership or a historical helper using old state, when collection is requested, then there is no self-deadlock or unsafe legacy deletion. | Graph Store resp-109 | Ownership-order and legacy-consumer fixtures; planned | + +**Decisions for later DC-S2 validation:** Use a current main baseline containing PR #145 and the relevant regressions; identify the verified one-time quiescence procedure for historical state. Recommended fallback is preserve-and-report when that procedure cannot prove safety. These are not approved by DC-S1. + +## DC-S3 — Collection and migration, revision 0.1 + +### Problem and observable outcome + +Recover disk occupied by unnecessary program state, including the namespaces missed by current cleanup, without deleting active inputs or losing the ability to recover from interruption. + +### Scope and requirements + +| ID | Specification | +| --- | --- | +| DC-S3-R1 | Collect unreferenced valid artifact keys only after recomputing current references under DC-S2 authority. Preserve active/live-run/recovery references and any unknown necessary state. | +| DC-S3-R2 | Recover or remove abandoned artifact temporaries and replacement backups. A valid backup of a missing referenced artifact must be reconciled before it can be classified disposable. Invalid/ambiguous backup evidence is preserved with a reason. | +| DC-S3-R3 | Recover abandoned modern workspaces through their established journals and collect them only after the associated execution is proven empty. Continue lease-aware retirement of superseded generations. | +| DC-S3-R4 | Run an idempotent migration for recognized old native-staging files and snapshots once legacy quiescence is proven. Unrecognized content is retained and reported. No age-based grace period is introduced for proven unnecessary state. | +| DC-S3-R5 | Retire approved former repository-local Direct destinations as coherent database/manifest/sidecar/cache bundles. Keep stable ownership files. A later explicit request must receive a clear rebuild-required outcome; explicit rebuilding may create a fresh destination through the normal writer protocol. Exact public wording/schema is validated with DC-S4. | +| DC-S3-R6 | Persist recoverable retirement state before destructive work. An interrupted collection resumes idempotently; it cannot make a partial Direct bundle appear committed or revive partially deleted state as a valid graph. | +| DC-S3-R7 | Keep reader-facing state-lock sections short. Detach only proven-unreferenced state, then perform bounded deletion outside the reader-facing critical section. Before detachment fails, preserve the original state; after durable detachment, retry the recorded deletion. | +| DC-S3-R8 | A filesystem failure records remaining work and preserves the primary operation's result/error. Preserve active graph readability, caller-owned outputs and foreign data. Do not evict referenced active artifacts to satisfy a disk quota. | + +### Failure and recovery + +An unavailable writer/reader lease defers work. Unknown references, corrupt authority or foreign paths preserve state. Filesystem failures leave explicit retryable retirement work. Cancellation stops at a recoverable boundary; restart resumes safely. Backup reconciliation follows verified checksums and existing artifact/schema validation rather than trusting a filename. + +### Acceptance and planned verification + +| ID | Given / When / Then | Existing Scryer mapping | Verification/evidence | +| --- | --- | --- | --- | +| DC-S3-A1 | Given obsolete keys and synthetic temporary/replacement residue, when ownership is idle and maintenance completes twice, then unnecessary bytes disappear and active cache reuse survives. | Graph Store resp-232, resp-234; additional collection claims if needed | Artifact interruption/recovery fixtures; planned | +| DC-S3-A2 | Given a missing active artifact with a usable replacement backup, when recovery runs, then usable required state is restored/preserved before backup removal. | Graph Store resp-234 | Replacement-boundary fixtures; planned | +| DC-S3-A3 | Given historical staging and an approved old Direct destination, when migration completes, then both are retired coherently and current managed query results remain unchanged. | Graph Store resp-108/109/232/233; lifecycle node-136; proposed retirement policy needs Scryer mapping | Legacy/Direct fixtures and current graph comparison; planned | +| DC-S3-A4 | Given process death at each retirement boundary or a permissions failure, when maintenance restarts, then it converges without double publication, unsafe deletion or loss of the active graph. | Graph Store resp-108, resp-109, resp-232 | Journal-boundary process fixtures; planned | +| DC-S3-A5 | Given many eligible files and a concurrent graph reader, when recursive deletion runs, then readers can proceed and external sentinels remain unchanged. | Graph Store resp-109 and confinement contract | Deterministic paused-collector/concurrent-reader fixture; planned | + +**Decisions for later DC-S3 validation:** Confirm the bounded, recoverable migration scope and behavior when a retired Direct destination is accessed again. Actual live deletion remains outside specification preparation. + +## DC-S4 — Automatic operation and reporting, revision 0.1 + +### Problem and observable outcome + +Unnecessary files must be reclaimed without requiring another successful graph build or a manual source edit. Users must be able to distinguish useful disk use from deferred or blocked cleanup. + +### Scope and requirements + +| ID | Specification | +| --- | --- | +| DC-S4-R1 | Schedule maintenance at coordinator startup, after build success/failure/cancellation, after unchanged refresh, and periodically while the coordinator runs. With no long-lived coordinator, ordinary runtime entries request bounded maintenance through the same protocol. | +| DC-S4-R2 | At most one collector acts on a destination at a time. Coalesce duplicate triggers; do not compete with an active writer or create a graph rebuild because cleanup changed internal files. | +| DC-S4-R3 | Approved defaults: a 60-second background interval and immediate eligible event requests. Fresh authoritative admission/checks/mutations share up to 100 filesystem entries or 100 ms of cooperative work, then yield. Advisory preparation and at most one payload validation/disposal may run longer outside storage/execution authority, with bounded aggregate memory/jobs. Individual calls can overrun; no syscall, invocation or completed-shutdown deadline is promised. See the approved revision-0.2 budget contract below. | +| DC-S4-R4 | Retry later without source edits; preserve fair selection and retirement progress across resident opportunities and repeated transient invocations. Long preparation cannot consume the following mutation allowance. A blocked first candidate cannot monopolize attempts. Persistent preparation holds no real dispatcher permit; transient calls/shutdown may wait for preparation/one validation/disposal. Do not busy-loop. | +| DC-S4-R5 | Proposed reporting surface: additive storage-maintenance fields on existing health/materialization reporting, available through existing CLI/MCP operation paths. No new GUI or standalone command is assumed. Existing response fields and error meanings remain compatible unless a specific change is separately approved. | +| DC-S4-R6 | Expose observation time/completeness, logical/allocated bytes by category, potential reclaimable bytes, deferred/unknown bytes, last attempt/outcome, reclaimed allocation estimates, and concrete blocked reasons. Do not report a live build as confirmed garbage or report unknown space as zero. | +| DC-S4-R7 | A cleanup failure must not mislabel an already committed generation as unpublished. Preserve the primary build failure and report cleanup pending separately. Do not silently erase cleanup failures. | +| DC-S4-R8 | New maintenance configuration/response contracts, defaults and compatibility behavior require Scryer planning and explicit DC-S4 validation before implementation. Do not log control capabilities or file contents as disk diagnostics. | + +### Failure and recovery + +A busy destination waits for a later trigger. A failed deletion remains pending with a reason and continuation state. An invalid scope fails closed for that scope. Inventory freshness and an executing collector are distinct observable states. Maintenance stops cleanly with the coordinator and resumes from durable state on next entry. + +### Acceptance and planned verification + +| ID | Given / When / Then | Existing Scryer mapping | Verification/evidence | +| --- | --- | --- | --- | +| DC-S4-A1 | Given only failed builds followed by no-op refreshes, when maintenance opportunities occur, then eligible artifacts converge without a successful publication or source edit. | Materialization resp-231/270; Refresh resp-147/268; proposed maintenance trigger claims required | Controlled-clock lifecycle/integration tests; planned | +| DC-S4-A2 | Given repeated simultaneous triggers and an active writer, when the scheduler runs, then attempts coalesce, yield and later resume with no overlapping collector/writer or rebuild loop. | Refresh resp-266/267/268; Graph Store resp-109 | Scheduler and process interleavings; planned | +| DC-S4-A3 | Given a permission failure, unknown containment or live lease, when health is read, then pending work and reason are distinguishable from retained active space; recovery later clears the specific reason. | Refresh resp-269; public-contract node-96; new storage reporting claims required | CLI/MCP contract fixtures; planned | +| DC-S4-A4 | Given a committed generation and a later cleanup failure, when the operation responds, then its committed publication remains truthful and cleanup is separately pending. | Graph Store resp-108; Materialization resp-238 | Fault injection after commit; planned | + +**Decisions for later DC-S4 validation:** Automatic triggers; proposed 60-second interval; proposed cooperative batch defaults; existing CLI/MCP reporting versus a separately scoped dedicated command; compatibility semantics. None is approved by DC-S1. + +## DC-S5 — Qualification and rollout gates, revision 0.1 + +### Problem and observable outcome + +A cleaner must be proven to reduce unnecessary whole-state disk usage while preserving graph behavior and safe recovery, rather than passing tests that inspect only the database file. + +### Scope and requirements + +| ID | Specification | +| --- | --- | +| DC-S5-R1 | Trace every accepted requirement and criterion to applicable Scryer claims, real implementation anchors, a verification method and executed evidence. Do not invent claims or label configured/attached tests as passing executions. | +| DC-S5-R2 | Extend whole-state convergence coverage beyond database size: valid keys, temporary/replacement state, runs, retired generations, tombstones, legacy staging and Direct retirement. Use an isolated clean-control fixture with the same final source state. | +| DC-S5-R3 | Proposed churn qualification: 20 supported source updates plus failures, unchanged refreshes and repeated maintenance. At verified idle, retain one active generation, current cache and necessary coordination files, with no known collectible residue. Blocked foreign/unknown state must be reported and excluded from a successful clean-state claim. | +| DC-S5-R4 | Prove graph/search/context equivalence and preservation of unchanged artifact reuse. Compare expected reachable content, not only counts or empty directories. | +| DC-S5-R5 | Exercise process death at temporary-write, replacement, publication, detachment and deletion boundaries; reader/writer contention; cancellation; permissions failures; stale inventory; missing/corrupt authority; symlink/foreign paths; historical helper ownership; and retries without new edits. Faults control only fixture-owned processes. | +| DC-S5-R6 | Use existing native CI platform gates and lifecycle profiles. Local macOS observations, extracted-function checks, default compilation, native Windows qualification and aggregate closure remain separate evidence. New required regressions run normally, without ignore or expected-failure treatment. | +| DC-S5-R7 | Before any implementation, obtain the repository-required fresh GPT-6.1 Sol Max proposal review and resolve decision-blocking findings. Before completion, obtain the required fresh final integrated review, fix every confirmed P0–P3 defect, and reverify affected paths. This specification turn does not claim either implementation gate is complete. | +| DC-S5-R8 | Record exact source revision, binary/environment identity, commands, JUnit evidence, outcomes and limitations. Complete required formatting/build/static/test checks without unresolved compiler warnings, then reconcile Scryer and wiki outcomes. | +| DC-S5-R9 | Rollout requires a compatible verified binary/helper set and truthful inventory before collection. Selection of installation/restart/live migration actions is validated separately with the actual operational context. Specification preparation neither updates an old daemon nor deletes this installation's data. | + +### Failure and recovery + +A failed or incomplete gate leaves qualification pending. Preserve reports and identify precondition/setup failures separately from demonstrated product failures. Do not weaken assertions or conceal failing runs. If a required reviewer/platform is unavailable, report that exact missing gate and keep the affected closure pending under repository rules. + +### Acceptance and planned verification + +| ID | Given / When / Then | Authority | Verification/evidence | +| --- | --- | --- | --- | +| DC-S5-A1 | Given the same final source workload, when churn and clean-control builds reach verified idle, then the collector leaves only allowed state and equivalent graph behavior. | Existing Graph Store/Materialization contracts and accepted DC-S1–S4 | Whole-state/native integration reports; planned | +| DC-S5-A2 | Given every listed interruption/ownership case, when recovery and repeated maintenance run, then required data and external sentinels survive and eligible work eventually clears. | Approved recovery contracts and accepted DC-S2/S3 | Native lifecycle JUnit reports; planned | +| DC-S5-A3 | Given a claimed completed implementation, when its evidence is audited, then each requirement has a real anchor, executed passing evidence, reviewed snapshot and honest platform limits. | Scryer closure rules; AGENTS review/CI/Git workflow | Traceability/evidence audit and independent reviewer verification; planned | +| DC-S5-A4 | Given a candidate rollout against this repository, when inventory and binary identity are checked, then live migration uses the approved policy and the proven ownership protocol. | Accepted DC-S1–S4 plus actual deployment context | Deployment inventory, identity, before/after query and disk evidence; planned | + +**Decisions for later DC-S5 validation:** The complete test matrix and proposed 20-update convergence fixture; required platform qualification and rollout boundaries. Approximately 3.5 GiB is not a pass/fail threshold; the same final workload and approved reachable state are the oracle. + +## Handoff and next validation + +All five specifications are approved for implementation by the explicit instruction recorded above. Per-step implementation, independent review, executed acceptance evidence, supported-platform qualification and deployment remain separate states. This approval does not claim any implementation already passed its checks. Revisit affected requirements if later work needs material changes to approved scope. + +Detailed implementation signatures, file ownership, worker slices and any added architecture claims remain with the future Scryer plan. This contract intentionally does not duplicate execution briefs or invent symbols for documentation-only work. The established Git/PR workflow applies when implementation changes are requested and completed; unrelated existing workspace changes remain preserved. + +## Executed local evidence — DC-S1 + +The read-only inventory foundation is committed as `bb3c716e899c` on current main. Its implementation and tests live in `src/storage/inventory.rs`; Scryer `node-2yhnem` (`resp-a133nw`, `resp-hd92fb`) and `node-hdwgka` have been anchored, supplied with 29 actual test attachments and the executed JUnit report. No destructive maintenance or live installation changes occurred. + +A separate fresh GPT-6.1 Sol Max reviewer accepted the frozen inventory SHA-256 `2d773c8ffbfd5e701b5af9cf73dd668477f157a1792e8a333fabe1f1e60b0428` after independently compiling the exact source, running all 29 unit cases and nine boundary probes. Confirmed review findings were corrected and verified: scope binding, active Direct classification, physical aliases/overlapping roles, configured external storage without local state, raw Direct address spelling, and static symlinks hidden by lexical parent traversal. Final reviewed JUnit run `cc7cbef3-35cf-4d41-a6d4-e7743f43036d` has SHA-256 `abba30344a85b32b9bdec3084dae421643a7a430c779aef743b07e6bcae1750d`. + +After integration with main, `cargo nextest run --offline --locked --lib --config-file /private/tmp/codebasegraph-disk-cleanup-7t9af0a6/nextest.toml --profile cleanup-s1 -E 'test(storage::inventory::tests)' --test-threads 1` again passed all 29 selected tests (run `467d04e0-f3a8-4d37-ab03-ea8a7216e0e9`). Evidence snapshots are retained under `/private/tmp/codebasegraph-disk-cleanup-7t9af0a6/evidence/s1-fix-round-3`; independent probe evidence is under `/private/tmp/codebasegraph-cleanup-s1-review-r3-048vl1pl`. + +Inventory currently reports a partial observation when compatible artifact-producer ownership cannot be established. It does not create locks or cohort metadata. Later ownership integration must supply a validated, noncreating observation path. This is local inventory qualification only: native Windows behavior, hostile concurrent ancestor replacement, large-cache resource behavior and final integrated cleanup remain unqualified. Unused foundation warnings must be resolved through the planned production callers before aggregate completion. + +## DC-S2 public native-handle decision — approved revision 0.2 + +On 2026-10-21 the user explicitly approved: “Approve both: the guarded Rust wrapper and mutation-only cleanup budget. Continue all remaining steps, each with a fresh implementation agent and a separate fresh reviewer.” + +The selected public compatibility boundary is the **strict guarded wrapper**. Both public Rust `open_ladybug_database` helpers return an opaque guard-owning database wrapper, retaining database-use exclusion for the native handle's entire lifetime, including ordinary borrowed connection access. Native handle teardown precedes release of its guards. Safe consuming extraction or conversion to an unguarded raw native database is excluded. This changes public Rust return types; explicit native-type or consuming-API callers may require updates. Some formerly valid readonly/unwritable-parent or symlink/hardlink/alias opens may be rejected when strict physical exclusion cannot be established. Those compatibility costs are explicitly approved. CLI/MCP response contracts and raw Direct lock/journal address spelling remain unchanged. + +The raw-return/all-consumer-downtime alternative and additional compatibility-preserving coordination alternative were considered and not selected. This approval resolves the earlier questions and authorizes dependent database-retirement implementation once actual guards, containment, incarnation and reference proofs are implemented and verified. It does not authorize live deployment, shutdown or deletion. Ordinary committed Direct reads still preserve their reviewed separation from optional producer-overlap registration; actual lifetime guards and required transaction replay remain strict. + +Scryer `node-2xwzve` owns cleanup authority and `node-9emgn1` / `resp-2x4van` owns native-handle lifetime exclusion. Detailed execution briefs and file ownership remain in Scryer. Approval, implementation, qualification and aggregate closure are distinct; the maintainer/user remains closure owner. + +## Executed local evidence — partial DC-S2–DC-S5 foundations + +DC-S2's guard foundation is committed as `c0741ce137e09dac309ac72f5a2c2243a92ef070`. A separate fresh GPT-6.1 Sol Max reviewer verified the corrected snapshot SHA-256 `0e2bde1fe2c37b664aa864175c0b95a1f9297f6f0a64094295aed98c44af2dc3`; actual focused/regression runs passed 128 and 127 executions, respectively. JUnit hashes are `6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876` and `ddccd6f40a368bcacd87f7c1c826028dde700841371e4d54fd6c27e45a11be92`. Findings corrected and independently rechecked include existing-root reads blocked by producer ownership, dangling worker evidence, artifact versus destination cohort provenance, Windows link-count checks, and a reproduced Unicode-alias exclusion bypass. Parent-family database guards now conservatively prevent alias bypass while preserving original Direct address keys. This is a guard-foundation checkpoint, not whole DC-S2 acceptance; reference mutation/seal integration, the user decision above and native-platform evidence remain pending. + +DC-S3's frozen expanded partial snapshot `ac4f8016de83c8a3cd156d24cd7b2b3d24f2d816fb64b3b45318f207b2464cf8` passed 31 real-library unit executions on Darwin arm64; JUnit SHA-256 `e00f773cf313ef6575f4fdcf79486dc865dd19ee634888b5b4cf3e1d427e0627`. It contains exact-incarnation retirement primitives, cooperative validation and retained traversal foundations. Separate Max review then identified four defects: post-publication cleanup error propagation, torn marker replay, insufficient retained-memory accounting, and housekeeping outside zero/single-entry allowances. The marker and budget fixes are independently verified at snapshot `1e4f8e9063133924204c712634203c6ff9f7aaaebf7fbf056148b725c7756f65`, with 35 actual passing tests (JUnit SHA-256 `8b6a70e681e8fa0ec013cfb0d0dc1deb85064163db66de579cd9e2871f653965`). Progress publication now binds and resumes only its own preparation inode, syncs complete contents before rename, and charges independent unlinks. Arbitrary non-prefix corruption stays unknown; power-loss convergence is unproven. The post-publication artifact-error finding is now independently verified as fixed in the S4 API/protocol snapshot (17 real pipeline/contract executions passed, including Direct/Managed readability and a Direct postcommit cleanup fault). The mixed Managed recovery/cleanup handoff and decoder/resource defects remain pending. No database/generation/run/legacy/Direct retirement is qualified by these primitive tests. Scryer owns the private reference-epoch and bounded transition prerequisites; no stale inventory or advisory parser result authorizes deletion. + +DC-S4's scheduler primitive source SHA-256 `480df68088b7ab2e01936c9e07733a8a70b1380eda38a65c929bfebc297ac9bf` passed separate fresh GPT-6.1 Sol Max review with no confirmed P0–P3 findings: 10 actual real-library nextest cases and seven additional independent boundary probes. Independent JUnit SHA-256 `8fa9207d515e7be607d98f1f9e799edc0970899e21bace664d9e971a270bd414` records run `ca7ff1b0-0683-4ec5-8cc1-b671fc13e83f`. Atomic dispatcher integration, real lifecycle triggers, reporting and collector budgets are separate pending checks. The primitive alone does not establish automatic cleanup. The later additive health DTO/default-false confirmation/CLI normalization and real dispatcher-admission slice passed the same independent reviewer with no confirmed P0–P3 findings. Its independent 44-test union includes 32 focused and 16 coordinator cases with four overlapping cases; JUnit SHA-256 `e93dd8463718f7ace48d05c5e38bcee59b8b6a7bdf5a755ff26df0515dcd7846`. Verification used accepted HEAD plus exact frozen S3/S4 prerequisites, not in-flight epoch code. Eleven independent files are committed as `44e467fa9ea1d0a9604a05a41cd34e763bb1c131`; the scheduler module awaits its collector dependency. The additive native materialization-response field and narrow post-publication correction are included in the combined foundation below. Lifecycle wiring, confirmation consumption, compact measurements and the mixed Managed recovery/cleanup handoff remain pending. + +DC-S5 has prepared [the acceptance/evidence matrix](../../docs/testing/graph-disk-cleanup-evidence.json), archived actual foundation JUnit reports and an integrity audit. Counts are test executions across possibly overlapping selections, not unique scenarios or integrated acceptance. The full churn/control, fault/restart, graph equivalence, native-platform and final integrated review gates remain pending. Rust 1.82 qualification is additionally blocked by the unchanged baseline lockfile: pinned clap 4.6.1 requires edition 2024/Rust 1.85, and pinned tree-sitter-language 0.1.8 declares Rust 1.90. Initial offline registry-cache failure was diagnosed separately. No dependency downgrade, manifest change or advertised MSRV change is included; current actual local executions use Rust 1.96. The baseline dependency mismatch does not prove the new source fails on 1.82, and it cannot be reported as a passing 1.82 check. + + +## Remaining integration gates + +The private epoch-only producer foundation and the independent S4 DTO/dispatcher slice passed focused fresh Max proposal starting reviews. Bounded preparation and collector sealing remain separate gates: validate the observed epoch before the collector's own invalidation, retain exact transition authority, and release all maintenance-owned guards on pending/error. Repeatedly restarting an oversized idle registry scan does not establish convergence. The current whole-envelope decoder's memory estimate, automatic partial-value destruction and synchronous join do not establish bounded cancellation. + +Unreferenced canonical artifacts and recognized unreferenced root temporaries require both destination and entry-origin proof, strict shape and current complete references. Root ownership, a hexadecimal directory name or a plausible envelope header alone does not establish entry origin; foreign, corrupt or unverified legacy content stays unknown. Decoding every graph row is not itself a deletion permit, and avoiding that decode does not waive entry ownership. Usable referenced replacement recovery requires separate validation. A resident cursor or parser stack alone cannot establish progress across repeated transient CLI processes. Durable logical discovery/reference progress must be implemented and verified, or the affected convergence qualification remains pending; no new daemon, silent routing change or reduced acceptance scope has been approved. + +The narrow S5 production contract run passed two external tests with zero skips (run `9c9f4e71-b6aa-425a-8ed1-910eb25df0e2`, JUnit SHA-256 `8baac7efb57e9919f20d5ae9c019fa688fbf8a9ffb1b566053bfb02e90abe6c6`). Its fresh independent Max review found evidence-audit and source-provenance gaps, with original execution archives clean. Both narrow findings are independently verified as corrected. A fresh capture records 145 source identities, including the actual parser, unchanged before/build/after execution; binary SHA-256 `eeac63536eaf120f28533f201720cab4ee7769755d6eaafaa81ba3c069092a76`; run `5f3b8e10-9d6f-414b-a4ec-6a910655d899` passed two tests with zero skips. The evidence audit now checks actual named-case outcomes, exact external case/run identity and all outcome counts. Ten audit regressions passed, including skipped-evidence rejection and lossless archival tamper checks. Accepted narrow evidence commits are `9eaa8de` and `a313b4d`; raw probe bytes remain unchanged inside a checked deterministic gzip archive. These checks do not qualify collection or aggregate closure. + +A reviewed reusable lesson is recorded in [the Unicode-alias ownership memory](../memory/episodic/database-use-unicode-alias-parent-family-2026-10-21.md): filename normalization/hashing does not reproduce filesystem equivalence, and Direct addressing must remain distinct from physical lifetime coordination. + + +## Approved DC-S3/DC-S4 mutation budget — revision 0.2 + +**Status: approved on 2026-10-21 by the explicit user instruction above; revision 0.2 governs affected requirements.** A fresh GPT-6.1 Sol Max proposal review found this alternative coherent, subject to memory and admission prerequisites. This does not approve a reduced safety boundary or establish implementation readiness. The maintainer/user owns this decision under the repository rule requiring validation of acceptance-criteria changes. + +The revision-0.1 all-work cooperative budget includes discovery, reference preparation, validation, disposal, admission and mutation. Across transient CLI processes, preserving that budget requires durable logical discovery/reference progress and resumable validation; an opaque Serde stack, directory ordinal or work ID is insufficient. A private catalog/index design is under review, not implemented. Legacy bootstrap also needs an explicit compatible protocol: relocating unknown children is not authorized by a recognized prefix name. + +The approved amendment changes **DC-S4-R3/R4**, with corresponding verification for **DC-S3-R7/R8**, **DC-S4-A1/A2** and **DC-S5-A1/A2**. Existing stable identities, retention policy, confinement, ownership, quiescence, recovery and primary-result contracts remain. No public raw-native-handle choice is implied. + +| Option | Observable latency | Engineering and qualification consequence | +| --- | --- | --- | +| Keep revision 0.1 | All work cooperates with the shared 100-entry/100-ms attempt; individual filesystem calls can still overrun. | Implement durable discovery/reference progress and resumable parsing/disposal, including a separately reviewed legacy-bootstrap protocol. | +| Selected mutation budget (revision 0.2) | Advisory discovery/reference preparation and at most one payload validation/disposal may exceed 100 ms. Authoritative admission and mutations get a fresh 100-entry/100-ms cooperative budget afterward. | Removes strict-latency-driven iterator/parser-stack persistence. Standalone CLI/direct API runtime entries that request maintenance—health, graph query, materialization and refresh paths—may wait for full preparation and one validation before returning/exiting. No invocation, cancellation-completion or exit deadline is promised. | + +The approved contract is: + +- Advisory preparation and validation hold **no** destination writer, state, database-family-exclusive, artifact-exclusive or coordinator execution authority. Bound the number of jobs and the combined retained/temporary memory for references, decoded objects, indexes, buffers, results and disposal against the configured memory ceiling. The existing encoded-byte heuristic is not proof of this bound. +- Start a separate 100-entry/100-ms cooperative budget before fresh authoritative admission. Count independent filesystem entries; a directory containing many files is not one deletion entry. Large admission scans cannot be hidden under authority; current phase/containment proof still needs bounded certification or compaction. +- Reacquire actual guards and revalidate current epoch, cohort, configuration, containment and candidate identity. State-lock sections contain fixed-size checks and recoverable transitions; recursive deletion remains outside them. Stop starting mutation work once the allowance expires. +- In a persistent coordinator, health enqueues work and returns cached/current status. Advisory validation and disposal run without retaining a request/execution permit. Only the short mutation phase acquires the real dispatcher permit. Real graph reads and writer admission must remain possible while validation is paused. +- Transient standalone runtime entries listed above may finish a full advisory pass and one validation/disposal; repeated invocations use retained retirement journals and small durable selection metadata. A blocked first candidate must not monopolize every invocation. Longer preparation must not permanently consume every mutation allowance. +- Cancellation stops new work and checks available interruption points, then completes unwinding/disposal. Acknowledging cancellation is distinct from completed shutdown. Disposal or an in-flight filesystem operation can delay completion; workers cannot escape controller shutdown. +- Keep the original active graph/cache, unknown/foreign preservation, legacy operator confirmation, exact-incarnation journals and primary build-result guarantees. CPU and scan-I/O contention remain possible even while graph admission is available. No new daemon or silent routing change is introduced. + +Additional required acceptance evidence: preparation exceeding 100 ms followed by real bounded mutation progress; no entry 101; stale epoch rejection after publication during preparation; real MCP reads/writer admission while validation/disposal is paused; repeated transient invocations converging across several batches with blocked-first candidates; enforced aggregate memory and completed cancellation/drain. Existing interruption, containment, confinement and primary-result regressions remain required. These are planned methods, not executed outcomes. + +The user selected this amendment and the corresponding Scryer execution briefs/sign-off are updated under `chg-6vx5tj`. The private implementation design still passes the required fresh proposal review before dependent coding. Existing foundations remain verified at their recorded snapshots; new runtime behavior and acceptance evidence remain to be delivered. + + +## Reviewed epoch and post-publication corrections + +The expanded epoch foundation's original 225-test report passed an independent exact-source rebuild. Review then reproduced an availability regression: optional cleanup admitted through a corrupt epoch control could block valid committed Managed/Direct reads. The corrected frozen owned identity `235548b4195215d9781ed885b424a34884694fa2b2e3e4d940afdcc7ca0d8b0f` passed 248 local tests, including actual runtime-resolution branches, corrupt/read-only/exhausted controls, retained residue, truthful pending status, and strict producer/journal-replay negatives. Independent fix verification passed six focused tests and six separate control-mode probes with a verified rebuilt binary. Producer and ambiguous Direct replay failures remain strict. This is epoch-foundation qualification, not complete DC-S2 or database-retirement authority. + +S4's narrow post-publication snapshot preserves S2 hooks and removes authorityless artifact deletion. Only Direct workspace disposal after completed publication/journaling/readback/validation is converted to separate pending diagnostics; publication and validation failures retain their existing errors. The optional native response maintenance field defaults to None when absent. Independent rebuilt execution passed 17 tests, run `ce7bf792-cc80-48fa-ac01-54ce87aba79e`, JUnit SHA-256 `3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216`. The mixed Managed recovery/cleanup error path remains an explicit integration gate; DC-S4-R7 is not fully qualified. + +A separate test-provenance issue was caught and corrected: an initial shared-target run reused a different test inventory despite the same selected pass count. That report is preserved but excluded from frozen-snapshot qualification. A forced root-package build from the verified reconstruction restored the expected inventory. Follow [the frozen Rust binary provenance runbook](../memory/procedural/shared-cargo-target-frozen-binary-provenance-2026-10-21.md); source hashes, compilation path, binary identity and test inventory establish different parts of the evidence chain. + +The clean combined foundation is committed as `e96893e025eda512b13f2a9ba754adbac45f4a49` against main `018df35e72447fc3e014e950d1d3c6648bbd4820`. All 137 source identities match the tested frozen candidate. Formatting, whitespace and workspace/all-target checks passed; 295 affected tests passed with zero skips in run `7f5466e4-cad5-4aeb-96cb-2390256fb386` (JUnit SHA-256 `3562f1cb18f27b76ecd94f3b71770d2659b91836e24c37f9bebc7c1c2adb8e76`). The all-target check still reports 75 unused foundation warnings. Exact commands, source identities, raw execution archives and limitations are in repository artifact `docs/testing/graph-disk-cleanup-foundation-integration.json`. Unqualified S3 validator, parked-job and catalog prototypes are excluded from shipping source and preserved separately as recovery evidence. + +Scryer locally folds the reference-mutation producer responsibility `node-yp5307` / `resp-ejzb4s` with verified implementation anchors and 20 attached executed tests. The combined report has been ingested against the current architecture metadata; 41 responsibilities have actual verdicts. The open change, remaining pending claims, drift scopes and unrelated missing test verdicts are retained. This local fold does not qualify collector seals, raw native handles, automatic scheduling or aggregate closure. + +A separate fresh GPT-6.1 Sol Max integrated review of `e96893e` against `018df35e` confirmed one P2 availability defect: ordinary Direct reads required creating or decoding producer-overlap registration. Identical isolated storage probes passed 3/3 on the baseline and 1/3 on the reviewed snapshot. The correction is committed as `077a8d8e1e75dbca553a4d9b0f65412ca9c5def1`. Ordinary reads retain writer, resource and physical-parent-family lifetime guards while leaving missing/corrupt registration untouched; producers and actual journal replay remain strict. This restores the approved reader-availability and unknown-retention contracts without changing architectural scope. + +Focused independent verification passed the three original counterexamples, four shipping registration/publication-lifecycle regressions and one valid Prepared-journal probe. The latter proves corrupt registration blocks mutation before watched bytes/identities change, then restores registration only in the fixture and replays the same untouched valid journal successfully. These are storage-admission fixtures with synthetic native bytes; no new native query or platform qualification is implied. A metadata-only correction distinguishes lexical path ordering from path-component ordering; all 137 per-file source hashes matched throughout. The reviewer verified the fix and reported no new confirmed P0–P3 code defects in the reviewed foundations. + +Final combined verification against the committed source passed 299 affected test executions with zero failures/errors/JUnit skips, run `bda126c4-1f9e-4980-9545-acf34ec6de22`, JUnit SHA-256 `698d41772c871f1398e5cf926c415a93af41ef65bd73b298220323266a7e761f`. The 196 tracked Rust/manifests and 12 compiled binary identities were unchanged before/after execution. Formatting and workspace/all-target checks passed; 75 unused foundation warnings remain. The initial sandbox-blocked run (285 pass, 14 local-listener permission failures) is preserved separately; the passing rerun used authorized loopback fixtures with no source changes. The final report was ingested into Scryer for 41 actual claim verdicts; unrelated missing verdicts, pending claims and the open change remain. See repository artifact `docs/testing/graph-disk-cleanup-foundation-integration.json`, especially `final_verified_snapshot`, for distinct historical and current evidence. + +The formerly outstanding native-handle and budget decisions are resolved by the user's revision-0.2 approval recorded above. No automatic collection, legacy migration, live deletion, warning-free final check, native-platform closure or aggregate completion is claimed. The source branch is a reviewed foundation checkpoint and remains unsuitable for release until dependent implementation and qualification gates are satisfied. + + +## Foundation handoff — draft PR 153 + +The reviewed foundation branch is published as [draft PR #153](https://github.com/rabii-chaarani/codebaseGraph/pull/153), `codex/automatic-graph-disk-cleanup`. At that foundation handoff, the last code-changing commit was `077a8d8e1e75dbca553a4d9b0f65412ca9c5def1`; later continuation evidence is recorded below. The PR remains a draft with revision-0.2 choices approved and dependent implementation/qualification gates above open. The active Scryer change remains `chg-6vx5tj`; no aggregate or release closure has been asserted. No live shutdown, upgrade, migration or deletion occurred. + + +## Revision 0.2 continuation — reviewed implementation checkpoints + +The user approved both the guarded Rust native database wrapper and the mutation-only cleanup budget on 2026-10-21, and requested a fresh implementation session plus a separate fresh review session for every remaining step. No product decision remains open. Concrete private execution briefs and remaining prerequisites are maintained in Scryer change `chg-6vx5tj`; the public retention, confinement and recovery acceptance criteria remain applicable. + +Commit `9836234` adds only the shared fallible memory account, nontransferable 100-entry/100-ms mutation grant, fixed-buffer lexical JSON facts and pure fair cursor codec. A separate fresh GPT-6.1 Sol / Max implementation review reported no confirmed P0–P3 defects. Independent execution passed 66 selected library tests plus allocator-failure/concurrency, lexical/depth and cursor probes. All 427 captured source inputs were unchanged. The account measures requested layouts and explicit reservations; it does not establish whole-process RSS or account unreserved caller allocations. Lexical facts and cursor bytes confer no origin, usability, reference or deletion authority. Evidence is in repository directory `docs/testing/graph-disk-cleanup-v2-evidence/`, including `s3-facts-review.md`, `s3-facts-review-evidence.json` and author/independent JUnit reports. Four narrow Scryer symbol claims are locally folded with actual test evidence; full collector claims remain pending. + +Commit `7b0ad57` changes public Rust database open helpers to return `GuardedDatabase`, retaining physical resource/family exclusion until the native database closes. Two internal raw native opens now use the same guarded constructor while preserving their existing native configuration. Fresh Max review reproduced a native tilde/path identity mismatch, then a Windows canonical-prefix compatibility regression; both were corrected before acceptance. The exact five-file successor patch is SHA-256 `c0d22b8bcbbdd6b838dac8506e29cf1dd698b87f924a22b1152df622a806211b`. The independent review passed 29 serial runtime tests, three actual doctests and formatting with 424 unchanged inputs; no confirmed P0–P3 defects remained in that reviewed scope. Evidence includes `s2-wrapper-review.md`, `s2-wrapper-review-evidence.json` and `s2-wrapper-review-junit.xml`. Windows-specific code was neither compiled nor executed on this Darwin host. Author parallel LEAK observations remain archived and unexplained; successful serial runs do not resolve their cause. Native Windows/Linux qualification remains pending. The GuardedDatabase lifetime claim is locally folded, without closing whole DC-S2 authority. + +Remaining implementation includes complete bounded admission, origin/usability validation, receipt and resumable retirement publication, actual collection, automatic lifecycle wiring and full qualification. The independent driver foundation and qualification harness are in progress; they do not establish runtime collection. No live daemon shutdown, installation upgrade, migration or deletion has occurred. Aggregate completion, warning-free final checks, supported-platform qualification and release remain open. + + +## Reviewed continuation — authority, validation, lifecycle and evidence + +The implementation remains in progress under the same approved requirements and Scryer change `chg-6vx5tj`. Delivery tracking in the existing CodebaseGraph project is [DC-S1 RAB-6](https://linear.app/rabii/issue/RAB-6), [DC-S2 RAB-7](https://linear.app/rabii/issue/RAB-7), [DC-S3 RAB-8](https://linear.app/rabii/issue/RAB-8), [DC-S4 RAB-9](https://linear.app/rabii/issue/RAB-9) and [DC-S5 RAB-10](https://linear.app/rabii/issue/RAB-10). These records track delivery; they neither replace the canonical wiki specification nor close aggregate qualification. + +- `993212c` supplies private noncreating fixed stamps and the canonical E→E+1 epoch transition. A separate fresh Max reviewer independently passed 76 serial tests, including 19 new bounded-control cases, with unchanged inputs and no confirmed P0–P3 finding. This is not a complete Ready/admission or deletion API. +- `2ad634f` supplies bounded schema/producer-form facts, exact raw-row joins, and pure receipt/journal reducers. Independent Max verification passed 78 tests and numeric, depth, collision, memory and crash-state differential probes. Actual root runtime features, rather than Cargo.lock membership or build-dependency features, govern the pinned JSON oracle. Facts alone confer no origin or deletion authority. The strict file/span comparison successor is integrated at `74b0527` after separate review, including validation of both complete strings after an early ordering difference. +- `956b46c` supplies the joined maintenance-driver primitive. The author passed 31 real driver/coordinator tests; independent review verified 19 exact-source focused cases and requested-layout allocation probes. Review-found early terminal status, completed-attempt history changes, uncharged startup storage and panic-payload disposal gaps were fixed and rechecked. Full collector wiring and refresh ownership remain separate work. Requested-layout/stack reservations are not a process-RSS guarantee. +- `dc5692f` binds qualification to actual fresh root compiler outputs and enabled debug assertions. Independent review verified both original stale-source and assertion-disabled counterexamples now fail closed, plus 24 harness checks and ten historical evidence audits. The new three-case narrow API capture includes an external symlink sentinel. This validates the harness and those API cases, not the unexecuted whole-state matrix. +- `45a9eae45236f6a1527c1173c93e2cb652f67b8d` binds artifact-root, epoch and publication proof to a genuinely created random nonce in the actual lock header. Independent verification passed 127 library tests, five real native storage cases and three doctests. All 462 source inputs and both executed binaries stayed unchanged. The original Linux regression was preserved; actual Linux and Windows-native qualification remain distinct pending gates. The combined accepted source passes workspace/all-target checking after registering the budget module in the synthetic storage harness; its 216 unused foundation warnings remain a final integration gate. + +Durable reports, source/binary identities, JUnit results, independent probes and historical failed attempts are preserved under repository directory `docs/testing/graph-disk-cleanup-v2-evidence/`, indexed by `reviewed-foundations-continuation-manifest.json` and `nonce-and-harness-manifest.json`. Each report distinguishes author execution, independent execution, source review and platform limits. Original hashed reports are retained when an additive qualification corrects overbroad wording. + +Modern completed producer origin and loader usability are distinct. Exact unchanged producer bytes with strict owned-leaf shape and complete fresh absence may be retired even when loader-incompatible; restoration still requires LoaderUsable. Unknown origin, mismatched integrity and unresolved recovery need remain protected. Detailed prerequisite protocols and file ownership remain only in Scryer. No live shutdown, upgrade, migration or graph-data deletion has occurred, and no automatic-collector, warning-free, supported-platform or aggregate completion is asserted. + + +## Additional reviewed checkpoints — native facts and lifetime ownership + +`a8b1c5b` adds actual live producer-capability borrows and pure fixed recovery-slot codecs. The author passed 133 library and six synthetic-harness cases; separate Max verification checked actual guards, nontransferable traits, reservation limits and every partial-write boundary. The accounting-test correction preserved production bytes and the same configured ceiling. No complete factory, admission or deletion authority is implied. + +`73eaf71c3c487ef3b52d19d48808d10bd400b939` adds explicit refresh cancellation, unique runtime join ownership, concurrent-drainer completion and the monitor's final-owner exit. Independent execution passed the same 61 distinct lifecycle/coordinator/facade/worker cases as the author, 20 corrected synchronization-fixture repetitions and two focused concurrency probes. Initial compile and fixture failures remain archived with their fixes. This closes the reviewed lifetime slice; actual collection, typed primary-result reporting and full S4 qualification remain pending. + +`bc43136` corrects Windows canonical-prefix observation while retaining raw Direct destination spelling and symlink/ancestor checks. Review found and corrected a prefix-only metadata bypass before acceptance. The successor passed 133 Darwin tests plus independent prefix/helper checks. Injected UNC metadata tests establish decision logic; actual successful UNC-share metadata and Windows execution remain separate qualification. Native Linux CI passed the earlier nonce checkpoint `45a9eae`; full final platform qualification is still required. + +`61c9d26d52547bb52730fca3305b7e11863044d5` adds bounded native header/page-range facts and complete NativeUtf8/JSON logical-key comparison and hashing. Author execution passed 124 selected tests; independent review passed 34 copied-module tests, 403 header checks and 433 logical-key checks with unchanged inputs. Header facts describe individual physical ranges and are not native-profile, origin, loader-usability or deletion proof. The complete selected-column reader and authority composition have separate gates. Synthetic databases and query oracles were produced by the actual public pipeline; no live graph payload was copied into fixtures. + +These reports and failure histories are indexed by `s2-cap-controls-manifest.json`, `s3-fd-manifest.json`, `s4-refresh-manifest.json` and `windows-and-native-facts-manifest.json` under `docs/testing/graph-disk-cleanup-v2-evidence/`. Historical proposal snapshots in that evidence directory are review inputs; Scryer alone retains current execution briefs and ownership. Linear synchronization is pending after the workspace integration mapped CodebaseGraph issues into an unrelated GitHub repository; further writes were stopped at the user's direction. Approved implementation continues independently. No live shutdown, upgrade, migration or graph-data deletion has occurred. + + +## Completion workflow — user direction 2026-10-21 + +The user directed completion of all approved steps, with only one final review and a push to existing PR #153. This replaces repeated intermediate review rounds; approved behavior and acceptance criteria remain unchanged. The current implementation assignments and private interfaces remain in Scryer. Historical continuation evidence through `61c9d26` is preserved byte-for-byte in `docs/testing/graph-disk-cleanup-v2-evidence/continuation-foundations-61c9d26.tar.gz`, indexed by `continuation-evidence.json`. Extract that archive into the evidence directory to resolve the six continuation manifests' original relative paths. Its internal `archive-members.json` records every member identity. This historical archive does not establish final collector or platform qualification. diff --git a/knowledge/architecture/graph-disk-cleanup-spec-reviews-2026-10-20.md b/knowledge/architecture/graph-disk-cleanup-spec-reviews-2026-10-20.md new file mode 100644 index 00000000..d0f8b06f --- /dev/null +++ b/knowledge/architecture/graph-disk-cleanup-spec-reviews-2026-10-20.md @@ -0,0 +1,107 @@ +--- +description: Five fresh GPT-6.1 Sol Max reviews of DC-S1–DC-S5 revision 0.1, with no confirmed defects and explicit implementation risks and pending decisions. +tags: +- graph-storage +- cleanup +- specifications +- review +- acceptance +timestamp: 2026-10-20 +title: Independent reviews of the five disk-cleanup specifications — 2026-10-20 +type: review +--- +# Independent reviews of the five disk-cleanup specifications — 2026-10-20 + +## Outcome and review boundary + +Five fresh independent reviewers assessed the five steps in [Automatic graph disk cleanup](./graph-disk-cleanup-contract.md), revision 0.1, one reviewer per step. All five returned **no confirmed P0–P3 specification findings** and considered their assigned draft suitable for its scheduled individual user validation. This is not user approval, implementation clearance, proof of a working collector or rollout qualification. + +The document was held unchanged throughout review. Its SHA-256 was `6a89ae66f5d089f39740d99a0b74a9b249616638601bd99511f5c925c15c7946`. The step requirements, proposed defaults and pending approval table were not edited. The user requested five new review sessions without the current context; each reviewer was spawned with **`fork_turns: "none"`**, received only a self-contained assignment and saved source references, and did not receive other reviewers' findings. + +Every reviewer used **GPT-6.1 Sol at Max effort**, the required adversarial-review skill, and a read-only assignment. The coordinator's Scryer ledger was `chg-gfsr7b`. No architecture claim was changed or marked implemented. Scryer remains the authority for architecture and future execution briefs; this page records review outcomes and recommendations. + +| Specification | Independent reviewer | Verdict | Confirmed findings | +| --- | --- | --- | --- | +| DC-S1 — Inventory and retention | `/root/review_cleanup_spec_1` | Suitable for user validation | None | +| DC-S2 — Ownership and recovery authority | `/root/review_cleanup_spec_2` | Suitable for user validation | None | +| DC-S3 — Collection and migration | `/root/review_cleanup_spec_3` | Suitable for user validation after prerequisite decisions | None | +| DC-S4 — Automatic operation and reporting | `/root/review_cleanup_spec_4` | Suitable for its scheduled validation after DC-S3 | None | +| DC-S5 — Qualification and rollout gates | `/root/review_cleanup_spec_5` | Suitable for its scheduled validation after DC-S1–S4 | None | + +The review comments below are conditional implementation risks, verification recommendations or decisions already deliberately pending in the specification. They are not scored defects. The reviewers actively checked the shared safeguards before classifying them. + +## DC-S1 — Inventory and retention + +The reviewer found the read-only boundary explicit in R2, conservative unknown-state handling in R4/R8, and stale-inventory protection in R7. No missing implementation was treated as a specification defect. + +Two conditions belong in the future Scryer execution brief and verification: + +1. **Use observational resolution.** At source HEAD `6aa5ba8bd99c3071e106a8bfc7a408652a8e4bb5`, normal runtime resolution can trigger managed cleanup (`src/api/context.rs:634`; `src/storage/managed.rs:410`), Direct recovery (`src/storage/direct.rs:107`), and retirement on generation-lease drop (`src/storage/managed.rs:93`). Reusing those paths unchanged would violate R2; the spec does not require their use. Non-creating observation must also avoid side effects during interruption or handle release. Extend A2's before/after checks with recoverable journals, orphan runs and superseded generations, rather than only clean state. +2. **Do not infer full consistency from the active pointer alone.** Build/run/candidate state can change before publication (`src/storage/managed.rs:341`) while the active generation remains constant. R7 already requires moving/partial/busy reporting when coherence cannot be established. Inject artifact/run mutations with a fixed pointer to verify this condition. + +The reviewer inspected source at HEAD only; the separately identified main revision was not counted as inspected or installed evidence. Checks were static, with successful graph discovery before source reads and source/hash verification. No tests or fixtures ran. + +## DC-S2 — Ownership and recovery authority + +The reviewer found destination-specific ownership, reader/run/containment protection, publication recovery, writer-to-state ordering, held-lease reuse and conservative historical-helper treatment coherent. The linked [worker recovery contract](./worker-recovery-regression-contract.md) already separates the relevant ownership boundaries. + +Conditions and decisions: + +1. **Pin baseline and legacy quiescence.** The exact implementation revision containing the approved recovery work and the procedure proving historical consumers stopped remain explicit DC-S2 decisions. Preserve-and-report is safe if proof is unavailable; it cannot be presented as completed retirement. +2. **Maintain authority through detachment.** The execution brief must define the lifetime of worker/phase/writer authority, including startup before ordinary writer admission. An observation followed by releasing ownership cannot become a later deletion permit. Exercise deterministic reader/run/phase startup between observation and detachment. +3. **Handle physical aliases conservatively.** The Direct destination key derives from database/manifest path spellings at both reviewed refs: `src/storage/layout.rs:295` at HEAD and `src/storage/layout.rs:303–318` at main `512b5dc197adf5590afa5d2f7b44550dfa32d8a2`. One spelling's lock does not automatically exclude every physical alias. Preserve existing journal keys; test aliases and defer ambiguous ownership. +4. **Retain established recovery qualifications.** Preserve managed last-generation readability while respecting blocked ambiguous Direct replay. Apply A3 consistently with already approved automatic retirement of v1 PID-only records; not all legacy metadata is permanently unknown. + +The reviewer inspected both supplied source revisions. Repeated graph-MCP busy errors required the permitted repository-local graph CLI with `--no-refresh` for discovery before source inspection. The older checkout's committed Scryer model lacked main's referenced Guardian node; this supports the baseline gate and limits older-model verification. No runtime tests or mutation occurred. + +## DC-S3 — Collection and migration + +The reviewer found current-reference recomputation, preservation of required backups, durable retirement-before-destruction, bounded detachment, stable lock protection and conservative confinement sufficient to defeat the principal unsafe-cleanup hypotheses. + +Conditions and verification refinements: + +1. **Bind resumed deletion to the retired incarnation.** If old Direct retirement is interrupted and the same destination is explicitly rebuilt, an old continuation must not delete the fresh files. Durable coherent detachment already prohibits that outcome. Add a scenario that interrupts retirement, rebuilds the pair, resumes old deletion twice and verifies the new bundle stays readable. During DC-S3 validation, settle whether explicit rebuilding ends that destination's prior retirement eligibility. +2. **Reconcile overlapping Direct registrations.** Database and manifest overrides can be selected independently (`src/api/context.rs:518` at HEAD), while coordination identity hashes the pair. Different pairs can share a physical database yet derive different locks. The review did not establish that the future registry accepts overlapping pairs. The existing ambiguous-identity safeguards remain controlling. Detect overlap/aliases or preserve-and-report; verify that one pair's retirement cannot damage another supported pair. +3. **State the permission-repair precondition in convergence evidence.** Read A4 in two stages: persistent denial preserves data and reports pending work; after permissions are restored, retries converge. R8 already permits that safe outcome. This is an acceptance-clarity recommendation. + +The reviewer statically inspected artifact enumeration/replacement/validation, Direct identity/leases and managed retirement at HEAD and the separate main comparison. Artifact validation code was identical between the supplied refs, supporting policy feasibility without proving a future collector. No tests, reproductions or temporary files were created. + +## DC-S4 — Automatic operation and reporting + +The reviewer found trigger coverage, failed/no-op-only convergence, coalescing, writer exclusion, no rebuild loops, truthful pending/unknown output and durable shutdown continuation coherent. + +Conditions and user decisions: + +1. **Define the cooperative budget's scope.** R3 explicitly proposes a deletion-batch budget while R1/R4 require bounded progressing maintenance. The future brief should cover scanning, enumeration, classification and recoverable continuation as well as unlinking; otherwise foreground work could still be dominated by an unbounded stage. Use controlled-clock fixtures with many entries, an inaccessible subtree and eligible entries late in traversal. The stated soft-budget caveat remains valid. +2. **Separate pure observation from requesting maintenance.** Define which existing reporting paths merely return observations, request work, or wait for a bounded attempt. R2 of DC-S1 remains mutation-free. Check coordinator shutdown during scan/deletion and restart without another source edit. At HEAD, the current coordinator's execution admission and shutdown behavior are relevant (`src/coordinator.rs:174,884`). +3. **Validate representative public output.** Before implementation approval, supply CLI/MCP examples for success, partial observation, primary build failure plus cleanup pending, and retired-Direct rebuild-required results. Define unavailable allocation values, existing `cleanup_pending` scope and per-attempt versus cumulative reclamation estimates. Preserve compatible typed and compact output. The interval, batch defaults, configuration and public schema remain user decisions, not review findings. + +Coordinator and API-core source inspected at HEAD had identical Git blobs at the separately pinned main ref. Graph discovery initially encountered busy responses and later succeeded. No tests, reproductions or runtime changes occurred. + +## DC-S5 — Qualification and rollout gates + +The reviewer found whole-state clean-control comparison, content equivalence, cache reuse, deferred-state protection, fair progress, traceable executed evidence, individual approval and separate deployment gates coherent. + +Conditions and decisions: + +1. **Freeze the complete matrix and proposed churn workload.** Include more eligible entries than one batch, a persistently blocked entry ahead of them, failure/no-op-only triggers and recovery after contention disappears. This makes the existing fairness promise falsifiable. +2. **Bind native evidence precisely.** Identify the selected source revision, actual target/profile, named test registration and executed reports. The curated [release verification contract](./release-verification.md) supplies native target/profile context; no current platform execution was qualified by this review. +3. **Resolve actual deployment and quiescence.** Repository refs and passing tests do not identify the installed daemon/helper set or prove that old consumers stopped. Compatible installation, coordinated restart and historical quiescence remain rollout gates. + +This reviewer inspected the specification, related contracts, Scryer claims and graph context, but no implementation source. No tests, fault injection or runtime operations ran. + +## Source, method and limitations + +- HEAD verified at dispatch: `6aa5ba8bd99c3071e106a8bfc7a408652a8e4bb5`. +- Separate `origin/main` reference: `512b5dc197adf5590afa5d2f7b44550dfa32d8a2`. +- All five reviewers checked the identical specification hash and reported it unchanged at completion. No revision was assumed to identify an installed binary. +- All five performed static specification reviews. Some inspected scoped source; DC-S5 did not. No runtime tests, new benchmarks, load tests, actual process faults, rebuilds, daemon changes, spec edits or approval changes were performed. +- Busy graph discovery was a tool limitation, not a product finding. Source discovery followed repository rules; the DC-S2 reviewer used the documented fallback. +- No reviewer found new verified durable knowledge requiring a separate memory record. Existing storage/recovery memory already covers the applicable principles; conditional implementation comments were not promoted to established facts. +- Local machine-readable coordinator consolidation: `/private/tmp/codebasegraph-cleanup-spec-reviews-20261020-3ivqt6lr/reports.json`. Dispatch settings are alongside it in `dispatch.json`. These are temporary local evidence, not portable runtime qualification. + +## Handoff + +Keep the five step-validation gates in the [canonical specification](./graph-disk-cleanup-contract.md). Reviewer suitability does not populate the user-approval column. DC-S1 remains the first user validation; later decisions and proposed defaults are still pending. No specification revision was made as part of these reviews. + +Carry the conditional safeguards and refined acceptance cases into the appropriate future Scryer execution briefs and user decisions. If they require a material contract change, revise and revalidate the affected step. The final implementation still requires the repository's proposal/final-review, executed-test, native-qualification and closure gates; these five static specification reviews do not substitute for those later gates. diff --git a/knowledge/architecture/graph-disk-retention-review-2026-10-20.md b/knowledge/architecture/graph-disk-retention-review-2026-10-20.md new file mode 100644 index 00000000..06fddfec --- /dev/null +++ b/knowledge/architecture/graph-disk-retention-review-2026-10-20.md @@ -0,0 +1,144 @@ +--- +description: Adversarial findings, measured disk reachability, selected retention policy, and a safe automatic cleanup proposal at revision 6aa5ba8. +tags: +- artifacts +- cleanup +- graph-storage +- lifecycle +- review +title: Graph disk retention review and cleanup proposal — 2026-10-20 +type: review +--- +# Graph disk retention review and cleanup proposal — 2026-10-20 + +## Decision and status + +Review and proposal only. No program source was changed and no graph, staging, cache, database, journal, or lock was deleted. The user selected this retention policy on 2026-10-20: **keep the current managed graph and its active parsing cache; retire older repository-local Direct graphs**. Explicitly requesting a retired Direct destination later will require a rebuild. + +Scryer remains the authority for architecture and implementation briefs. This review used change `chg-138pdr` for investigation, Graph Store `node-88` and Materialization API `node-56`. Existing applicable claims are `resp-108` (complete publication), `resp-109` (reader/writer coordination), `resp-232` (abandoned workspace recovery), `resp-233` (lease-aware generation retirement), `resp-234` (validated partition reuse), and `resp-238` (complete candidate assembly). The cleanup proposal is not an implemented or qualified architecture claim. Detailed implementation ownership, interfaces, prerequisites and claim assignments belong in Scryer when implementation is requested. + +## Review boundary and evidence + +A fresh independent reviewer used **GPT-6.1 Sol, Max effort**, following the adversarial-review-engineer skill. The reviewed runtime/test source is HEAD `6aa5ba8bd99c3071e106a8bfc7a408652a8e4bb5` on `codex/fix-k-wiki-diagnostics`. Source and tests matched HEAD during this investigation. Pre-existing wiki edits and untracked work were preserved. + +The scope is graph state lifecycle: managed and Direct layouts, artifact writing/collection, staging ownership, publication, abort/no-op/recovery, leases, and relevant existing tests. This is not a whole-repository correctness or security audit. + +The coordinator measured filesystem allocated blocks and classified content by active-manifest reachability. The snapshot was taken at 11:11:59 am Australia/Darwin on 2026-10-20. The active pointer was unchanged throughout the scan, no scanned files vanished, and a new build was in progress. Counts and sizes are observations, not a permanently stable quota or a guarantee that every old file is immediately safe to delete. + +Local evidence: + +- Inventory: `/private/tmp/codebasegraph-disk-review-2026-10-20.json`, SHA-256 `9d8c6bcc9e916a617cb1557ccf1e23323f7f566a7b5685c26d73a811a9701151`. +- Source manifest: `/private/tmp/codebasegraph-disk-review-source-snapshot-2026-10-20.json`, SHA-256 `006420a7f9fcac48ffc30f8faf7b3e73c8e2500f8a994bf43be502f5254d6364`. +- Isolated reviewer harness: `/private/tmp/codebasegraph-storage-review-wihkqic4/review.rs` and `result.json`; harness SHA-256 `d68e65801ad9e7da286a2b9859ab1d7974698b74c20f17c07063a6c60c3d49d7`. + +Temporary evidence paths are local artifacts, not portable release qualification. + +## Why these directories exist + +| State | Allocated size | Meaning and disposition | +| --- | ---: | --- | +| `native-staging/run-*` | 49.395 GiB | 1,328 old staging directories, approximately 139,930 files. Directory modification dates are in August. They contain generated import JSON/CSV. Migrate and reclaim only after proving legacy ownership is quiescent. | +| Other `native-staging` contents | About 6 MiB | Old source snapshots and incidental directory metadata. Include recognized program-owned snapshot state in migration; do not treat arbitrary unknown content as disposable. | +| Active generation, database and search sidecars | 1.264 GiB | Current graph bundle. Retain atomically with its manifest/readiness/metadata. | +| Artifacts referenced by the active manifest | 2.255 GiB | 323 active source-partition keys. Useful, regenerable parsing data required to preserve current reuse behavior. Retain under the selected policy. | +| `storage/artifacts/.tmp-*` | 44.08 MiB | 54 abandoned temporary directories. Current artifact-key GC never enumerates them. | +| Other valid artifact keys at snapshot | 236 KiB | Only two keys; a live build may own them. Do not infer garbage from absence in the active manifest alone. | +| `storage/runs/run-*` at snapshot | 2.861 GiB | An in-flight staged run, including staging/candidate state. Size changes during normal full-generation construction. Preserve while live; recover on proven abandonment. | +| `.direct-artifacts-cf7528fe0cf5be9b` | 759.4 MiB | Separate cache for the former repository-local Direct database/manifest pair. Retire as a destination bundle under the accepted policy, using its own ownership lock. | +| Former root `manifest.json` | 140.6 MiB | Legacy schema-2 manifest containing 201 files. It accompanies the old Direct destination, not the active compact managed manifest. Include it in coherent retirement. | + +The Direct cache suffix exactly matches `DirectLayout::destination_key` for this repository's `.codebaseGraph/codebaseGraph_graph.ldb` and `.codebaseGraph/manifest.json`. `src/storage/layout.rs:280-305` derives cache roots from the database/manifest destination. Managed builds use `storage/artifacts` instead (`src/api/materialization.rs:846-870`). Merely being unreferenced by the managed configuration does not by itself prove a Direct destination has no live explicit reader or writer. + +The initial roughly 6.5 GiB managed-storage measurement combined the active generation, its useful parsing cache, and transient build space. It was not 6.5 GiB of redundant graph copies. At the measured workload, normal quiescent storage is approximately **3.52 GiB**, plus small coordination metadata. Temporary build capacity remains necessary. Roughly **50 GiB** is potentially recoverable historical/temporary state after safe migration and Direct retirement; the exact result depends on ownership checks and any changed active inputs. + +## Confirmed findings + +### P2 — Historical staging is outside the current cleanup namespace + +**Current locations:** `src/api/materialization.rs:285-290`, `src/storage/layout.rs:94-99`, `src/storage/managed.rs:410-423`. + +The old pipeline allocated a unique `native-staging/run---` directory. At historical revision `e4f5c95^`, `src/execution/run.rs:121-122` reached `staging_run.cleanup()` only after successful graph writing. Earlier errors returned through `?`; `src/staging_writer/run_directory.rs:10-41` had no Drop cleanup, and explicit cleanup ignored deletion errors. Forced process termination also bypassed cleanup. + +The current API redirects actual staging to a leased run workspace and cleans that workspace. Its managed janitor visits `storage/runs`, not the old root. Therefore upgrading the pipeline does not collect previously stranded directories; ordinary current cleanup cannot reclaim the observed historical staging. This is confirmed by the source trace plus the measured directory layout. The individual historical event that stranded each directory is not known. + +**Remedy:** A one-time, idempotent legacy-state migration integrated with storage maintenance, with verified quiescence and confined deletion. Current writer locks alone cannot prove that older helpers without those locks have stopped. Report an explicit blocked reason when proof is unavailable. + +**Verification:** Seed the historical layout, interrupt legacy-compatible staging, migrate, restart during collection, and verify eventual reclamation while preserving query results and every live/current workspace. + +### P2 — Artifact collection permanently overlooks interrupted temporary writes + +**Locations:** `src/artifact_store.rs:156-190,273-315,447-473`; `src/api/materialization.rs:1165-1188`. + +Artifact writes create a root-level `.tmp--` directory before publishing by rename. Process death or selected fallible sync/comparison operations can leave it behind. Replacement also briefly creates `.replaced-*` backups. The only artifact collector iterates `list_keys()`, which explicitly skips root-level temporary directories and accepts only valid hexadecimal artifact directory names beneath prefixes. Replacement leftovers are also invisible to that enumeration. + +The isolated harness used verbatim production enumeration, deletion, GC, confinement and sync functions with minimal stand-in types. It retained the active key and removed an obsolete valid key, but left both synthetic `.tmp-*` and `.replaced-*` residue after **two** successful collections. It also rejected a symlinked artifact root while preserving an external sentinel. This validates collection behavior; it did not inject a crash into the shipped binary. + +**Remedy:** Give transient artifact state an explicit storage-owned lifecycle and recover or collect proven-abandoned temp/replacement state under the same destination ownership protocol as its writers. If a referenced artifact is missing, reconcile a matching replacement backup before deciding that backup is disposable. + +**Verification:** Interrupt each write/replacement transition, then run maintenance repeatedly. Assert temporary bytes converge to zero once ownership is released, active artifacts remain usable, and live writers and foreign/symlinked paths survive. + +### P2 — Aborted artifacts are not retried by ordinary recovery or unchanged refresh + +**Locations:** `src/api/materialization.rs:323-337,482-503,1003-1004,1053-1057`; `src/storage/managed.rs:410-423`; `src/api/context.rs:634-668`. + +Artifact GC is called during successful finalization. Abort cleans candidates/workspaces but not shared artifact keys already written by the failed attempt. The runtime janitor recovers runs and retired generations without collecting artifacts; unchanged-refresh acknowledgement finishes without an artifact sweep. Consequently a failed build, or interruption after publication but before artifact GC, can leave obsolete keys until another fully successful publication. Health and no-op refresh can leave them indefinitely and do not account for them as artifact cleanup pending. + +Normal successful finalization does collect obsolete valid keys while retaining writer ownership; that safeguard defeats a claim that all current parsing artifacts leak. Most measured artifacts are current and should remain. + +**Remedy:** Schedule recoverable artifact maintenance independently of successful publication, including startup and terminal build paths, with paced retry for deferred work and honest category-level pending status. + +**Verification:** Fail after creating new artifacts, perform only recovery/health and unchanged refresh thereafter, and require eventual cleanup without a source edit or forced rebuild. Repeat for interruption after activation and before GC. + +## Safety conditions and counterevidence + +- Current generation retirement is lease-aware. `src/storage/managed.rs:718-771` excludes the active generation and requires an exclusive generation lease before deletion. Exactly one generation existed at the measured snapshot. +- The current source does **not** implement all guardian/containment guarantees described in the architecture documentation. See [Graph Freshness and Recovery](./graph-freshness-recovery.md) for intended recovery behavior and the later [worker recovery regression contract](./worker-recovery-regression-contract.md) for merged-repair qualification. Outer-worker termination can leave database-loader descendants. Expanding cleanup without proving those descendants empty can erase files still in use. Treat containment repair/verification as a prerequisite for automatic abandoned-run destruction, not as an already-present safeguard. +- Direct readers hold the destination's shared writer lease; retirement must acquire that **Direct destination's** exclusive lease, not just the managed-store lease. Keep stable lock files/inodes, including zero-byte coordination files. +- The public low-level materialization pipeline can receive caller-owned staging paths and returns COPY paths. Never turn an arbitrary `request.staging_dir` or user-specified database parent into a recursive deletion root. Automatic collection is limited to registered program-owned layouts. +- Old directory dates, missing PIDs and filename patterns are not sufficient deletion authority. Unknown ownership, versions or containment preserve state and expose a retryable reason. +- Large recursive removal must not run while holding the state lock needed by graph readers. Establish eligibility with short critical sections and delete eligible tombstoned state in bounded, restartable batches. + +## Proposed plan + +1. **Define one storage inventory and retention calculation.** Classify active bundles, leased retired generations, live runs, journal/containment evidence, active/live artifact references, temporary writes and approved retired destinations. Reconcile interrupted publication before computing reachability. Preserve active graph and source-cache behavior. Reject symlinks, traversal and unknown ownership. + +2. **Establish deletion authority before broadening collection.** Restore or verify whole-build containment and persisted proof across crash/cancel/recovery. Coordinate every supported writer to an automatically collected root. Maintain writer-to-state lock ordering and avoid reacquiring a writer lease already held by the caller. Acquire each retired Direct destination's own exclusive ownership. + +3. **Add recoverable collection and legacy migration.** Collect obsolete valid artifacts, proven-abandoned `.tmp-*` and dead run state; reconcile replacement backups before collecting `.replaced-*`, preserving a usable backup when its referenced destination is missing. Retire the approved old Direct database, manifest, sidecars and artifact cache coherently. Sweep recognized historical `native-staging` state only after a version-aware, verified quiescent migration. Use a same-root tombstone/retirement journal so interrupted deletions retry. Never remove lock inodes or promote old candidates accidentally. + +4. **Make maintenance automatic and observable.** Run bounded maintenance at startup, build completion, failure/cancellation, unchanged refresh and paced coordinator maintenance. A busy writer defers it. Cleanup failure preserves the primary build error and records retryable pending work. Cleanup must not trigger another graph build. Expose allocated bytes by category, reclaimable bytes, pending bytes and reasons such as live lease, live writer, unknown containment or filesystem error. No fixed total-size cap may delete necessary active inputs. + +5. **Qualify convergence and failure safety before rollout.** Extend existing lifecycle, artifact-reuse and generation-churn verification to whole-state allocated disk usage and the cases below. First offer a reviewable inventory/dry-run report, then exercise collection in isolated repositories. Only after acceptance should the approved migration run against this installation. Windows and macOS native lifecycle qualification remains distinct from a local function harness. + +## Acceptance and evidence plan + +| Criterion | Applicable existing claim | Observable check | Evidence status | +| --- | --- | --- | --- | +| Active graph and search remain identical across cleanup | resp-108, resp-109 | Search/context/query equivalence before/after; active bundle checksums where unchanged | Planned | +| Idle state contains one generation, no abandoned run/temp/replacement/legacy state | resp-232, resp-233 | Churn plus crash fixtures, repeated maintenance and whole-state allocated-byte comparison to a clean control | Planned | +| Current cache reuse survives cleanup | resp-234 | Unchanged source uses retained valid artifacts without reparsing | Planned | +| Concurrent work and reader leases are protected | resp-109, resp-233 | Paused writer/reader, active-pointer transition and destination-retirement interleavings | Planned | +| Interrupted cleanup converges without a new source edit | resp-232 | Process death around artifact/publish/tombstone steps; health/no-op-only/maintenance retries | Planned | +| No deletion outside registered roots | resp-109 plus established confinement contract | Symlink, traversal, foreign metadata, arbitrary low-level staging and missing-root fixtures | Harness verifies artifact root symlink rejection only; remaining cases planned | +| Retired Direct destinations follow the selected policy safely | New policy selected 2026-10-20; Scryer contract mapping required before implementation | Own-destination lock, coherent bundle retirement, later explicit old-destination rebuild behavior | Policy selected; implementation/qualification pending | +| Pending space is reported truthfully | Existing lifecycle context; additive observability claims require Scryer review | Failed-only builds, permission failures and unknown containment expose category/reason and later clear on recovery | Planned | + +The existing generation-churn test `tests/generation_storage.rs:178-250` checks one generation, zero runs, graph queryability and database-size convergence, but does not account for the entire state tree or historical and temporary artifact namespaces. The artifact GC unit test `src/api/materialization.rs:1401-1440` checks active/stale valid keys only. These tests are useful counterevidence for normal generation leaks but do not disprove the findings above. + +## Executed checks and limitations + +- Filesystem allocated/logical-size inventory and active-manifest reachability classification completed read-only. +- Source/history traces and source-to-Scryer mapping completed; current program source/tests were unchanged. +- Isolated extracted-function harness compiled with `rustc --edition=2021 -D warnings` and exited 0. Synthetic temporary/replacement leftovers survived two collections; the active key survived, obsolete key was deleted, and the symlink escape was rejected. +- No production crash/cancel injection, graph rebuild, load test, full suite or cross-platform qualification was performed for this review. No current-process attribution is claimed from metadata; process listing was sandbox-restricted. A final optional graph dependency query encountered a server transport failure after source discovery; it does not change the cited source traces. +- The executing installed daemon binary was not conclusively bound to this source revision. On-disk observations are reported separately from current-source findings. Installed config/Cargo metadata can be stale and are not binary provenance. +- Existing [storage lifecycle intent](./graph-storage-lifecycle.md), [materialization pipeline](./materialization-pipeline.md) and the [worker recovery contract](./worker-recovery-regression-contract.md) remain canonical context. No implementation has satisfied the proposed acceptance checks yet. + +## Handoff + +Recommend implementing the cleanup work with the selected retention policy, while treating legacy/Direct deletion authority and descendant containment as required safeguards. Preserve existing architecture and closure identities. Before implementation, author any necessary new policy/observability claims and detailed execution briefs in Scryer, obtain sign-off for changed intent, and follow the repository's large-implementation review and qualification gates. This review does not approve arbitrary directory deletion or claim those changes are already delivered. + + +## Specification validation follow-up — 2026-10-20 + +The [five-step cleanup specification](./graph-disk-cleanup-contract.md) defines stable steps DC-S1–DC-S5, observable requirements, failure/recovery behavior and acceptance checks. The user requested individual validation before implementation. Revision 0.1 of DC-S1 is proposed first; all five step approvals remain pending until explicitly recorded in that contract. The earlier retention-policy selection remains settled and does not approve the new step specifications. Scryer remains the authority for architecture and detailed implementation briefs; no implementation or live cleanup follows merely from authoring these drafts. diff --git a/knowledge/architecture/graph-runtime.md b/knowledge/architecture/graph-runtime.md index b695dbf4..af57c4af 100644 --- a/knowledge/architecture/graph-runtime.md +++ b/knowledge/architecture/graph-runtime.md @@ -23,7 +23,7 @@ The Graph Runtime is the product executable and embeddable library. It exposes o | Public boundary | Public API Contracts, Public API Facade, Unified API Core, Catalog Provider, Response Presenter | Define stable requests and responses, register operations once, dispatch them, and present typed or compact block output. | | Runtime preparation | Request Normalizer, Repository Runtime Resolver | Apply canonical defaults, reject invalid requests, resolve schema-v1 versus storage-v2 state, and select Managed or Direct storage mode. | | Application services | Graph Read Service, Materialization API, Materialization Worker, Repository Lifecycle Service, Repository Refresh Service | Execute graph reads, isolated generation builds, installation lifecycle, and refresh behavior independently of transport. | -| Build pipeline | Source Scanner, Execution Planner, Graph Writer, Search Index Builder, Database Phase Runner | Revalidate inputs, reuse or rebuild raw partitions, externally merge deterministic rows, build disk-backed search, and load a candidate within hard memory limits. | +| Build pipeline | Source Scanner, Execution Planner, Graph Writer, Search Index Builder, Database Phase Runner | Revalidate inputs, reuse or rebuild raw partitions, externally merge deterministic rows, build disk-backed search, and load a fresh candidate using streaming and batching optimizations without application workload ceilings. | | Persistence | Graph Store | Own immutable generation publication, read leases, abandoned-run recovery, retirement, direct-mode recovery, and partition artifacts. | ## Dependency direction diff --git a/knowledge/architecture/graph-storage-lifecycle.md b/knowledge/architecture/graph-storage-lifecycle.md index 18082cb6..c7fe407f 100644 --- a/knowledge/architecture/graph-storage-lifecycle.md +++ b/knowledge/architecture/graph-storage-lifecycle.md @@ -147,7 +147,7 @@ Health and materialization output expose: - pending run count and cleanup status; - physical and logical database sizes; - parsing, staging, search, and database phase high-water marks plus spill bytes; -- configured Rust and worker RSS limits; +- batching/concurrency settings and observed resource high-water marks; - refresh role, coordinator/leader PID, active worker PID, pending state, coalesced and overflow counts, deduplicated refreshes, and the latest no-op reason. A healthy idle managed store reports format v2, one active generation, zero run directories, and `cleanup_pending = false`. diff --git a/knowledge/architecture/materialization-pipeline.md b/knowledge/architecture/materialization-pipeline.md index 193911a2..63baf5c6 100644 --- a/knowledge/architecture/materialization-pipeline.md +++ b/knowledge/architecture/materialization-pipeline.md @@ -48,7 +48,7 @@ The Source Scanner hashes file metadata before retaining content. Required sourc ### 4. Reuse or build raw partitions -The Execution Planner computes an artifact key from repository identity, relative path, content hash, language, parser/profile/ontology versions, and artifact schema version. A fixed-size worker pool reserves memory fallibly, emits partitions in stable order, persists each raw artifact, and releases it. A source or artifact that cannot fit the working budget returns a structured `memory_budget_exceeded` failure. +The Execution Planner computes an artifact key from repository identity, relative path, content hash, language, parser/profile/ontology versions, and artifact schema version. A fixed-size worker pool reserves memory fallibly, emits partitions in stable order, persists each raw artifact, and releases it. Source, partition and artifact estimates do not impose application memory ceilings. Fallible allocation, I/O and native-engine failures still propagate normally. ### 5. Assemble deterministic candidate rows @@ -62,9 +62,13 @@ New generations build a generation-owned `disk_bm25_v1` sidecar from externally ### 7. Build and validate the database -Ladybug work is ordered into pre-COPY schema, bulk COPY, and post-COPY indexes. The complete MCP build runs in the Materialization Worker, and Ladybug phases run in nested short-lived children. Both supervisors sample every 25 ms. During a Ladybug phase, the phase supervisor adds a composable memory charge for the materialization parent and Ladybug child, then terminates the phase before the configured worker ceiling is exceeded. The Ladybug buffer pool starts at 256 MiB and may retry COPY/index pool exhaustion at 320 and 384 MiB, while the configured worker RSS limit remains the hard authority. +Ladybug work is ordered into pre-COPY schema, sequential per-file COPY, primary-key hash indexes, and post-COPY statements. The complete MCP build runs in the Materialization Worker, and Ladybug phases run in nested short-lived children. Supervision and containment preserve cancellation, owner-loss teardown and execution ownership without an application RSS ceiling. Ladybug selects its buffer pool through native defaults. Each mutating phase executes once; a reported mutation or checkpoint error makes the candidate ineligible for publication. Do not replay an uncertain statement, suppress a native error based on existing-object wording, or create a compensating index and continue. The storage owner abandons the candidate while preserving the published graph; an independent later build creates a fresh candidate. Automatic whole-candidate retries are a separate, unimplemented policy. -On macOS, supervision adds each process's current physical footprint from `proc_pid_rusage(...).ri_phys_footprint`. The cumulative `ri_resident_size` field is not a live measurement, while adding raw current RSS from parent and child double-counts their clean shared runtime mappings. Rust graph construction uses mimalloc and forces collection at the Ladybug boundary after dropping scan/planner state; platform-allocator relief covers foreign allocations. Structured budget failures include the parent/child memory split and active pool size. +Final JSON and connector CSV files use the existing spill-byte setting as a rotation target, including framing and headers. Each file has its own COPY statement; JSON list/glob consolidation is not assumed. A repeated primary-key node table receives its hash index after the first successful chunk and before the next chunk. A valid single row larger than the target occupies its own chunk rather than being rejected. Spill/merge uses bounded fan-in and an execution window; these optimizations do not enforce a total memory/disk ceiling. Physical allocation, format and corruption limits remain. Native errors keep bounded diagnostic input and phase context; cleanup errors remain secondary. + +Scope and approval are recorded in [CBG-COPY-01](../../docs/specs/CBG-COPY-01/spec.md) and its [approval record](../../docs/specs/CBG-COPY-01/approval.md). The [resource-ceiling amendment](../../docs/specs/CBG-COPY-01/resource-ceilings-amendment.md) removes workload enforcement and worker/Rust budget options while retaining spill/concurrency tuning and safety bounds. Local execution evidence is separate from native-platform and rollout qualification. + +On macOS, supervision adds each process's current physical footprint from `proc_pid_rusage(...).ri_phys_footprint`. The cumulative `ri_resident_size` field is not a live measurement, while adding raw current RSS from parent and child double-counts their clean shared runtime mappings. Rust graph construction uses mimalloc and forces collection at the Ladybug boundary after dropping scan/planner state; platform-allocator relief covers foreign allocations. Resource samples remain observations, not triggers for application budget termination. Managed mode writes the database, compact manifest v5, metadata, sidecar siblings, and readiness marker into a fresh candidate. The database is closed, reopened read-only, and checked before publication. Existing v4 generations remain readable; their next write performs one bounded full rebuild. @@ -82,7 +86,7 @@ The run journal records publication and explicit workspace cleanup. Artifact gar | Contract | Purpose | | --- | --- | -| Materialization input | Source root, storage target, refresh intent, memory limits, configuration, active manifest, and execution options. | +| Materialization input | Source root, storage target, refresh intent, batching/concurrency tuning, configuration, active manifest, and execution options. | | Source snapshot and manifest diff | Verified source snapshot plus rebuild, delete, reuse, and ignored decisions. | | Artifact key and raw partition | Durable parse output with every invalidation dimension encoded in its identity. | | Compact manifest v5 entry | Path, content hash, language, partition ID, artifact key, row counts, and timestamp. | diff --git a/knowledge/memory/episodic/database-use-unicode-alias-parent-family-2026-10-21.md b/knowledge/memory/episodic/database-use-unicode-alias-parent-family-2026-10-21.md new file mode 100644 index 00000000..f47f1bc7 --- /dev/null +++ b/knowledge/memory/episodic/database-use-unicode-alias-parent-family-2026-10-21.md @@ -0,0 +1,47 @@ +--- +agent_memory: + version: 1 + kind: episodic + scope: repository + status: active + owner: repository-maintainers + created_at: 2026-10-20T17:42:45Z + last_verified_at: 2026-10-20T17:42:45Z + verified_by: codex-coordinator + review_after: null + supersedes: [] + superseded_by: null + sources: + - kind: git-source + reference: c0741ce137e09dac309ac72f5a2c2243a92ef070:src/storage/maintenance_ownership.rs::DatabaseUseLease + content_hash: a3ab4989eb5432d099a2aa7657796ffc6f0b63c7615dccdd9a535bf5c149666a + - kind: test + reference: src/storage/maintenance_ownership.rs::dc_s2_family_macos_unicode_nfc_nfd_existing_and_creation_aliases_exclude; dc_s2_family_macos_long_s_consumer_blocks_ascii_direct_even_with_valid_record at c0741ce + content_hash: null + - kind: executed-junit + reference: docs/testing/graph-disk-cleanup-evidence/s2-family-focused-junit.xml + content_hash: 6118b539934bbec11a16cacaf410fe10b12aea7d4b9e17a5ef600dc1d7941876 + - kind: independent-probe + reference: /private/tmp/cleanup-s2-family-review-yahled4r/result.txt + content_hash: null + history: + - from: candidate + to: active + actor: codex-coordinator + at: 2026-10-20T17:42:45Z + reason: Verified candidate claim against committed c0741ce source, actual Darwin focused JUnit and independent physical-alias probe output. Explicitly excludes unresolved raw helper lifetime and unrun native platforms. +description: A native macOS alias reproduction required physical-parent family exclusion while preserving raw Direct journal addresses. +tags: +- disk-cleanup +- ownership +- storage +- unicode +timestamp: 2026-10-20T17:42:45Z +title: Database basename hashes do not cover filesystem Unicode aliases +type: agent-memory +--- +During the disk-cleanup guard review, native macOS resolved NFC/NFD spellings and ASCII s versus U+017F long s to the same singly linked database inode, while lowercasing and hashing the addressed basename produced different coordination names. A consumer through the alternate spelling could therefore bypass the old exclusive retirement proof even beside a valid destination cohort record. + +The reviewed correction retains shared physical-parent family ownership through every guarded database use, including absent-to-created resources and conversion into a session lock. Grouped exclusive acquisition deduplicates physical parents and retains all actual family/resource guards; replacing the parent or stable coordination inode invalidates the proof. This deliberately permits conservative same-parent deferral. Preserve existing raw Direct pair spelling for journal/writer keys rather than canonicalizing those persisted addresses as an alias fix. + +Verify actual filesystem aliases and full guard lifetime, not only string-case equality or Unix link counts. The focused Darwin tests and independent probes verified the fix at c0741ce; this does not establish lifetime safety for public raw native database helpers, which remain a separate unresolved API boundary, or qualify native Windows/Linux behavior. \ No newline at end of file diff --git a/knowledge/memory/episodic/guard-native-resource-after-sdk-path-projection-2026-10-21.md b/knowledge/memory/episodic/guard-native-resource-after-sdk-path-projection-2026-10-21.md new file mode 100644 index 00000000..f62eb450 --- /dev/null +++ b/knowledge/memory/episodic/guard-native-resource-after-sdk-path-projection-2026-10-21.md @@ -0,0 +1,47 @@ +--- +agent_memory: + version: 1 + kind: episodic + scope: repository + status: active + owner: repository-maintainers + created_at: 2026-10-21T01:45:48Z + last_verified_at: 2026-10-21T01:45:48Z + verified_by: codex-coordinator + review_after: null + supersedes: [] + superseded_by: null + sources: + - kind: git-source + reference: 7b0ad57ba26152c9fe19a9145a7c83c363f87b23:src/db_writer/access.rs::guarded_native_path + content_hash: 104d9ed3ecc6b3b3c3ecef64a16b462464465d6f7aa0cbbaa2dc3d9dbc8b2a1d + - kind: independent-review + reference: docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review.md + content_hash: null + - kind: executed-junit + reference: docs/testing/graph-disk-cleanup-v2-evidence/s2-wrapper-review-junit.xml + content_hash: b539f8fa4d2cc86f107177ed8951a5858797c34e70bb17e43dcf260c1b4a6fee + - kind: historical-native-probe + reference: /private/tmp/cleanup-s2-wrapper-review-ccjn2do9/tilde-probe.log + content_hash: null + history: + - from: candidate + to: active + actor: codex-coordinator + at: 2026-10-21T01:45:48Z + reason: Reviewed against exact committed wrapper/path source, historical real native tilde counterexample and independent final29case JUnit/report; qualification limits are explicit. +description: A native tilde reproduction showed that guarding the caller's path can exclude a different resource from the SDK's transformed path. +tags: +- graph-storage +- native-database +- ownership +- path-identity +timestamp: 2026-10-21T01:45:48Z +title: Bind database exclusion to the path actually opened by the native SDK +type: agent-memory +--- +During the guarded database review, the pinned Ladybug Rust SDK converted a Path through display(), while native path expansion interpreted a leading tilde and lexically normalized components. A real Darwin native query succeeded against a different physical parent from the one guarded using the caller's original spelling. Lifetime exclusion alone was therefore insufficient. + +The reviewed correction validates and captures one absolute native spelling before coordination, then uses that same spelling for both exclusion and native open. Reject ambiguous/non-UTF-8 forms rather than silently projecting them. Preserve existing raw Direct journal keys separately. Windows canonicalize returns extended namespaces: blanket rejection breaks ordinary resolver paths, while stripping prefixes can change length and parsing semantics. The correction preserves supported canonical prefixes and checks effective ANSI/OEM FileAPI encoding without changing process mode. + +Independent Darwin execution verified the final reviewed wrapper and path-rejection cases. Windows prefix/encoding correction was inspected statically only; native Windows/Linux qualification and the author's parallel output-handle LEAK observations remained open. Reuse the lesson by tracing the exact pinned SDK and native filename pipeline before choosing a resource identity, then test actual native open plus competing exclusion. \ No newline at end of file diff --git a/knowledge/memory/procedural/canonical-temp-parents-for-owned-workspace-fixtures-2026-10-24.md b/knowledge/memory/procedural/canonical-temp-parents-for-owned-workspace-fixtures-2026-10-24.md new file mode 100644 index 00000000..2cf4018f --- /dev/null +++ b/knowledge/memory/procedural/canonical-temp-parents-for-owned-workspace-fixtures-2026-10-24.md @@ -0,0 +1,50 @@ +--- +agent_memory: + version: 1 + kind: procedural + scope: repository + status: active + owner: repository-maintainers + created_at: 2026-10-24T09:45:53Z + last_verified_at: 2026-10-24T09:48:00Z + verified_by: codex + review_after: null + supersedes: [] + superseded_by: null + sources: + - kind: git-source + reference: 073f0fb91e8a866d31e5f01a07a20898c3eb2370:src/storage/inventory.rs::real_path + content_hash: null + - kind: executed-baseline + reference: /private/tmp/pr153-native-ci-baseline-macos.log; exact Managed recovery case fails before changes + content_hash: null + - kind: executed-comparison + reference: /private/tmp/pr153-native-ci-canonical-temp-probe.log; same baseline binary, TMPDIR=/private/tmp, exact case passes + content_hash: null + - kind: executed-junit + reference: /private/tmp/pr153-native-ci-nextest/ci-pr153/junit.xml; 31/31 passing + content_hash: 637fc9f29282bc598c24793ff402d88722b480debac405e49eb6b0512662e14a + - kind: source-snapshot + reference: PR153 isolated CI worktree src/storage/managed.rs + content_hash: 8090dfa8926c954b569f84eb9db507f09fee04f16c6e5659abe352b5b239937c + history: + - from: candidate + to: active + actor: codex + at: 2026-10-24T09:48:00Z + reason: Reverified the exact baseline failure, same-binary canonical-parent comparison, 31-case passing JUnit hash, unchanged fixed fixture source hash, and the baseline real_path invariant. The procedure preserves ownership checks and explicitly limits native evidence to Darwin ARM64. +description: macOS temporary paths can cross /var symlinks and leave strict origin factories untracked even though primary graph creation succeeds. +tags: +- graph-storage +- macos +- ownership +- testing +timestamp: 2026-10-24T09:45:53Z +title: Canonicalize temporary parents before testing owned workspace creation +type: agent-memory +--- +When a regression requires genuine workspace registration or terminal-retirement authority, resolve the existing temporary parent with `std::env::temp_dir().canonicalize()` before creating the new fixture namespace. On macOS the default temporary path may start at `/var`, while the actual directory is under `/private/var`. The existing `real_path` ownership check intentionally rejects symlinked ancestors; a successful primary write does not prove optional origin registration succeeded. + +PR #153 CI run 40074045438 exposed six such fixture failures. The exact baseline Managed-recovery case failed with the default temporary path and passed with an otherwise unchanged binary when its temporary parent was `/private/tmp`. Canonical-parent fixes then passed the six original failures and the surrounding 31-case native regression selection. Preserve the ownership checks and assert that fixtures obtained real `.workspace.binding`/owner scope before testing retirement. This evidence is Darwin ARM64; Windows/Linux qualification remains separate. + +The behavior remains within the approved [disk cleanup contract](../../architecture/graph-disk-cleanup-contract.md), especially DC-S2 ownership evidence and DC-S5 qualification. \ No newline at end of file diff --git a/knowledge/memory/procedural/large-self-hosting-rebuild-memory-budget-2026-08-28.md b/knowledge/memory/procedural/large-self-hosting-rebuild-memory-budget-2026-08-28.md index 16a81cff..f8102103 100644 --- a/knowledge/memory/procedural/large-self-hosting-rebuild-memory-budget-2026-08-28.md +++ b/knowledge/memory/procedural/large-self-hosting-rebuild-memory-budget-2026-08-28.md @@ -3,7 +3,7 @@ agent_memory: version: 1 kind: procedural scope: repository - status: active + status: quarantined owner: codex created_at: 2026-08-28T12:39:47+09:30 last_verified_at: 2026-08-28T12:39:47+09:30 @@ -30,6 +30,11 @@ agent_memory: actor: codex at: 2026-08-28T12:39:47+09:30 reason: Verified against the current materialization and artifact-load budget checks in source, the successful 1.7.0 full rebuild, and the persisted repository-local configuration. + - from: active + to: quarantined + actor: repository-maintainers + at: 2026-10-26T12:22:20.315228+00:00 + reason: User-approved CBG-COPY-01 R1 removes worker_memory_mib/rust_memory_mib enforcement and options. This historical recipe must not recommend retired flags for the new implementation; preserved for source-version history, not active operating guidance. description: The codebaseGraph repository’s own full graph exceeds the default two-worker memory split; a single worker with a larger bounded budget completes the rebuild. tags: - codebase-graph diff --git a/knowledge/memory/procedural/shared-cargo-target-frozen-binary-provenance-2026-10-21.md b/knowledge/memory/procedural/shared-cargo-target-frozen-binary-provenance-2026-10-21.md new file mode 100644 index 00000000..cec15d20 --- /dev/null +++ b/knowledge/memory/procedural/shared-cargo-target-frozen-binary-provenance-2026-10-21.md @@ -0,0 +1,44 @@ +--- +agent_memory: + version: 1 + kind: procedural + scope: repository + status: active + owner: repository-maintainers + created_at: 2026-10-20T18:36:45Z + last_verified_at: 2026-10-20T18:36:45Z + verified_by: codex-coordinator + review_after: null + supersedes: [] + superseded_by: null + sources: + - kind: independent-review-evidence + reference: /private/tmp/codebasegraph-s4-postpublication-review-et3thm39/review-evidence.json + content_hash: 6101c50b30b24228d473ff86ab98c4d6d16128fd8e98195705df595ea5d22729 + - kind: executed-junit + reference: /private/tmp/codebasegraph-s4-postpublication-review-et3thm39/nextest/s4-postpublication-review/junit.xml + content_hash: 3df4dc85b9039e597ac040cfca0615d7e4e0cbb05b60b2c94ea7d321c66b6216 + - kind: excluded-historical-report + reference: /private/tmp/codebasegraph-s4-postpublication-review-et3thm39/first-reused-target-junit.xml + content_hash: ed70ff0327920ea194f4df6fd76ceac547cd67db2ccdc03b96c06f8c9c4d408c + history: + - from: candidate + to: active + actor: codex-coordinator + at: 2026-10-20T18:36:45Z + reason: Reviewed against independent S4 exact-source manifest, explicit package-only rebuild command and source-specific passing JUnit; the ambiguous earlier report is separately preserved and excluded. Procedure is scoped to provenance checks, not a universal Cargo implementation diagnosis. +description: A shared Cargo target reused a test inventory from another checkout until the root package was explicitly rebuilt. +tags: +- cargo +- provenance +- reviews +- testing +timestamp: 2026-10-20T18:36:45Z +title: Verify the rebuilt binary for frozen Rust reviews +type: agent-memory +--- +When several checkouts share CARGO_TARGET_DIR, matching source hashes and a passing selected test count alone do not establish which frozen snapshot executed. In the 2026-10-21 S4 review, an initial no-rebuild run registered 559 tests while the frozen reconstruction expected 555. Both runs selected the same 17 passing cases. An explicit root-package rebuild from the reconstructed path restored the expected inventory; the first run was preserved and excluded from frozen-snapshot verification. + +For an independent reconstruction, retain the source/dependency manifest before execution, then force and verify an actual root-package compilation. A package-specific profile override such as --config 'profile.test.package.codebase-graph.codegen-units=128' can retain dependency caches; choose an uncached value when targets are shared and require the log's Compiling line to name the intended checkout. Reusing an override is not proof of a rebuild. Do not disable debug assertions or test-feature gates to change the cache key. + +Record the exact command/profile, root compilation line, binary hash and registered test inventory alongside JUnit and before/after source identities. Check named cases and inventory, not only selected pass totals. Preserve ambiguous or mismatched reports as historical evidence, explicitly excluded from source-specific qualification. Distinguish a rerun of an already captured binary from a fresh source compilation; both may be useful, but establish different facts. \ No newline at end of file diff --git a/release/install/install.ps1 b/release/install/install.ps1 index 7453c659..05950595 100644 --- a/release/install/install.ps1 +++ b/release/install/install.ps1 @@ -7,6 +7,11 @@ param( $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest +if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT -or + [Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString() -ne 'X64') { + throw 'This installer supports Windows x86_64. Use install.sh on macOS ARM64.' +} + function Get-ExpectedHashes { param([string]$ChecksumsPath) diff --git a/release/install/install.sh b/release/install/install.sh index fa0ccc03..c8d5c388 100644 --- a/release/install/install.sh +++ b/release/install/install.sh @@ -62,6 +62,11 @@ while [[ $# -gt 0 ]]; do esac done +case "$(uname -s):$(uname -m)" in + Darwin:arm64|Darwin:aarch64) ;; + *) echo 'Supported platforms: macOS ARM64 (install.sh) and Windows x86_64 (install.ps1).' >&2; exit 1 ;; +esac + source_dir="$(CDPATH='' cd -- "$source_dir" && pwd)" checksums_path="${source_dir}/checksums.txt" [[ -f "$checksums_path" ]] || { echo "missing checksums.txt in ${source_dir}" >&2; exit 1; } diff --git a/src/adapters/cli/dispatch.rs b/src/adapters/cli/dispatch.rs index 390e4532..9712cc06 100644 --- a/src/adapters/cli/dispatch.rs +++ b/src/adapters/cli/dispatch.rs @@ -68,6 +68,7 @@ fn run_graph_health(args: &[String], stdout: &mut W) -> Result<(), Str return Ok(()); } let response = execute_api_request(OperationRequest::Health(HealthRequest { + confirm_legacy_quiescence: options.confirm_legacy_quiescence, repo: repo_selector_from_options( options.repo_root, options.config, diff --git a/src/adapters/cli/format/help.rs b/src/adapters/cli/format/help.rs index 386a03c5..395398ec 100644 --- a/src/adapters/cli/format/help.rs +++ b/src/adapters/cli/format/help.rs @@ -11,15 +11,15 @@ pub(in crate::adapters::cli) fn mcp_install_help() -> &'static str { } pub(in crate::adapters::cli) fn materialize_help() -> &'static str { - "codebase-graph build\n\nUSAGE:\n codebase-graph build [--source-root |--repo-root ] [--db ] [--manifest ] [--mode full|changed] [--json]\n codebase-graph build --native-request [--manifest ] [--json]\n\nOPTIONS:\n --source-root Repository or source root override; auto-detected when omitted\n --repo-root Alias for --source-root\n --db Ladybug database path; defaults under .codebaseGraph\n --manifest Manifest path; defaults under .codebaseGraph\n --mode full or changed; defaults to changed\n --no-git Disable Git file discovery and scan the filesystem\n --git-diff Materialize files from git diff plus untracked files\n --git-base Revision used by --git-diff; defaults to HEAD\n --include Include only paths matching the glob; repeatable\n --exclude Exclude paths matching the glob; repeatable\n --parallel Parse independent files concurrently; default behavior\n --single-thread Disable concurrent parsing and build partitions serially\n --worker-memory-mib Worker RSS budget; defaults to config or 768\n --rust-memory-mib Rust working-state budget; defaults to config or 384\n --spill-chunk-mib External-sort chunk budget; defaults to config or 32\n --max-parallelism Materialization worker count; defaults to config or 2\n --progress Include progress events in JSON output\n --no-fts Skip FTS extension loading and index creation\n --native-request JSON NativeSyntaxMaterializationRequest payload\n --json Emit JSON output" + "codebase-graph build\n\nUSAGE:\n codebase-graph build [--source-root |--repo-root ] [--db ] [--manifest ] [--mode full|changed] [--json]\n codebase-graph build --native-request [--manifest ] [--json]\n\nOPTIONS:\n --source-root Repository or source root override; auto-detected when omitted\n --repo-root Alias for --source-root\n --db Ladybug database path; defaults under .codebaseGraph\n --manifest Manifest path; defaults under .codebaseGraph\n --mode full or changed; defaults to changed\n --no-git Disable Git file discovery and scan the filesystem\n --git-diff Materialize files from git diff plus untracked files\n --git-base Revision used by --git-diff; defaults to HEAD\n --include Include only paths matching the glob; repeatable\n --exclude Exclude paths matching the glob; repeatable\n --parallel Parse independent files concurrently; default behavior\n --single-thread Disable concurrent parsing and build partitions serially\n --spill-chunk-mib External-sort chunk size; defaults to config or 32\n --max-parallelism Worker count; defaults to config or 2\n --progress Include progress events in JSON output\n --no-fts Skip FTS extension loading and index creation\n --native-request JSON NativeSyntaxMaterializationRequest payload\n --json Emit JSON output" } pub(in crate::adapters::cli) fn plan_help() -> &'static str { - "codebase-graph plan\n\nUSAGE:\n codebase-graph plan [--source-root |--repo-root ] [--manifest ] [--mode full|changed] [--json]\n\nOPTIONS:\n --source-root Repository or source root override; auto-detected when omitted\n --repo-root Alias for --source-root\n --manifest Manifest path; defaults under .codebaseGraph\n --mode full or changed; defaults to changed\n --no-git Disable Git file discovery and scan the filesystem\n --git-diff Plan files from git diff plus untracked files\n --git-base Revision used by --git-diff; defaults to HEAD\n --include Include only paths matching the glob; repeatable\n --exclude Exclude paths matching the glob; repeatable\n --worker-memory-mib Worker RSS budget; defaults to config or 768\n --rust-memory-mib Rust working-state budget; defaults to config or 384\n --spill-chunk-mib External-sort chunk budget; defaults to config or 32\n --max-parallelism Materialization worker count; defaults to config or 2\n --native-request JSON NativeSyntaxMaterializationRequest payload\n --json Emit JSON output" + "codebase-graph plan\n\nUSAGE:\n codebase-graph plan [--source-root |--repo-root ] [--manifest ] [--mode full|changed] [--json]\n\nOPTIONS:\n --source-root Repository or source root override; auto-detected when omitted\n --repo-root Alias for --source-root\n --manifest Manifest path; defaults under .codebaseGraph\n --mode full or changed; defaults to changed\n --no-git Disable Git file discovery and scan the filesystem\n --git-diff Plan files from git diff plus untracked files\n --git-base Revision used by --git-diff; defaults to HEAD\n --include Include only paths matching the glob; repeatable\n --exclude Exclude paths matching the glob; repeatable\n --spill-chunk-mib External-sort chunk size; defaults to config or 32\n --max-parallelism Worker count; defaults to config or 2\n --native-request JSON NativeSyntaxMaterializationRequest payload\n --json Emit JSON output" } pub(in crate::adapters::cli) fn watch_help() -> &'static str { - "codebase-graph watch\n\nUSAGE:\n codebase-graph watch [--source-root |--repo-root ] [--mode full|changed] [--watch-backend auto|native|poll] [--poll-ms ] [--debounce-ms ]\n\nOPTIONS:\n --source-root Repository or source root override; auto-detected when omitted\n --repo-root Alias for --source-root\n --mode full or changed; defaults to changed\n --watch-backend auto, native, or poll; defaults to auto\n --poll-ms Poll interval for poll backend or auto fallback; defaults to 500\n --debounce-ms Quiet-window debounce interval in milliseconds; defaults to 250\n --max-iterations Stop after n refreshes, useful for tests\n --once Run one refresh immediately and exit\n --no-git Disable Git file discovery and scan the filesystem\n --git-diff Refresh files from git diff plus untracked files\n --git-base Revision used by --git-diff; defaults to HEAD\n --include Include only paths matching the glob; repeatable\n --exclude Exclude paths matching the glob; repeatable\n --parallel Parse independent files concurrently; default behavior\n --single-thread Disable concurrent parsing and build partitions serially\n --worker-memory-mib Worker RSS budget; defaults to config or 768\n --rust-memory-mib Rust working-state budget; defaults to config or 384\n --spill-chunk-mib External-sort chunk budget; defaults to config or 32\n --max-parallelism Materialization worker count; defaults to config or 2\n --progress Include progress events in JSON output" + "codebase-graph watch\n\nUSAGE:\n codebase-graph watch [--source-root |--repo-root ] [--mode full|changed] [--watch-backend auto|native|poll] [--poll-ms ] [--debounce-ms ]\n\nOPTIONS:\n --source-root Repository or source root override; auto-detected when omitted\n --repo-root Alias for --source-root\n --mode full or changed; defaults to changed\n --watch-backend auto, native, or poll; defaults to auto\n --poll-ms Poll interval; defaults to 500\n --debounce-ms Quiet-window debounce interval; defaults to 250\n --max-iterations Stop after n refreshes, useful for tests\n --once Run one refresh immediately and exit\n --no-git Disable Git file discovery and scan filesystem\n --git-diff Refresh files from git diff plus untracked files\n --git-base Revision used by --git-diff; defaults to HEAD\n --include Include only paths matching glob; repeatable\n --exclude Exclude paths matching glob; repeatable\n --parallel Parse independent files concurrently; default behavior\n --single-thread Disable concurrent parsing and build partitions serially\n --spill-chunk-mib External-sort chunk size; defaults to config or 32\n --max-parallelism Worker count; defaults to config or 2\n --progress Include progress events in JSON output" } pub(in crate::adapters::cli) fn setup_help() -> &'static str { @@ -35,7 +35,7 @@ pub(in crate::adapters::cli) fn uninstall_help() -> &'static str { } pub(in crate::adapters::cli) fn graph_health_help() -> &'static str { - "codebase-graph check-health\n\nUSAGE:\n codebase-graph check-health [--repo-root ] [--config ] [--db ] [--manifest ] [--json]\n\nOPTIONS:\n --repo-root Repository root override; auto-detected when omitted\n --config Setup config path; defaults to .codebaseGraph/config.json\n --db Ladybug database path override\n --manifest Manifest path override\n --json Emit JSON output" + "codebase-graph check-health\n\nUSAGE:\n codebase-graph check-health [--repo-root ] [--config ] [--db ] [--manifest ] [--confirm-legacy-quiescence] [--json]\n\nOPTIONS:\n --repo-root Repository root override; auto-detected when omitted\n --config Setup config path; defaults to .codebaseGraph/config.json\n --db Ladybug database path override\n --manifest Manifest path override\n --confirm-legacy-quiescence Confirm all old daemons/helpers were stopped and upgraded before this maintenance request; does not replace ownership checks\n --json Emit JSON output" } pub(in crate::adapters::cli) fn graph_schema_help() -> &'static str { diff --git a/src/adapters/cli/graph/options.rs b/src/adapters/cli/graph/options.rs index 4067818a..dda3b910 100644 --- a/src/adapters/cli/graph/options.rs +++ b/src/adapters/cli/graph/options.rs @@ -9,6 +9,7 @@ use std::path::PathBuf; #[derive(Debug)] pub(crate) struct HealthOptions { + pub(crate) confirm_legacy_quiescence: bool, pub(crate) repo_root: Option, pub(crate) config: Option, pub(crate) db: Option, @@ -20,6 +21,7 @@ pub(crate) struct HealthOptions { impl HealthOptions { pub(crate) fn parse(args: &[String]) -> Result { let mut options = Self { + confirm_legacy_quiescence: false, repo_root: None, config: None, db: None, @@ -62,6 +64,10 @@ impl HealthOptions { options.manifest = Some(PathBuf::from(value)); index += 2; } + "--confirm-legacy-quiescence" => { + options.confirm_legacy_quiescence = true; + index += 1; + } "--json" => { options.json = true; index += 1; @@ -584,3 +590,21 @@ impl GraphContextOptions { }) } } + +#[cfg(test)] +mod maintenance_tests { + use super::HealthOptions; + + #[test] + fn health_quiescence_confirmation_is_opt_in_and_documented() { + assert!(!HealthOptions::parse(&[]).unwrap().confirm_legacy_quiescence); + let arguments = vec!["--confirm-legacy-quiescence".into(), "--json".into()]; + let options = HealthOptions::parse(&arguments).unwrap(); + assert!(options.confirm_legacy_quiescence); + assert!(options.json); + let help = crate::adapters::cli::format::graph_health_help(); + assert!(help.contains("--confirm-legacy-quiescence")); + assert!(help.contains("stopped and upgraded")); + assert!(HealthOptions::parse(&["--confirm-legacy-quiescence=true".into()]).is_err()); + } +} diff --git a/src/adapters/cli/materialization_input.rs b/src/adapters/cli/materialization_input.rs index 8ded845f..3543786b 100644 --- a/src/adapters/cli/materialization_input.rs +++ b/src/adapters/cli/materialization_input.rs @@ -18,8 +18,6 @@ pub(crate) struct MaterializeOptions { pub(crate) exclude_patterns: Vec, pub(crate) candidate_paths: Vec, pub(crate) parallel: bool, - pub(crate) worker_memory_mib: Option, - pub(crate) rust_memory_mib: Option, pub(crate) spill_chunk_mib: Option, pub(crate) max_parallelism: Option, pub(crate) progress: bool, @@ -44,8 +42,6 @@ impl Default for MaterializeOptions { exclude_patterns: Vec::new(), candidate_paths: Vec::new(), parallel: true, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, progress: false, @@ -150,14 +146,6 @@ impl MaterializeOptions { options.parallel = true; index += 1; } - "--worker-memory-mib" => { - options.worker_memory_mib = Some(parse_positive_u64(args, index)?); - index += 2; - } - "--rust-memory-mib" => { - options.rust_memory_mib = Some(parse_positive_u64(args, index)?); - index += 2; - } "--spill-chunk-mib" => { options.spill_chunk_mib = Some(parse_positive_u64(args, index)?); index += 2; @@ -235,8 +223,6 @@ pub(in crate::adapters::cli) fn materialize_request( exclude_patterns: options.exclude_patterns.clone(), candidate_paths: options.candidate_paths.clone(), parallel: options.parallel, - worker_memory_mib: options.worker_memory_mib, - rust_memory_mib: options.rust_memory_mib, spill_chunk_mib: options.spill_chunk_mib, max_parallelism: options.max_parallelism, progress: options.progress, @@ -262,10 +248,6 @@ mod tests { "--include".to_string(), "src/**/*.rs".to_string(), "--single-thread".to_string(), - "--worker-memory-mib".to_string(), - "640".to_string(), - "--rust-memory-mib".to_string(), - "320".to_string(), "--spill-chunk-mib".to_string(), "16".to_string(), "--max-parallelism".to_string(), @@ -288,21 +270,18 @@ mod tests { assert!(request.git_diff); assert_eq!(request.include_patterns, ["src/**/*.rs"]); assert!(!request.parallel); - assert_eq!(request.worker_memory_mib, Some(640)); - assert_eq!(request.rust_memory_mib, Some(320)); assert_eq!(request.spill_chunk_mib, Some(16)); assert_eq!(request.max_parallelism, Some(3)); assert_eq!(request.output_format, OutputFormat::Block); } #[test] - fn materialize_memory_overrides_reject_zero_and_non_numeric_values() { - for args in [ - vec!["--worker-memory-mib".to_string(), "0".to_string()], - vec!["--max-parallelism".to_string(), "many".to_string()], - ] { - let error = MaterializeOptions::parse(&args).unwrap_err(); - assert!(error.contains("requires a positive integer")); + fn materialize_options_reject_removed_memory_flags_and_invalid_tuning() { + for flag in ["--worker-memory-mib", "--rust-memory-mib"] { + let error = MaterializeOptions::parse(&[flag.into(), "1".into()]).unwrap_err(); + assert!(error.contains(flag), "{error}"); } + assert!(MaterializeOptions::parse(&["--max-parallelism".into(), "many".into()]).is_err()); + assert!(MaterializeOptions::parse(&["--spill-chunk-mib".into(), "0".into()]).is_err()); } } diff --git a/src/adapters/cli/tests/dispatch_materialize.rs b/src/adapters/cli/tests/dispatch_materialize.rs index 9b744aff..e22e49ce 100644 --- a/src/adapters/cli/tests/dispatch_materialize.rs +++ b/src/adapters/cli/tests/dispatch_materialize.rs @@ -637,8 +637,6 @@ fn setup_materializes_graph_and_writes_config() { assert!(config["materialization"] .get("semantic_provider_mode") .is_none()); - assert_eq!(config["materialization"]["worker_memory_mib"], 768); - assert_eq!(config["materialization"]["rust_memory_mib"], 384); assert_eq!(config["materialization"]["spill_chunk_mib"], 32); assert_eq!(config["materialization"]["max_parallelism"], 2); assert_eq!(config["mcp"]["server_name"], "codebase_graph"); diff --git a/src/adapters/cli/tests/fixtures.rs b/src/adapters/cli/tests/fixtures.rs index 592a7037..81dbafac 100644 --- a/src/adapters/cli/tests/fixtures.rs +++ b/src/adapters/cli/tests/fixtures.rs @@ -77,8 +77,6 @@ pub(super) fn test_http_options(root: PathBuf, auth_token: Option<&str>) -> McpH api: None, refresh_policy: None, refresh_backend: None, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, }, diff --git a/src/adapters/cli/tests/graph.rs b/src/adapters/cli/tests/graph.rs index 6781856e..f911311d 100644 --- a/src/adapters/cli/tests/graph.rs +++ b/src/adapters/cli/tests/graph.rs @@ -635,6 +635,15 @@ fn graph_health_reports_native_database() { ) .unwrap(); + // Producer terminal roots remain registered until bounded collection. The + // legacy flag reports that known pending work even when no run is live. + let registered_terminal_work = fs::read_dir(root.join(".codebaseGraph/storage/runs")) + .unwrap() + .any(|entry| { + let path = entry.unwrap().path(); + path.join(".workspace.binding").is_file() + && path.join(".reference-scope.json").is_file() + }); let mut output = Vec::new(); run( [ @@ -656,7 +665,11 @@ fn graph_health_reports_native_database() { assert_eq!(value["writable"], true); assert!(value["active_generation"].as_str().is_some()); assert_eq!(value["pending_runs"], 0); - assert_eq!(value["cleanup_pending"], false); + assert!(value["cleanup_pending"].is_boolean()); + if registered_terminal_work { + assert_eq!(value["cleanup_pending"], true); + } + assert!(value["maintenance"]["pending"].is_boolean()); assert!(value["physical_database_bytes"].as_u64().unwrap() > 0); assert!(value["logical_database_bytes"].as_u64().unwrap() > 0); assert!( diff --git a/src/adapters/cli/tests/install.rs b/src/adapters/cli/tests/install.rs index 128db565..9df7e6e7 100644 --- a/src/adapters/cli/tests/install.rs +++ b/src/adapters/cli/tests/install.rs @@ -87,8 +87,6 @@ fn install_writes_schema_v3_managed_config_without_static_database_or_manifest_p assert_eq!(config["schema_version"], 3); assert_eq!(config["refresh"]["policy"], "leader"); assert_eq!(config["refresh"]["backend"], "auto"); - assert_eq!(config["materialization"]["worker_memory_mib"], 768); - assert_eq!(config["materialization"]["rust_memory_mib"], 384); assert_eq!(config["materialization"]["spill_chunk_mib"], 32); assert_eq!(config["materialization"]["max_parallelism"], 2); assert!(config["mcp"]["command"] diff --git a/src/adapters/cli/tests/mcp.rs b/src/adapters/cli/tests/mcp.rs index ffecc5c3..036619cf 100644 --- a/src/adapters/cli/tests/mcp.rs +++ b/src/adapters/cli/tests/mcp.rs @@ -12,8 +12,6 @@ fn mcp_options_resolve_config_defaults_and_cli_overrides() { "repo_root": root, "refresh": {"policy": "off", "backend": "auto", "reconcile_interval_ms": 1234}, "materialization": { - "worker_memory_mib": 900, - "rust_memory_mib": 450, "spill_chunk_mib": 45, "max_parallelism": 3 } @@ -30,10 +28,6 @@ fn mcp_options_resolve_config_defaults_and_cli_overrides() { "leader".into(), "--refresh-backend".into(), "poll".into(), - "--worker-memory-mib".into(), - "1024".into(), - "--rust-memory-mib".into(), - "512".into(), "--spill-chunk-mib".into(), "64".into(), "--max-parallelism".into(), @@ -52,8 +46,6 @@ fn mcp_options_resolve_config_defaults_and_cli_overrides() { crate::api::context::GraphRefreshBackend::Poll ); assert_eq!(settings.reconcile_interval_ms, 1234); - assert_eq!(settings.worker_memory_mib, 1024); - assert_eq!(settings.rust_memory_mib, 512); assert_eq!(settings.spill_chunk_mib, 64); assert_eq!(settings.max_parallelism, 4); @@ -63,10 +55,6 @@ fn mcp_options_resolve_config_defaults_and_cli_overrides() { root.to_string_lossy().into_owned(), "--refresh-policy".into(), "off".into(), - "--worker-memory-mib".into(), - "800".into(), - "--rust-memory-mib".into(), - "400".into(), ], format::mcp_help(), ) @@ -79,31 +67,12 @@ fn mcp_options_resolve_config_defaults_and_cli_overrides() { } #[test] -fn mcp_options_reject_invalid_resource_limits() { - let error = McpServeOptions::parse( - &["--worker-memory-mib".into(), "0".into()], - format::mcp_help(), - ) - .unwrap_err(); - assert!(error.contains("positive integer")); - - let root = unique_temp_dir("codebase-graph-rust-mcp-invalid-limits"); - fs::create_dir_all(&root).unwrap(); - let options = McpServeOptions::parse( - &[ - "--repo-root".into(), - root.to_string_lossy().into_owned(), - "--worker-memory-mib".into(), - "256".into(), - ], - format::mcp_help(), - ) - .unwrap(); - assert!(options - .runtime_settings() - .unwrap_err() - .contains("rust_memory_mib")); - let _ = fs::remove_dir_all(root); +fn mcp_options_reject_removed_memory_flags() { + for option in ["--worker-memory-mib", "--rust-memory-mib"] { + let error = + McpServeOptions::parse(&[option.into(), "1".into()], format::mcp_help()).unwrap_err(); + assert!(error.contains("unknown mcp start option")); + } } #[test] @@ -121,8 +90,6 @@ fn mcp_graph_query_binds_json_parameters() { api: None, refresh_policy: None, refresh_backend: None, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, }; @@ -221,8 +188,6 @@ fn mcp_stdio_serves_tools_and_tool_errors() { api: None, refresh_policy: None, refresh_backend: None, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, }; @@ -240,6 +205,11 @@ fn mcp_stdio_serves_tools_and_tool_errors() { assert!(tools.iter().any(|tool| tool["name"] == "graph_health")); assert!(tools.iter().any(|tool| tool["name"] == "graph_search")); assert!(tools.iter().any(|tool| tool["name"] == "graph_query")); + for tool in tools { + let properties = &tool["inputSchema"]["properties"]; + assert!(properties.get("worker_memory_mib").is_none()); + assert!(properties.get("rust_memory_mib").is_none()); + } assert!(tools.iter().all(|tool| tool["inputSchema"]["properties"] .get("output_format") .is_some())); @@ -320,8 +290,6 @@ fn mcp_stdio_refresh_policy_off_is_watcher_free() { api: None, refresh_policy: Some(crate::api::context::GraphRefreshPolicy::Off), refresh_backend: None, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, }; diff --git a/src/adapters/mcp/options.rs b/src/adapters/mcp/options.rs index 5bfb25fe..842abb31 100644 --- a/src/adapters/mcp/options.rs +++ b/src/adapters/mcp/options.rs @@ -2,8 +2,7 @@ use super::http::is_local_host; use crate::adapters::required_arg; use crate::api::context::{ read_selected_install_config, GraphInstallConfig, GraphRefreshBackend, GraphRefreshPolicy, - DEFAULT_MAX_PARALLELISM, DEFAULT_RUST_MEMORY_MIB, DEFAULT_SPILL_CHUNK_MIB, - DEFAULT_WORKER_MEMORY_MIB, + DEFAULT_MAX_PARALLELISM, DEFAULT_SPILL_CHUNK_MIB, }; use crate::api::RefreshConfigOverrides; use crate::api::{CoordinatorCodebaseGraphApi, RepoSelector}; @@ -15,8 +14,6 @@ pub(crate) struct McpRuntimeSettings { pub(crate) refresh_backend: GraphRefreshBackend, pub(crate) reconcile_interval_ms: u64, pub(crate) include_fts: bool, - pub(crate) worker_memory_mib: u64, - pub(crate) rust_memory_mib: u64, pub(crate) spill_chunk_mib: u64, pub(crate) max_parallelism: usize, pub(crate) explicit_overrides: RefreshConfigOverrides, @@ -29,8 +26,6 @@ impl Default for McpRuntimeSettings { refresh_backend: GraphRefreshBackend::Auto, reconcile_interval_ms: crate::api::context::DEFAULT_RECONCILE_INTERVAL_MS, include_fts: true, - worker_memory_mib: DEFAULT_WORKER_MEMORY_MIB, - rust_memory_mib: DEFAULT_RUST_MEMORY_MIB, spill_chunk_mib: DEFAULT_SPILL_CHUNK_MIB, max_parallelism: DEFAULT_MAX_PARALLELISM, explicit_overrides: RefreshConfigOverrides::default(), @@ -47,8 +42,6 @@ pub(crate) struct McpServeOptions { pub(in crate::adapters) api: Option, pub(in crate::adapters) refresh_policy: Option, pub(in crate::adapters) refresh_backend: Option, - pub(in crate::adapters) worker_memory_mib: Option, - pub(in crate::adapters) rust_memory_mib: Option, pub(in crate::adapters) spill_chunk_mib: Option, pub(in crate::adapters) max_parallelism: Option, } @@ -79,8 +72,6 @@ impl McpServeOptions { api: None, refresh_policy: None, refresh_backend: None, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, } @@ -124,20 +115,6 @@ impl McpServeOptions { )?)?); index + 2 } - "--worker-memory-mib" => { - self.worker_memory_mib = Some(parse_positive_u64( - required_arg(args, index, "--worker-memory-mib")?, - "--worker-memory-mib", - )?); - index + 2 - } - "--rust-memory-mib" => { - self.rust_memory_mib = Some(parse_positive_u64( - required_arg(args, index, "--rust-memory-mib")?, - "--rust-memory-mib", - )?); - index + 2 - } "--spill-chunk-mib" => { self.spill_chunk_mib = Some(parse_positive_u64( required_arg(args, index, "--spill-chunk-mib")?, @@ -178,14 +155,6 @@ impl McpServeOptions { settings.refresh_backend = value; settings.explicit_overrides.backend = true; } - if let Some(value) = self.worker_memory_mib { - settings.worker_memory_mib = value; - settings.explicit_overrides.worker_memory_mib = true; - } - if let Some(value) = self.rust_memory_mib { - settings.rust_memory_mib = value; - settings.explicit_overrides.rust_memory_mib = true; - } if let Some(value) = self.spill_chunk_mib { settings.spill_chunk_mib = value; settings.explicit_overrides.spill_chunk_mib = true; @@ -204,8 +173,6 @@ fn settings_from_install_config(config: GraphInstallConfig) -> McpRuntimeSetting refresh_backend: config.refresh.backend, reconcile_interval_ms: config.refresh.reconcile_interval_ms, include_fts: config.materialization.include_fts, - worker_memory_mib: config.materialization.worker_memory_mib, - rust_memory_mib: config.materialization.rust_memory_mib, spill_chunk_mib: config.materialization.spill_chunk_mib, max_parallelism: config.materialization.max_parallelism, explicit_overrides: RefreshConfigOverrides::default(), @@ -213,20 +180,8 @@ fn settings_from_install_config(config: GraphInstallConfig) -> McpRuntimeSetting } fn validate_runtime_settings(settings: McpRuntimeSettings) -> Result { - if settings.worker_memory_mib == 0 - || settings.rust_memory_mib == 0 - || settings.spill_chunk_mib == 0 - || settings.max_parallelism == 0 - { - return Err( - "MCP materialization memory limits and max parallelism must be positive".into(), - ); - } - if settings.rust_memory_mib > settings.worker_memory_mib { - return Err("rust_memory_mib must not exceed worker_memory_mib".into()); - } - if settings.spill_chunk_mib > settings.rust_memory_mib { - return Err("spill_chunk_mib must not exceed rust_memory_mib".into()); + if settings.spill_chunk_mib == 0 || settings.max_parallelism == 0 { + return Err("MCP spill_chunk_mib and max_parallelism must be positive".into()); } Ok(settings) } diff --git a/src/adapters/mcp/refresh.rs b/src/adapters/mcp/refresh.rs index ad084535..2a9fca4d 100644 --- a/src/adapters/mcp/refresh.rs +++ b/src/adapters/mcp/refresh.rs @@ -11,8 +11,6 @@ pub(in crate::adapters) fn start_configured_api( GraphRefreshPolicy::Leader => Some(RefreshServiceConfig { policy: settings.refresh_policy, include_fts: settings.include_fts, - worker_memory_mib: settings.worker_memory_mib, - rust_memory_mib: settings.rust_memory_mib, spill_chunk_mib: settings.spill_chunk_mib, max_parallelism: settings.max_parallelism, backend: settings.refresh_backend, diff --git a/src/api/context.rs b/src/api/context.rs index a7a2e9e4..6f5d256d 100644 --- a/src/api/context.rs +++ b/src/api/context.rs @@ -1,9 +1,8 @@ use crate::api::contracts::RepoSelector; use crate::storage::direct::DirectStore; -use crate::storage::layout::{DirectLayout, RepositoryLayout}; +use crate::storage::layout::{DirectLayout, ManagedLayout, RepositoryLayout}; use crate::storage::locks::WriterLease; use crate::storage::managed::{GraphStorage, ManagedReadSnapshot, StorageMode}; -use crate::storage::run_workspace::RunWorkspace; use serde::{Deserialize, Serialize}; use std::fs; use std::path::{Component, Path, PathBuf}; @@ -99,6 +98,43 @@ impl RepositoryIdentity { Self::capture_bound(&selector) } + pub(crate) fn maintenance_target( + &self, + memory: &crate::storage::maintenance_budget::CleanupMemory, + ) -> Result< + crate::storage::maintenance::AccountedMaintenanceTarget, + crate::storage::maintenance_budget::ResourceLimit, + > { + // RepoPaths creates temporary default paths; reserve before any clone + // or join, including Vec growth, and hold until all temporaries drop. + let scratch_bytes = self + .repo_root + .as_os_str() + .len() + .checked_add(512) + .and_then(|bytes| bytes.checked_mul(16)) + .ok_or(crate::storage::maintenance_budget::ResourceLimit::ArithmeticOverflow)?; + let _scratch = memory.reserve(scratch_bytes)?; + let paths = RepoPaths::derive(&self.repo_root); + let direct_selected = + self.explicit_db_path || self.explicit_manifest_path || self.storage_root.is_none(); + crate::storage::maintenance::AccountedMaintenanceTarget::from_paths( + &self.repo_root, + &paths.state_dir, + (!direct_selected) + .then_some(self.storage_root.as_deref()) + .flatten(), + direct_selected.then_some(( + self.direct_db_path.as_deref().unwrap_or(&paths.db_path), + self.direct_manifest_path + .as_deref() + .unwrap_or(&paths.manifest_path), + )), + self.config_path.as_deref(), + memory, + ) + } + pub(crate) fn validate(&self) -> Result<(), String> { let current_selector = bind_repo_selector(&RepoSelector { repo_root: Some(self.repo_root.clone()), @@ -248,8 +284,6 @@ impl RepoPaths { } pub(crate) const INSTALL_CONFIG_SCHEMA_VERSION: u64 = 3; -pub(crate) const DEFAULT_WORKER_MEMORY_MIB: u64 = 768; -pub(crate) const DEFAULT_RUST_MEMORY_MIB: u64 = 384; pub(crate) const DEFAULT_SPILL_CHUNK_MIB: u64 = 32; pub(crate) const DEFAULT_MAX_PARALLELISM: usize = 2; pub(crate) const DEFAULT_RECONCILE_INTERVAL_MS: u64 = 30_000; @@ -258,14 +292,6 @@ const fn default_true() -> bool { true } -const fn default_worker_memory_mib() -> u64 { - DEFAULT_WORKER_MEMORY_MIB -} - -const fn default_rust_memory_mib() -> u64 { - DEFAULT_RUST_MEMORY_MIB -} - const fn default_spill_chunk_mib() -> u64 { DEFAULT_SPILL_CHUNK_MIB } @@ -286,10 +312,6 @@ pub(crate) struct GraphInstallMaterializationConfig { pub exclude: Vec, #[serde(default = "default_true")] pub include_fts: bool, - #[serde(default = "default_worker_memory_mib")] - pub worker_memory_mib: u64, - #[serde(default = "default_rust_memory_mib")] - pub rust_memory_mib: u64, #[serde(default = "default_spill_chunk_mib")] pub spill_chunk_mib: u64, #[serde(default = "default_max_parallelism")] @@ -302,8 +324,6 @@ impl Default for GraphInstallMaterializationConfig { include: Vec::new(), exclude: Vec::new(), include_fts: true, - worker_memory_mib: DEFAULT_WORKER_MEMORY_MIB, - rust_memory_mib: DEFAULT_RUST_MEMORY_MIB, spill_chunk_mib: DEFAULT_SPILL_CHUNK_MIB, max_parallelism: DEFAULT_MAX_PARALLELISM, } @@ -499,6 +519,20 @@ pub(crate) fn bind_repo_selector(selector: &RepoSelector) -> Result Result { + resolve_runtime_with_rebuild(selector, false) +} + +/// An explicit full build may establish a new Direct incarnation without +/// treating retired bytes as a committed readable graph. begin_write still +/// performs actual ownership acquisition and required publication recovery. +pub(crate) fn resolve_runtime_for_rebuild(selector: &RepoSelector) -> Result { + resolve_runtime_with_rebuild(selector, true) +} + +fn resolve_runtime_with_rebuild( + selector: &RepoSelector, + explicit_rebuild: bool, +) -> Result { let selector = bind_repo_selector(selector)?; let repo_root = selector .repo_root @@ -532,9 +566,16 @@ pub(crate) fn resolve_runtime(selector: &RepoSelector) -> Result Result 0, - pending_runs: direct_cleanup.skipped_locked, + cleanup_pending: run_cleanup_pending || candidate_cleanup_pending, + pending_runs, active_read: None, direct_read, }); @@ -594,9 +635,16 @@ pub(crate) fn resolve_runtime(selector: &RepoSelector) -> Result Result 0, - pending_runs: direct_cleanup.skipped_locked, + cleanup_pending: run_cleanup_pending || candidate_cleanup_pending, + pending_runs, active_read: None, direct_read, }) @@ -616,6 +664,55 @@ pub(crate) fn resolve_runtime(selector: &RepoSelector) -> Result (bool, usize) { + use crate::storage::locks::{path_identity, RootedDirectory}; + let path = state_dir.join("direct-runs"); + match fs::symlink_metadata(&path) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return (false, 0), + Ok(metadata) if metadata.is_dir() && !metadata.file_type().is_symlink() => {} + _ => return (true, 0), + } + let root = match path_identity(&path) + .ok() + .and_then(|identity| RootedDirectory::open_readonly_root(&path, identity).ok()) + { + Some(root) => root, + None => return (true, 0), + }; + let mut first = false; + let mut known_run = 0; + let result = root.visit_entries(&mut [0; 4096], &mut |name| { + first = true; + known_run = usize::from(root.open_directory_readonly(name).is_ok()); + Err(std::io::ErrorKind::Interrupted.into()) + }); + (first || result.is_err(), known_run) +} + +// Required recovery may have no run journal to replay while optional cleanup +// controls remain unusable. Keep that legacy pending diagnostic conservative +// through fixed, noncreating observations; do not scan or repair graph state. +fn managed_cleanup_controls_pending(layout: &ManagedLayout) -> bool { + use crate::storage::maintenance_ownership::{ + observe_reference_epoch, EpochObservation, ReferenceDomain, + }; + let Ok(domain) = ReferenceDomain::managed(layout) else { + return true; + }; + let Ok(EpochObservation::Verified(epoch)) = observe_reference_epoch(&domain) else { + return true; + }; + if epoch.counter() == u64::MAX { + return true; + } + match fs::symlink_metadata(domain.artifact_root().join(".reference-epoch")) { + Ok(metadata) => !metadata.is_file() || metadata.permissions().readonly(), + Err(_) => true, + } +} + fn resolve_managed_runtime( repo_root: PathBuf, paths: RepoPaths, @@ -632,7 +729,11 @@ fn resolve_managed_runtime( .to_path_buf() }); let store = GraphStorage::managed(storage_root.clone()); - let cleanup = store.cleanup().map_err(|error| error.to_string())?; + let recovery = store + .recover_for_read() + .map_err(|error| error.to_string())?; + let cleanup_pending = recovery.janitor.pending || !recovery.recovery.observation_complete; + let cleanup = recovery.janitor.legacy; let snapshot = store .resolve_active_read() .map_err(|error| error.to_string())?; @@ -653,6 +754,9 @@ fn resolve_managed_runtime( None, ), }; + let cleanup_pending = cleanup_pending + || (active_read.is_some() + && managed_cleanup_controls_pending(&ManagedLayout::new(&storage_root))); Ok(RepoRuntime { repo_root, state_dir: paths.state_dir, @@ -663,7 +767,8 @@ fn resolve_managed_runtime( storage_root: Some(storage_root), active_generation, writable: true, - cleanup_pending: cleanup.retired_generations_pending > 0 + cleanup_pending: cleanup_pending + || cleanup.retired_generations_pending > 0 || cleanup.run_recovery.skipped_locked > 0, pending_runs: cleanup.run_recovery.skipped_locked, active_read, @@ -676,6 +781,10 @@ fn resolve_direct_read( manifest_path: &Path, ) -> Result>, String> { let layout = DirectLayout::new(db_path, manifest_path); + let store = DirectStore::new(layout.clone()).map_err(|error| error.to_string())?; + store + .ensure_not_retired() + .map_err(|error| error.to_string())?; if !db_path.exists() && !manifest_path.exists() && !layout.journal_path().exists() @@ -684,8 +793,8 @@ fn resolve_direct_read( { return Ok(None); } - DirectStore::new(layout) - .and_then(|store| store.begin_read()) + store + .begin_read() .map(Arc::new) .map(Some) .map_err(|error| format!("failed to recover direct graph publication: {error}")) @@ -932,8 +1041,7 @@ mod tests { use super::{ bind_repo_selector, read_install_config, resolve_runtime, GraphRefreshBackend, GraphRefreshPolicy, RepoPaths, RepositoryIdentity, DEFAULT_MAX_PARALLELISM, - DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_RUST_MEMORY_MIB, DEFAULT_SPILL_CHUNK_MIB, - DEFAULT_WORKER_MEMORY_MIB, + DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_SPILL_CHUNK_MIB, }; use crate::api::contracts::RepoSelector; use crate::storage::atomic::write_json_atomically; @@ -958,6 +1066,36 @@ mod tests { )) } + #[test] + fn dc_direct_runtime_run_presence_defers_collection_without_touching_outputs() { + let root = unique_temp_dir("direct-run-presence"); + assert_eq!(super::direct_run_presence(&root), (false, 0)); + let runs = root.join("direct-runs"); + fs::create_dir_all(&runs).unwrap(); + assert_eq!(super::direct_run_presence(&root), (false, 0)); + let run = runs.join("unknown-producer"); + fs::create_dir(&run).unwrap(); + let leaf = run.join("output.csv"); + fs::write(&leaf, b"caller-owned").unwrap(); + assert_eq!(super::direct_run_presence(&root), (true, 1)); + assert_eq!(fs::read(&leaf).unwrap(), b"caller-owned"); + fs::remove_dir_all(root).unwrap(); + } + + #[cfg(unix)] + #[test] + fn dc_direct_runtime_run_presence_preserves_foreign_symlink() { + let root = unique_temp_dir("direct-run-presence-symlink"); + fs::create_dir_all(&root).unwrap(); + let foreign = root.join("foreign"); + fs::create_dir(&foreign).unwrap(); + fs::write(foreign.join("sentinel"), b"untouched").unwrap(); + std::os::unix::fs::symlink(&foreign, root.join("direct-runs")).unwrap(); + assert_eq!(super::direct_run_presence(&root), (true, 0)); + assert_eq!(fs::read(foreign.join("sentinel")).unwrap(), b"untouched"); + fs::remove_dir_all(root).unwrap(); + } + #[test] fn graph_paths_are_deterministic() { let paths = RepoPaths::derive(&std::path::PathBuf::from("/tmp/demo")); @@ -1211,6 +1349,151 @@ mod tests { let _ = fs::remove_dir_all(root); } + #[test] + fn dc_epoch_optional_managed_cleanup_keeps_runtime_readable_and_pending() { + use crate::storage::layout::ManagedLayout; + use crate::storage::locks::path_identity; + use crate::storage::maintenance_ownership::{ + tests::{break_epoch_control, EpochControlFault}, + ReferenceDomain, + }; + use crate::storage::managed::ManagedStore; + for fault in [ + EpochControlFault::Corrupt, + EpochControlFault::Unwritable, + EpochControlFault::Exhausted, + ] { + let root = unique_temp_dir("epoch-managed-runtime"); + fs::create_dir_all(root.join(".codebaseGraph")).unwrap(); + let root = fs::canonicalize(root).unwrap(); + let layout = ManagedLayout::new(root.join("storage")); + let store = ManagedStore::new(layout.clone()); + let publish = |bytes: &[u8]| { + let mut session = store.begin_write().unwrap(); + fs::write(session.candidate_db_path(), bytes).unwrap(); + fs::write(session.candidate_manifest_path(), b"{}").unwrap(); + let id = session + .publish_with_stats(&crate::protocol::GraphSummary { + node_count: 0, + edge_count: 0, + }) + .unwrap(); + session.finish(); + id + }; + let old = publish(b"old committed graph"); + let retained = store.open_read().unwrap(); + let active = publish(b"current committed graph"); + let old_path = layout.generation(&old).unwrap().db_path(); + let old_identity = path_identity(&old_path).unwrap(); + write_json_atomically( + &root.join(".codebaseGraph/config.json"), + &json!({ + "schema_version": 2, "repo_root": root, "storage_root": layout.storage_root() + }), + ) + .unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + let epoch = domain.artifact_root().join(".reference-epoch"); + let permissions = fs::metadata(&epoch).unwrap().permissions(); + break_epoch_control(&domain, fault); + let epoch_before = fs::read(&epoch).unwrap(); + let runtime = resolve_runtime(&RepoSelector { + repo_root: Some(root.clone()), + ..Default::default() + }) + .unwrap(); + assert_eq!( + runtime.active_generation.as_deref(), + Some(active.as_str()), + "{fault:?}" + ); + assert_eq!( + fs::read(&runtime.db_path).unwrap(), + b"current committed graph" + ); + assert!(runtime.cleanup_pending, "{fault:?}"); + assert_eq!(runtime.pending_runs, 0); + assert_eq!( + fs::read(&epoch).unwrap(), + epoch_before, + "optional pending diagnostic repaired or advanced {fault:?}" + ); + assert_eq!(path_identity(&old_path).unwrap(), old_identity); + drop(runtime); + drop(retained); + assert_eq!(path_identity(&old_path).unwrap(), old_identity); + fs::set_permissions(epoch, permissions).unwrap(); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn dc_epoch_optional_direct_cleanup_keeps_both_runtime_selectors_readable_and_pending() { + use crate::storage::direct::DirectStore; + use crate::storage::locks::path_identity; + use crate::storage::maintenance_ownership::{ + tests::{break_epoch_control, EpochControlFault}, + ReferenceDomain, + }; + for configured in [false, true] { + for fault in [ + EpochControlFault::Corrupt, + EpochControlFault::Unwritable, + EpochControlFault::Exhausted, + ] { + let root = unique_temp_dir("epoch-direct-runtime"); + fs::create_dir_all(root.join(".codebaseGraph")).unwrap(); + let root = fs::canonicalize(root).unwrap(); + let layout = DirectLayout::new( + root.join("direct/graph.ldb"), + root.join("direct/manifest.json"), + ); + let store = DirectStore::new(layout.clone()).unwrap(); + let mut writer = store.begin_write().unwrap(); + fs::write(writer.db_candidate_path(), b"committed graph").unwrap(); + fs::write(writer.manifest_candidate_path(), b"{}").unwrap(); + writer.publish().unwrap(); + writer.finish(); + fs::write(layout.db_candidate_path(), b"unpublished residue").unwrap(); + let residue = path_identity(&layout.db_candidate_path()).unwrap(); + let selector = if configured { + write_json_atomically(&root.join(".codebaseGraph/config.json"), &json!({ + "repo_root": root, + "database_path": layout.db_path(), "manifest_path": layout.manifest_path() + })).unwrap(); + RepoSelector { + repo_root: Some(root.clone()), + ..Default::default() + } + } else { + RepoSelector { + repo_root: Some(root.clone()), + db_path: Some(layout.db_path().to_owned()), + manifest_path: Some(layout.manifest_path().to_owned()), + ..Default::default() + } + }; + let domain = ReferenceDomain::direct(&layout).unwrap(); + let epoch = domain.artifact_root().join(".reference-epoch"); + let permissions = fs::metadata(&epoch).unwrap().permissions(); + break_epoch_control(&domain, fault); + let runtime = resolve_runtime(&selector).unwrap(); + assert!(runtime.direct_read.is_some()); + assert_eq!(fs::read(&runtime.db_path).unwrap(), b"committed graph"); + assert!( + runtime.cleanup_pending, + "configured={configured}, fault={fault:?}" + ); + assert_eq!(runtime.pending_runs, 0, "candidate residue is not a run"); + assert_eq!(path_identity(&layout.db_candidate_path()).unwrap(), residue); + drop(runtime); + fs::set_permissions(epoch, permissions).unwrap(); + fs::remove_dir_all(root).unwrap(); + } + } + } + #[test] fn resolve_runtime_recovers_direct_pair_before_returning_read_lease() { let root = unique_temp_dir("codebase-graph-api-runtime-direct-recovery"); @@ -1301,8 +1584,6 @@ mod tests { exclude_patterns: vec![], candidate_paths: vec![], parallel: false, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, progress: false, @@ -1494,7 +1775,9 @@ mod tests { "mcp": {}, "materialization": { "semantic_enrichment": true, - "semantic_provider_mode": "remote" + "semantic_provider_mode": "remote", + "worker_memory_mib": 0, + "rust_memory_mib": 0 }, })) .unwrap(), @@ -1515,14 +1798,8 @@ mod tests { let materialization_json = serde_json::to_value(&config.materialization).unwrap(); assert!(materialization_json.get("semantic_enrichment").is_none()); assert!(materialization_json.get("semantic_provider_mode").is_none()); - assert_eq!( - config.materialization.worker_memory_mib, - DEFAULT_WORKER_MEMORY_MIB - ); - assert_eq!( - config.materialization.rust_memory_mib, - DEFAULT_RUST_MEMORY_MIB - ); + assert!(materialization_json.get("worker_memory_mib").is_none()); + assert!(materialization_json.get("rust_memory_mib").is_none()); assert_eq!( config.materialization.spill_chunk_mib, DEFAULT_SPILL_CHUNK_MIB diff --git a/src/api/contracts.rs b/src/api/contracts.rs index 66303aa3..137668b2 100644 --- a/src/api/contracts.rs +++ b/src/api/contracts.rs @@ -106,6 +106,8 @@ pub enum OutputFormat { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HealthRequest { pub repo: RepoSelector, + #[serde(default)] + pub confirm_legacy_quiescence: bool, pub refresh_status: Option, pub output_format: OutputFormat, } @@ -170,10 +172,6 @@ pub struct MaterializationRequest { pub candidate_paths: Vec, pub parallel: bool, #[serde(default, skip_serializing_if = "Option::is_none")] - pub worker_memory_mib: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub rust_memory_mib: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] pub spill_chunk_mib: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub max_parallelism: Option, @@ -395,8 +393,6 @@ mod tests { exclude_patterns: vec!["target/**".to_string()], candidate_paths: vec!["src/lib.rs".to_string()], parallel: true, - worker_memory_mib: Some(768), - rust_memory_mib: Some(384), spill_chunk_mib: Some(32), max_parallelism: Some(2), progress: false, @@ -438,11 +434,22 @@ mod tests { assert!(encoded.get("semantic_provider_mode").is_none()); } + #[test] + fn older_health_requests_decode_with_confirmation_disabled() { + let request: HealthRequest = serde_json::from_value( + json!({"repo": repository(), "refresh_status": null, "output_format": "Typed"}), + ) + .unwrap(); + assert!(!request.confirm_legacy_quiescence); + assert!(serde_json::from_value::(json!({"repo": repository(), "refresh_status": null, "output_format": "Typed", "confirm_legacy_quiescence": "true"})).is_err()); + } + #[test] fn public_operation_contracts_round_trip_without_transport_types() { let requests = vec![ OperationRequest::Health(HealthRequest { repo: repository(), + confirm_legacy_quiescence: false, refresh_status: Some(json!({"running": true})), output_format: OutputFormat::Block, }), diff --git a/src/api/core.rs b/src/api/core.rs index 9f11a3c2..6f055d3b 100644 --- a/src/api/core.rs +++ b/src/api/core.rs @@ -1,5 +1,5 @@ use crate::api::catalog::{filter_catalog, load_catalog, supported_syntax_languages}; -use crate::api::context::{resolve_runtime, RepoRuntime}; +use crate::api::context::{resolve_runtime, resolve_runtime_for_rebuild, RepoRuntime}; use crate::api::contracts::{ ApiError, ContextRequest, HealthRequest, MaterializationRequest, OperationInvocation, OperationRequest, OperationResponse, OutputFormat, QueryRequest, SearchRequest, @@ -36,6 +36,32 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; +/// Declared before execution resources so unwind requests maintenance only +/// after the producer/read stack has released them. Normal completion disarms +/// this fixed handle before projecting its newly queued state. +struct MaintenanceCompletion<'a> { + client: Option< + &'a crate::storage::maintenance::MaintenanceClient< + Arc, + >, + >, +} +impl MaintenanceCompletion<'_> { + fn request(&mut self) { + if let Some(client) = self.client.take() { + client.request( + crate::storage::maintenance_scheduler::Trigger::Completion, + false, + ); + } + } +} +impl Drop for MaintenanceCompletion<'_> { + fn drop(&mut self) { + self.request(); + } +} + #[derive(Debug, Clone, Copy)] pub struct OperationDescriptor { pub id: &'static str, @@ -70,6 +96,12 @@ pub(crate) struct RegisteredOperation { pub struct ApiCore { refresh: Option>, isolate_materialization: bool, + maintenance: Option< + crate::storage::maintenance::MaintenanceClient< + Arc, + >, + >, + maintenance_unavailable: Option, } impl ApiCore { @@ -77,10 +109,30 @@ impl ApiCore { Self::default() } + #[cfg(test)] pub(crate) fn for_coordinator(refresh: Option>) -> Self { Self { refresh, isolate_materialization: true, + maintenance: None, + maintenance_unavailable: None, + } + } + + pub(crate) fn for_coordinator_with_maintenance( + refresh: Option>, + maintenance: Option< + crate::storage::maintenance::MaintenanceClient< + Arc, + >, + >, + unavailable: Option, + ) -> Self { + Self { + refresh, + isolate_materialization: true, + maintenance, + maintenance_unavailable: unavailable, } } @@ -89,7 +141,7 @@ impl ApiCore { OperationDescriptor { id: "health", summary: "Check codebase graph and manifest availability", - request_schema: empty_request_schema, + request_schema: health_request_schema, surfaces: &["api", "cli", "mcp"], supported_outputs: &[OutputFormat::Typed, OutputFormat::Block], mcp_tool_name: Some("graph_health"), @@ -222,6 +274,162 @@ impl ApiCore { } pub fn execute(&self, request: &OperationRequest) -> Result { + self.execute_with_legacy_confirmation(request, None) + } + fn execute_with_legacy_confirmation( + &self, + request: &OperationRequest, + handled_confirmation: Option, + ) -> Result { + use crate::storage::maintenance_scheduler::Trigger; + let confirmation = + matches!(request, OperationRequest::Health(health) if health.confirm_legacy_quiescence); + let confirmation_unbound = if let Some(bound) = handled_confirmation { + confirmation && !bound + } else if let Some(maintenance) = &self.maintenance { + if matches!(request, OperationRequest::Health(_)) { + let disposition = maintenance.request(Trigger::Health, confirmation); + confirmation + && disposition + != crate::storage::maintenance_scheduler::RequestDisposition::Enqueued + } else { + false + } + } else { + false + }; + // execute_inner owns all read/producer/runtime resources. Its return + // or unwind releases them before a completion opportunity is requested. + let mut completion = MaintenanceCompletion { + client: self.maintenance.as_ref(), + }; + let mut result = self.execute_inner(request); + completion.request(); + let trigger = Trigger::Completion; + let mut summary = if let Some(maintenance) = &self.maintenance { + Some(maintenance.projection()) + } else if self.isolate_materialization { + self.maintenance_unavailable + .map(crate::storage::maintenance::MaintenanceProjection::unavailable) + } else { + self.transient_maintenance(request, trigger, confirmation) + }; + if confirmation_unbound { + if let Some(projection) = summary.as_mut() { + projection.summary.pending = true; + projection + .summary + .reasons + .push("legacy_confirmation_not_bound".into()); + projection + .summary + .reasons + .push("legacy_source_capture_busy_or_unavailable".into()); + } + } + if let (Ok(response), Some(projection)) = (&mut result, summary.as_ref()) { + if matches!( + request, + OperationRequest::Health(_) + | OperationRequest::Materialize(_) + | OperationRequest::Refresh(_) + ) { + if let Some(payload) = response.payload.as_object_mut() { + let mut projected = serde_json::to_value(&projection.summary) + .expect("fixed maintenance summary"); + // The native operation may already own a secondary janitor + // error after verified publication. Preserve those actual + // errors by moving caller-output values, without cloning + // their possibly long filesystem messages into the worker + // cache or its bounded projection. Current measurements and + // attempt state still come from the real controller. + if let Some(mut previous) = payload.remove("maintenance") { + let has_errors = previous + .get("errors") + .and_then(serde_json::Value::as_array) + .is_some_and(|errors| !errors.is_empty()); + if has_errors { + if let (Some(previous), Some(projected)) = + (previous.as_object_mut(), projected.as_object_mut()) + { + for field in ["errors", "reasons"] { + if let Some(serde_json::Value::Array(mut values)) = + previous.remove(field) + { + if field == "reasons" { + values.retain(|value| value.as_str() != Some("artifact_collection_requires_owned_maintenance")); + } + if let Some(serde_json::Value::Array(current)) = + projected.get_mut(field) + { + values.append(current); + *current = values; + } + } + } + } + } + } + payload.insert("maintenance".into(), projected); + } + } + } + result.map(|response| present_operation_response(response, request.output_format())) + } + + fn transient_maintenance( + &self, + request: &OperationRequest, + trigger: crate::storage::maintenance_scheduler::Trigger, + confirmation: bool, + ) -> Option { + let selector = request.repo_selector()?; + let identity = crate::api::context::RepositoryIdentity::capture(selector).ok()?; + let unavailable = || { + Some( + crate::storage::maintenance::MaintenanceProjection::unavailable( + crate::storage::maintenance_scheduler::DriverError::ResourceLimit, + ), + ) + }; + let memory = match crate::storage::maintenance_budget::CleanupMemory::new( + crate::storage::maintenance::MAINTENANCE_MEMORY_BYTES, + ) { + Ok(memory) => memory, + Err(_) => return unavailable(), + }; + let target = match identity.maintenance_target(&memory) { + Ok(target) => target, + Err(_) => return unavailable(), + }; + let dispatcher = Arc::new(crate::coordinator::ExecutionDispatcher::default()); + match crate::storage::maintenance::start_controller(target, memory, dispatcher, false) { + Ok((mut runtime, maintenance)) => { + let bound = if confirmation { + maintenance.confirm_and_wait() + } else { + maintenance.request(trigger, false); + maintenance.wait_for_first_attempt(); + true + }; + runtime.stop_and_join(); + let mut projection = maintenance.projection(); + if !bound { + projection.summary.pending = true; + projection + .summary + .reasons + .push("legacy_confirmation_not_bound".into()); + } + Some(projection) + } + Err(error) => { + Some(crate::storage::maintenance::MaintenanceProjection::unavailable(error)) + } + } + } + + fn execute_inner(&self, request: &OperationRequest) -> Result { let mut request = normalize_request(request); if let (Some(refresh), OperationRequest::Health(health)) = (&self.refresh, &mut request) { if health.refresh_status.is_none() { @@ -239,7 +447,13 @@ impl ApiCore { })?; let mut runtime = request .repo_selector() - .map(resolve_runtime) + .map(|selector| { + if matches!(&request, OperationRequest::Materialize(materialize) if materialize.mode == "full") { + resolve_runtime_for_rebuild(selector) + } else { + resolve_runtime(selector) + } + }) .transpose() .map_err(|error| ApiError::new("runtime_resolution_failed", error))?; if operation_requires_graph_write(&request) { @@ -273,10 +487,32 @@ impl ApiCore { } else { dispatch_operation(&request, &operation, runtime.as_ref())? }; - Ok(present_operation_response( - response, - request.output_format(), - )) + Ok(response) + } + + pub(crate) fn prepare_legacy_confirmation_request( + &self, + operation_id: &str, + invocation: &OperationInvocation, + ) -> Result, ApiError> { + let request = prepare_operation_request(operation_id, invocation)?; + if !matches!(request, OperationRequest::Health(ref health) if health.confirm_legacy_quiescence) + { + return Ok(None); + } + Ok(Some(self.maintenance.as_ref().is_some_and(|maintenance| { + maintenance.request(crate::storage::maintenance_scheduler::Trigger::Health, true) + == crate::storage::maintenance_scheduler::RequestDisposition::Enqueued + }))) + } + pub(crate) fn execute_invocation_with_legacy_confirmation( + &self, + operation_id: &str, + invocation: &OperationInvocation, + handled: Option, + ) -> Result { + let request = prepare_operation_request(operation_id, invocation)?; + self.execute_with_legacy_confirmation(&request, handled) } pub(crate) fn execute_invocation( @@ -345,6 +581,16 @@ fn build_registry(descriptors: impl IntoIterator) -> OperationRegistry { operations: by_id } } +fn health_request_schema() -> serde_json::Value { + json!({ + "confirm_legacy_quiescence": { + "type": "boolean", + "default": false, + "description": "Confirm all old daemons/helpers have been stopped and upgraded before this maintenance request. This operator precondition does not replace destination ownership checks." + } + }) +} + fn empty_request_schema() -> serde_json::Value { json!({}) } @@ -662,6 +908,7 @@ fn execute_health( "freshness": refresh_health["freshness"].clone(), "refresh_readiness": refresh_health["readiness"].clone(), "refresh_health": refresh_health, + "maintenance": maintenance_health_payload(&crate::protocol::MaintenanceSummary::default()), }); Ok(OperationResponse::from_payload( "health", @@ -670,6 +917,11 @@ fn execute_health( )) } +// Only compact cached summaries belong on this path; never scan an inventory. +fn maintenance_health_payload(summary: &crate::protocol::MaintenanceSummary) -> serde_json::Value { + serde_json::to_value(summary).expect("maintenance summary contains only serializable values") +} + fn unix_time_millis() -> u128 { SystemTime::now() .duration_since(UNIX_EPOCH) @@ -1027,14 +1279,6 @@ fn materialization_operation_response( } fn materialization_api_error(message: String) -> ApiError { - let Ok(details) = serde_json::from_str::(&message) else { - return ApiError::new("materialization_failed", message); - }; - if details.get("error").and_then(serde_json::Value::as_str) == Some("memory_budget_exceeded") { - return ApiError::new("memory_budget_exceeded", message) - .with_details(details) - .retryable(false); - } ApiError::new("materialization_failed", message) } @@ -1142,7 +1386,7 @@ fn materialization_payload( #[cfg(test)] mod tests { - use super::{materialization_api_error, refresh_health_payload}; + use super::refresh_health_payload; use crate::api::contracts::{ MaterializationRequest, McpInstallRequest, OperationInvocation, OperationRequest, OutputFormat, RepoSelector, RepositoryLifecycleRequest, @@ -1166,24 +1410,6 @@ mod tests { )) } - #[test] - fn materialization_memory_failure_preserves_structured_details() { - let error = materialization_api_error( - serde_json::json!({ - "error": "memory_budget_exceeded", - "phase": "staging", - "limit_bytes": 1024, - "accounted_bytes": 2048, - "observed_rss_bytes": 1536, - }) - .to_string(), - ); - - assert_eq!(error.code, "memory_budget_exceeded"); - assert!(!error.retryable); - assert_eq!(error.details.unwrap()["phase"], "staging"); - } - fn write_legacy_install_config(root: &PathBuf) { let state = root.join(".codebaseGraph"); fs::create_dir_all(&state).unwrap(); @@ -1238,6 +1464,29 @@ mod tests { ); } + #[test] + fn health_schema_documents_explicit_default_false_quiescence() { + let descriptor = ApiCore::new() + .operations() + .into_iter() + .find(|descriptor| descriptor.id == "health") + .unwrap(); + let schema = (descriptor.request_schema)(); + assert_eq!(schema["confirm_legacy_quiescence"]["type"], "boolean"); + assert_eq!(schema["confirm_legacy_quiescence"]["default"], false); + assert!(schema["confirm_legacy_quiescence"]["description"] + .as_str() + .unwrap() + .contains("stopped and upgraded")); + let payload = + super::maintenance_health_payload(&crate::protocol::MaintenanceSummary::default()); + assert!(payload["bytes"]["total"]["allocated_bytes"].is_null()); + assert_eq!( + payload["reasons"], + serde_json::json!(["maintenance_not_observed"]) + ); + } + #[test] fn duplicate_operations_are_rejected_in_registration() { let core = ApiCore::new(); @@ -1337,8 +1586,6 @@ mod tests { exclude_patterns: Vec::new(), candidate_paths: Vec::new(), parallel: false, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, progress: false, @@ -1361,8 +1608,6 @@ mod tests { exclude_patterns: Vec::new(), candidate_paths: Vec::new(), parallel: false, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, progress: false, diff --git a/src/api/facade.rs b/src/api/facade.rs index 77ed4cd9..f902dd9b 100644 --- a/src/api/facade.rs +++ b/src/api/facade.rs @@ -5,7 +5,10 @@ use crate::api::{ RefreshWatchConfig, RefreshWatchObserver, RepoSelector, }, core::{ApiCore, OperationDescriptor}, - refresh::{run_refresh_watch, start_refresh_service, RefreshServiceConfig}, + refresh::{ + run_refresh_watch, start_refresh_service_with_maintenance, RefreshCancellation, + RefreshRuntime, RefreshServiceConfig, + }, }; use crate::coordinator::{CoordinatorApiConfig, CoordinatorClient}; @@ -81,14 +84,41 @@ impl CodebaseGraphApi { self.core.resolve_mcp_operation(tool_name) } + #[cfg(test)] pub(crate) fn for_coordinator_owner( selector: RepoSelector, refresh: Option, - ) -> Self { - let refresh = refresh.map(|config| start_refresh_service(selector, config)); - Self { - core: ApiCore::for_coordinator(refresh), - } + cancellation: RefreshCancellation, + ) -> (Self, Option) { + Self::for_coordinator_owner_with_maintenance(selector, refresh, cancellation, None, None) + } + + pub(crate) fn for_coordinator_owner_with_maintenance( + selector: RepoSelector, + refresh: Option, + cancellation: RefreshCancellation, + maintenance: Option< + crate::storage::maintenance::MaintenanceClient< + std::sync::Arc, + >, + >, + unavailable: Option, + ) -> (Self, Option) { + let runtime = refresh.map(|config| { + start_refresh_service_with_maintenance( + selector, + config, + cancellation, + maintenance.clone(), + ) + }); + let state = runtime.as_ref().map(RefreshRuntime::state); + ( + Self { + core: ApiCore::for_coordinator_with_maintenance(state, maintenance, unavailable), + }, + runtime, + ) } pub(crate) fn watch_repository( @@ -110,6 +140,24 @@ impl CodebaseGraphApi { "2025-11-25" } + pub(crate) fn prepare_legacy_confirmation_request( + &self, + operation_id: &str, + invocation: &OperationInvocation, + ) -> Result, ApiError> { + self.core + .prepare_legacy_confirmation_request(operation_id, invocation) + } + pub(crate) fn execute_invocation_with_legacy_confirmation( + &self, + operation_id: &str, + invocation: &OperationInvocation, + handled: Option, + ) -> Result { + self.core + .execute_invocation_with_legacy_confirmation(operation_id, invocation, handled) + } + pub fn execute_invocation( &self, operation_id: &str, diff --git a/src/api/graph_read.rs b/src/api/graph_read.rs index fcb6bb9d..11a2d9db 100644 --- a/src/api/graph_read.rs +++ b/src/api/graph_read.rs @@ -1,9 +1,10 @@ use crate::api::catalog::support::{metadata_payload, value_array, value_str, GRAPH_SCHEMA_JSON}; use crate::db_writer::{ - connect_ladybug_database, open_ladybug_database, retry_transient_database, READ_RETRY_POLICY, + connect_ladybug_database, open_ladybug_database, open_ladybug_database_with_config, + retry_transient_database, READ_RETRY_POLICY, }; use crate::protocol::SearchBackendMetadata; -use lbug::{Connection, Database, SystemConfig, Value}; +use lbug::{Connection, SystemConfig, Value}; use serde_json::json; use std::{collections::BTreeSet, path::Path}; @@ -54,12 +55,8 @@ pub(crate) fn execute_graph_search( manifest_path: &Path, options: &GraphSearchRequest, ) -> Result, String> { - let db = Database::new(db_path, SystemConfig::default().read_only(true)).map_err(|error| { - format!( - "failed to open graph database {}: {error}", - db_path.display() - ) - })?; + let db = open_ladybug_database_with_config(db_path, SystemConfig::default().read_only(true)) + .map_err(|error| error.to_string())?; let conn = Connection::new(&db).map_err(|error| format!("failed to connect to graph: {error}"))?; let candidate_limit = options.limit.clamp(10, 50); @@ -236,12 +233,8 @@ pub(crate) fn execute_graph_context( if options.context_limit == 0 || options.budget == 0 { return Ok(Vec::new()); } - let db = Database::new(db_path, SystemConfig::default().read_only(true)).map_err(|error| { - format!( - "failed to open graph database {}: {error}", - db_path.display() - ) - })?; + let db = open_ladybug_database_with_config(db_path, SystemConfig::default().read_only(true)) + .map_err(|error| error.to_string())?; let conn = Connection::new(&db).map_err(|error| format!("failed to connect to graph: {error}"))?; let schema = metadata_payload(GRAPH_SCHEMA_JSON)?; @@ -1014,8 +1007,6 @@ mod tests { )]; write_database(LadybugWriteRequest { db_path: db_path.to_string_lossy().to_string(), - worker_memory_bytes: 768 * 1024 * 1024, - buffer_pool_bytes: 256 * 1024 * 1024, max_num_threads: 1, defer_hash_indexes: false, include_fts: true, diff --git a/src/api/materialization.rs b/src/api/materialization.rs index 856d83fe..a6c834d5 100644 --- a/src/api/materialization.rs +++ b/src/api/materialization.rs @@ -7,12 +7,17 @@ use crate::api::{ }; use crate::artifact_store::ArtifactStore; use crate::protocol::{ - ManifestEntry, NativeManifest, NativeSyntaxMaterializationRequest, + MaintenanceSummary, ManifestEntry, NativeManifest, NativeSyntaxMaterializationRequest, NativeSyntaxMaterializationResponse, MATERIALIZATION_MANIFEST_SCHEMA_VERSION, }; use crate::storage::direct::{DirectStore, DirectWriteSession}; -use crate::storage::layout::{DirectLayout, RepositoryLayout, DIRECT_DB_SIDECAR_SUFFIXES}; -use crate::storage::managed::{GraphStorage, ManagedStore, ManagedWriteSession, StorageMode}; +use crate::storage::layout::{ + DirectLayout, ManagedLayout, RepositoryLayout, DIRECT_DB_SIDECAR_SUFFIXES, +}; +use crate::storage::managed::{ + GraphStorage, ManagedJanitorReport, ManagedReadSnapshot, ManagedStore, ManagedWriteSession, + StorageMode, +}; use crate::storage::run_workspace::{RunPhase, RunWorkspace}; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet}; @@ -43,8 +48,6 @@ pub(crate) struct MaterializeOptions { pub(crate) exclude_patterns: Vec, pub(crate) candidate_paths: Vec, pub(crate) parallel: bool, - pub(crate) worker_memory_mib: Option, - pub(crate) rust_memory_mib: Option, pub(crate) spill_chunk_mib: Option, pub(crate) max_parallelism: Option, pub(crate) progress: bool, @@ -78,8 +81,6 @@ impl Default for MaterializeOptions { exclude_patterns: Vec::new(), candidate_paths: Vec::new(), parallel: true, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, progress: false, @@ -116,8 +117,6 @@ impl MaterializeOptions { exclude_patterns: request.exclude_patterns.clone(), candidate_paths: request.candidate_paths.clone(), parallel: request.parallel, - worker_memory_mib: request.worker_memory_mib, - rust_memory_mib: request.rust_memory_mib, spill_chunk_mib: request.spill_chunk_mib, max_parallelism: request.max_parallelism, progress: request.progress, @@ -196,8 +195,6 @@ fn resolve_materialization_path(path: &Path) -> Result { pub(crate) struct ConfigScanRules { pub(crate) include_patterns: Vec, pub(crate) exclude_patterns: Vec, - pub(crate) worker_memory_mib: Option, - pub(crate) rust_memory_mib: Option, pub(crate) spill_chunk_mib: Option, pub(crate) max_parallelism: Option, } @@ -228,8 +225,6 @@ pub(crate) fn materialization_request( exclude_patterns: options.exclude_patterns.clone(), candidate_paths: options.candidate_paths.clone(), parallel: options.parallel, - worker_memory_mib: options.worker_memory_mib, - rust_memory_mib: options.rust_memory_mib, spill_chunk_mib: options.spill_chunk_mib, max_parallelism: options.max_parallelism, progress: options.progress, @@ -308,13 +303,14 @@ pub(crate) fn execute_materialization_request( } let started = Instant::now(); let final_request = request; - let mut response = - match crate::db_writer::with_phase_workspace_root(execution.phase_workspace_root(), || { - crate::execute_materialization_pipeline(&final_request) - }) { - Ok(response) => response, - Err(error) => return Err(execution.abort_with_cleanup(error.to_string())), - }; + let mut response = match crate::db_writer::with_phase_workspace_scope( + execution.phase_workspace_root(), + execution.phase_owner_scope(), + || crate::execute_materialization_pipeline(&final_request), + ) { + Ok(response) => response, + Err(error) => return Err(execution.abort_with_cleanup(error.to_string())), + }; response.phase_timings.insert( "native_cli_seconds".to_string(), started.elapsed().as_secs_f64(), @@ -403,6 +399,13 @@ enum StorageExecution { } impl StorageExecution { + fn phase_owner_scope(&self) -> Option { + match self { + Self::Direct { workspace, .. } => workspace.phase_owner_scope(), + Self::Managed { session, .. } => session.phase_owner_scope(), + } + } + fn phase_workspace_root(&self) -> PathBuf { match self { Self::Direct { session, .. } => session.phase_workspace_root(), @@ -553,14 +556,6 @@ pub(crate) fn build_request( }; let staging_dir = paths.state_dir.join("native-staging"); let artifact_root = paths.state_dir.join("artifacts"); - let worker_memory_mib = options - .worker_memory_mib - .or(config_rules.worker_memory_mib) - .unwrap_or(crate::api::context::DEFAULT_WORKER_MEMORY_MIB); - let rust_memory_mib = options - .rust_memory_mib - .or(config_rules.rust_memory_mib) - .unwrap_or(crate::api::context::DEFAULT_RUST_MEMORY_MIB); let spill_chunk_mib = options .spill_chunk_mib .or(config_rules.spill_chunk_mib) @@ -569,12 +564,7 @@ pub(crate) fn build_request( .max_parallelism .or(config_rules.max_parallelism) .unwrap_or(crate::api::context::DEFAULT_MAX_PARALLELISM); - validate_materialization_limits( - worker_memory_mib, - rust_memory_mib, - spill_chunk_mib, - max_parallelism, - )?; + validate_execution_options(spill_chunk_mib, max_parallelism)?; Ok(NativeSyntaxMaterializationRequest { source_root: source_root.to_string_lossy().to_string(), @@ -599,8 +589,6 @@ pub(crate) fn build_request( atomic_rebuild: true, strict: true, parallel: options.parallel, - worker_memory_mib, - rust_memory_mib, spill_chunk_mib, max_parallelism, progress: options.progress, @@ -627,12 +615,6 @@ pub(crate) fn read_materialization_config_rules(path: &Path) -> Result Result Result<(), String> { - if worker_memory_mib == 0 - || rust_memory_mib == 0 - || spill_chunk_mib == 0 - || max_parallelism == 0 - { - return Err("materialization memory limits and max_parallelism must be positive".into()); - } - if rust_memory_mib > worker_memory_mib { - return Err("rust_memory_mib must not exceed worker_memory_mib".into()); - } - if spill_chunk_mib > rust_memory_mib { - return Err("spill_chunk_mib must not exceed rust_memory_mib".into()); +fn validate_execution_options(spill_chunk_mib: u64, max_parallelism: usize) -> Result<(), String> { + if spill_chunk_mib == 0 || max_parallelism == 0 { + return Err("spill_chunk_mib and max_parallelism must be positive".into()); } Ok(()) } @@ -813,6 +780,11 @@ pub(crate) fn write_manifest( graph_build_digest: Option, search_backend: Option, ) -> Result { + let _reference_mutation = + crate::storage::maintenance_ownership::ReferenceMutation::begin_producing( + &request.resolved_artifact_root(), + ) + .map_err(|error| error.to_string())?; let files = materialized_entries .iter() .map(|(path, entry)| (path.clone(), entry.clone().compact())) @@ -846,8 +818,15 @@ fn prepare_storage_execution(options: &MaterializeOptions) -> Result Result, ) -> Result<(), String> { + // Finalization occurs after the public pipeline's guard is released. + // The execution/session and scoped run remain active through all effects. + let _reference_mutation = + crate::storage::maintenance_ownership::ReferenceMutation::begin_existing( + execution.artifact_root(), + ) + .map_err(|error| error.to_string())?; let request_db_path = execution.request_db_path(); let request_manifest_path = execution.request_manifest_path(); let artifact_root = execution.artifact_root().to_path_buf(); @@ -1013,28 +1006,42 @@ fn finalize_materialization( .map(|cleanup| cleanup.to_string()), )); } - if let Err(error) = garbage_collect_artifacts(&artifact_root, &published_manifest) { - return Err(append_cleanup_error( - error.clone(), - workspace - .abort(Some(error)) - .err() - .map(|cleanup| cleanup.to_string()), - )); - } + defer_artifact_maintenance(response); response.storage_format = "direct".to_string(); response.active_generation = None; response.cleanup_pending = false; response.pending_runs = 0; + if let Some(error) = session.take_retirement_issue() { + response.cleanup_pending = true; + record_postpublication_cleanup_error( + response, + "direct_active_attestation_pending", + error.to_string(), + ); + } if !manifest_path.exists() { return Err(format!( "direct manifest publication did not produce {}", manifest_path.display() )); } - workspace - .finish() - .map_err(|error| format!("failed to clean direct run workspace: {error}"))?; + // Publication, its journal transition and required readback have + // succeeded. Failure to dispose the published run is independent. + #[cfg(test)] + POST_DIRECT_CLEANUP_HOOK.with(|hook| { + if let Some(hook) = hook.take() { + hook(workspace.root()); + } + }); + if let Err(error) = workspace.finish() { + response.cleanup_pending = true; + response.pending_runs = 1; + record_postpublication_cleanup_error( + response, + "direct_run_cleanup_pending", + format!("failed to clean direct run workspace: {error}"), + ); + } session.finish(); } StorageExecution::Managed { @@ -1043,43 +1050,168 @@ fn finalize_materialization( store, .. } => { - let published_generation = session - .publish_with_stats(&response.graph_summary) + let publication = session + .publish_with_stats_typed(&response.graph_summary) .map_err(|error| format!("failed to publish managed graph generation: {error}"))?; - let read = store - .open_read() - .map_err(|error| format!("failed to reopen managed graph generation: {error}"))?; + let published_generation = publication.generation_id; + let mut janitor = publication.janitor; + let read = match store.open_read() { + Ok(read) => read, + Err(error) => { + return Err(append_managed_janitor_secondary( + format!("failed to reopen managed graph generation: {error}"), + &mut janitor, + )) + } + }; if read.generation_id != published_generation { - return Err(format!( + let error = format!( "managed publish activated {}, but reopened generation was {}", published_generation, read.generation_id - )); + ); + release_managed_validation_snapshot(read, &session, &store, &published_generation); + return Err(append_managed_janitor_secondary(error, &mut janitor)); } - let published_manifest = read_manifest(&read.manifest_path)?; - validate_candidate_bundle( + let published_manifest = match read_manifest(&read.manifest_path) { + Ok(manifest) => manifest, + Err(error) => { + release_managed_validation_snapshot( + read, + &session, + &store, + &published_generation, + ); + return Err(append_managed_janitor_secondary(error, &mut janitor)); + } + }; + if let Err(error) = validate_candidate_bundle( &read.db_path, &read.manifest_path, &artifact_root, &published_manifest, response, - )?; - let cleanup = session - .recover_and_gc() - .map_err(|error| format!("failed to reconcile managed storage cleanup: {error}"))?; - if cleanup.run_recovery.skipped_locked == 0 { - garbage_collect_artifacts(&artifact_root, &published_manifest)?; + ) { + release_managed_validation_snapshot(read, &session, &store, &published_generation); + return Err(append_managed_janitor_secondary(error, &mut janitor)); + } + #[cfg(test)] + POST_MANAGED_VERIFICATION_HOOK.with(|hook| { + if let Some(hook) = hook.take() { + hook(&session, &store); + } + }); + let reconciliation = match session.reconcile_typed() { + Ok(report) => report, + Err(error) => { + release_managed_validation_snapshot( + read, + &session, + &store, + &published_generation, + ); + return Err(append_managed_janitor_secondary( + format!("failed to reconcile managed storage cleanup: {error}"), + &mut janitor, + )); + } + }; + #[cfg(test)] + POST_MANAGED_RECONCILE_HOOK.with(|hook| { + if let Some(hook) = hook.take() { + hook(&session, &store); + } + }); + let recovery_complete = reconciliation.recovery.observation_complete; + janitor.merge_latest(reconciliation.janitor); + if let Err(error) = session.check_active_generation(&published_generation) { + release_managed_validation_snapshot(read, &session, &store, &published_generation); + return Err(append_managed_janitor_secondary( + error.to_string(), + &mut janitor, + )); + } + if recovery_complete { + let verified = NativeVerifiedGeneration { + session: &session, + store: &store, + read: &read, + published: &published_generation, + }; + if let Err(error) = + crate::storage::maintenance_generations::seal_verified_generation(&verified) + { + janitor.record_generation_disposition_failure(error); + } + } + if !recovery_complete { + janitor.deferred_reason = Some("managed_recovery_deferred"); + janitor.pending = true; + janitor.observation_complete = false; } + // Release every generation/native-family reader before publishing + // completion and releasing the writer to the bounded controller. + release_managed_validation_snapshot(read, &session, &store, &published_generation); + defer_artifact_maintenance(response); response.storage_format = "managed_v2".to_string(); response.active_generation = Some(published_generation); - response.cleanup_pending = - cleanup.retired_generations_pending > 0 || cleanup.run_recovery.skipped_locked > 0; - response.pending_runs = cleanup.run_recovery.skipped_locked; - session.finish(); + response.cleanup_pending = janitor.pending + || janitor.legacy.retired_generations_pending > 0 + || janitor.legacy.run_recovery.skipped_locked > 0; + response.pending_runs = janitor.legacy.run_recovery.skipped_locked; + record_managed_janitor_report(response, janitor); + session.finish_without_cleanup(); } } Ok(()) } +fn release_managed_validation_snapshot( + read: ManagedReadSnapshot, + session: &ManagedWriteSession, + store: &ManagedStore, + generation: &str, +) { + drop(read); + #[cfg(test)] + POST_MANAGED_SNAPSHOT_DROP_HOOK.with(|hook| { + if let Some(hook) = hook.take() { + hook(session, store, generation); + } + }); + #[cfg(not(test))] + let _ = (session, store, generation); +} + +/// Only this module constructs this token, after actual native readback and +/// required reconciliation/current-identity checks. Its borrows retain the +/// genuine publishing session writer and the verified generation reader. +pub(crate) struct NativeVerifiedGeneration<'a> { + session: &'a ManagedWriteSession, + store: &'a ManagedStore, + read: &'a ManagedReadSnapshot, + published: &'a str, +} +impl NativeVerifiedGeneration<'_> { + pub(crate) fn layout(&self) -> &ManagedLayout { + self.store.layout() + } + pub(crate) fn writer(&self) -> &crate::storage::locks::WriterLease { + self.session + .writer_lease() + .expect("verified publication retains its actual writer") + } + pub(crate) fn generation_id(&self) -> &str { + debug_assert_eq!(self.read.generation_id, self.published); + self.published + } + pub(crate) fn previous_generation_id(&self) -> Option<&str> { + self.session + .base_generation + .as_ref() + .map(|generation| generation.generation_id.as_str()) + } +} + fn run_root_from_candidate(candidate_root: &Path) -> PathBuf { candidate_root .parent() @@ -1175,30 +1307,97 @@ fn validate_candidate_bundle( Ok(()) } -fn garbage_collect_artifacts( - artifact_root: &Path, - active_manifest: &NativeManifest, -) -> Result<(), String> { - let retained = active_manifest - .files - .values() - .filter_map(|entry| entry.artifact_key.clone()) - .collect::>(); - let store = ArtifactStore::new(artifact_root); - for key in store.list_keys().map_err(|error| { - format!( - "failed to list artifacts in {}: {error}", - artifact_root.display() - ) - })? { - if retained.contains(&key) { - continue; +/// The former manifest-only deletion loop had no destination/use ownership +/// proof and could scan without a budget. Leave the owned collector's work +/// pending rather than exercising authorityless ArtifactStore::delete_key. +fn defer_artifact_maintenance(response: &mut NativeSyntaxMaterializationResponse) { + let summary = response + .maintenance + .get_or_insert_with(MaintenanceSummary::default); + summary.pending = true; + let reason = "artifact_collection_requires_owned_maintenance"; + if !summary.reasons.iter().any(|value| value == reason) { + summary.reasons.push(reason.into()); + } +} + +fn record_postpublication_cleanup_error( + response: &mut NativeSyntaxMaterializationResponse, + reason: &str, + error: String, +) { + let summary = response + .maintenance + .get_or_insert_with(MaintenanceSummary::default); + summary.pending = true; + summary.reasons.push(reason.into()); + summary.errors.push(error.clone()); + response + .diagnostics + .push(format!("cleanup pending: {error}")); +} + +fn record_managed_janitor_report( + response: &mut NativeSyntaxMaterializationResponse, + mut report: ManagedJanitorReport, +) { + let summary = response + .maintenance + .get_or_insert_with(MaintenanceSummary::default); + if !report.observation_complete { + summary.observation_complete = false; + summary + .reasons + .push("managed_janitor_observation_incomplete".into()); + } + if let Some(reason) = report.deferred_reason { + if !report.observation_complete { + summary.reasons.push(reason.into()); } - store - .delete_key(&key) - .map_err(|error| format!("failed to delete artifact {key}: {error}"))?; } - Ok(()) + summary.pending |= report.pending; + if let Some(issue) = report.first_issue.take() { + let reason = if report.pending { + issue.stage.reason() + } else { + "managed_janitor_issue_resolved" + }; + summary.reasons.push(reason.into()); + let error = issue.source.to_string(); + summary.errors.push(error.clone()); + response.diagnostics.push(format!( + "{}: {error}", + if report.pending { + "cleanup pending" + } else { + "cleanup diagnostic" + } + )); + } + if report.issue_count > 1 { + summary + .reasons + .push("additional_managed_janitor_errors_omitted".into()); + response.diagnostics.push(format!( + "{} additional managed cleanup errors", + report.issue_count - 1 + )); + } +} + +#[cfg(test)] +type ManagedTestHook = fn(&ManagedWriteSession, &ManagedStore); +#[cfg(test)] +type ManagedSnapshotTestHook = fn(&ManagedWriteSession, &ManagedStore, &str); + +#[cfg(test)] +thread_local! { + // Inject a real workspace cleanup failure only after commit/readback. + static POST_DIRECT_CLEANUP_HOOK: std::cell::Cell> = const { std::cell::Cell::new(None) }; + static POST_MANAGED_VERIFICATION_HOOK: std::cell::Cell> = const { std::cell::Cell::new(None) }; + static POST_MANAGED_RECONCILE_HOOK: std::cell::Cell> = const { std::cell::Cell::new(None) }; + static POST_MANAGED_SNAPSHOT_DROP_HOOK: std::cell::Cell> = const { std::cell::Cell::new(None) }; + static MANAGED_SNAPSHOT_RELEASE_OBSERVED: std::cell::Cell = const { std::cell::Cell::new(false) }; } fn cleanup_direct_candidate(session: &DirectWriteSession) -> Result<(), String> { @@ -1252,6 +1451,19 @@ fn remove_path_if_exists(path: &Path) -> Result<(), String> { } } +fn append_managed_janitor_secondary( + primary_error: String, + report: &mut ManagedJanitorReport, +) -> String { + append_cleanup_error( + primary_error, + report + .first_issue + .take() + .map(|issue| issue.source.to_string()), + ) +} + fn append_cleanup_error(primary_error: String, cleanup_error: Option) -> String { match cleanup_error { Some(cleanup_error) if !cleanup_error.is_empty() => { @@ -1283,7 +1495,8 @@ mod tests { use std::time::{SystemTime, UNIX_EPOCH}; fn unique_temp_dir(prefix: &str) -> PathBuf { - std::env::temp_dir().join(format!( + // Ownership fixtures need a real parent; macOS TMPDIR can start at /var. + std::env::temp_dir().canonicalize().unwrap().join(format!( "{prefix}-{}-{}", std::process::id(), SystemTime::now() @@ -1293,6 +1506,50 @@ mod tests { )) } + #[test] + fn dc_epoch_manifest_finalization_advances_tracked_reference_domain() { + use crate::storage::layout::{create_fresh_reference_root, ManagedLayout}; + use crate::storage::locks::{open_locked, LockMode}; + use crate::storage::maintenance_ownership::{ + initialize_fresh_reference_domain, observe_reference_epoch, EpochObservation, + ReferenceDomain, + }; + let root = unique_temp_dir("epoch-finalization-manifest"); + fs::create_dir_all(&root).unwrap(); + let storage = root.join("storage"); + let fresh = create_fresh_reference_root(&storage).unwrap().unwrap(); + let layout = ManagedLayout::new(&storage); + layout.ensure_roots().unwrap(); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + drop(writer); + let before = match observe_reference_epoch(&domain).unwrap() { + EpochObservation::Verified(epoch) => epoch, + other => panic!("{other:?}"), + }; + let path = layout + .generations_root() + .join("gen-finalization/manifest.json"); + let mut request = build_request(&MaterializeOptions { + source_root: Some(root.clone()), + db: Some(storage.join("candidate.ldb")), + manifest: Some(path.clone()), + ..MaterializeOptions::default() + }) + .unwrap(); + request.artifact_root = layout.artifacts_root().to_string_lossy().into_owned(); + let manifest = write_manifest(&path, &request, &BTreeMap::new(), None, None).unwrap(); + assert!(manifest.files.is_empty()); + assert!(path.is_file()); + let after = match observe_reference_epoch(&domain).unwrap() { + EpochObservation::Verified(epoch) => epoch, + other => panic!("{other:?}"), + }; + assert!(after.counter() > before.counter()); + fs::remove_dir_all(root).unwrap(); + } + #[test] fn native_request_source_root_must_match_a_bound_repository_identity() { let selected_root = unique_temp_dir("codebase-graph-materialization-identity-source"); @@ -1412,8 +1669,8 @@ mod tests { } #[test] - fn artifact_gc_retains_active_manifest_keys_and_removes_stale_keys() { - let root = unique_temp_dir("codebase-graph-artifact-gc"); + fn deferred_artifact_collection_preserves_unregistered_foreign_state() { + let root = unique_temp_dir("unregistered-artifacts"); let retained_key = "a".repeat(64); let stale_key = "b".repeat(64); let retained_dir = root.join("aa").join(&retained_key); @@ -1422,36 +1679,173 @@ mod tests { fs::create_dir_all(&stale_dir).unwrap(); fs::write(retained_dir.join("partition.json"), "retained").unwrap(); fs::write(stale_dir.join("partition.json"), "stale").unwrap(); - let manifest = NativeManifest { - schema_version: 2, - ontology: "code_ontology_v1".to_string(), - parser_version: "native".to_string(), - graph_build_digest: Some("digest".to_string()), - search_backend: None, - files: BTreeMap::from([( - "src/lib.rs".to_string(), - ManifestEntry { - path: "src/lib.rs".to_string(), - content_hash: "hash".to_string(), - language: "rust".to_string(), - partition_id: "partition".to_string(), - artifact_key: Some(retained_key.clone()), - node_ids: Vec::new(), - edge_ids: Vec::new(), - node_types: BTreeMap::new(), - edge_types: BTreeMap::new(), - node_count: 0, - edge_count: 0, - materialized_at: "unix:0".to_string(), - }, - )]), + let mut response = empty_test_response(); + defer_artifact_maintenance(&mut response); + let summary = response.maintenance.unwrap(); + assert!(summary.pending); + assert!(summary.inventory_status.is_none()); + assert!(summary.bytes.unknown.allocated_bytes.is_none()); + assert!(summary + .reasons + .iter() + .any(|reason| reason == "artifact_collection_requires_owned_maintenance")); + let authority_error = ArtifactStore::new(&root) + .delete_key(&stale_key) + .unwrap_err(); + assert!(authority_error + .to_string() + .contains("requires_current_maintenance_authority")); + assert!(!summary.requested); + assert!(!summary.running); + assert_eq!( + fs::read(retained_dir.join("partition.json")).unwrap(), + b"retained" + ); + assert_eq!( + fs::read(stale_dir.join("partition.json")).unwrap(), + b"stale" + ); + fs::remove_dir_all(root).unwrap(); + } + + fn empty_test_response() -> NativeSyntaxMaterializationResponse { + NativeSyntaxMaterializationResponse::skipped( + BTreeMap::new(), + crate::protocol::ManifestDiff { + added: Vec::new(), + modified: Vec::new(), + unchanged: Vec::new(), + deleted: Vec::new(), + force_rebuild: false, + }, + Vec::new(), + Vec::new(), + BTreeMap::new(), + ) + } + + fn committed_artifact_fixture(managed: bool) -> (PathBuf, MaterializeOptions, PathBuf) { + let root = unique_temp_dir(if managed { "postgc-m" } else { "postgc-d" }); + fs::create_dir_all(root.join("src")).unwrap(); + fs::write(root.join("src/lib.rs"), "pub fn first_version() {}\n").unwrap(); + let options = MaterializeOptions { + source_root: Some(root.clone()), + mode: "full".into(), + parallel: false, + storage_root: managed.then(|| root.join(".codebaseGraph/storage")), + db: (!managed).then(|| root.join("output/graph.ldb")), + manifest: (!managed).then(|| root.join("output/manifest.json")), + ..MaterializeOptions::default() + }; + let (request, _) = execute_materialization(&options).unwrap(); + let artifact_root = PathBuf::from(request.artifact_root); + (root, options, artifact_root) + } + + fn assert_published_graph_readable( + options: &MaterializeOptions, + response: &NativeSyntaxMaterializationResponse, + ) { + let database = if let Some(storage_root) = &options.storage_root { + GraphStorage::managed(storage_root) + .layout() + .generation(response.active_generation.as_ref().unwrap()) + .unwrap() + .db_path() + } else { + options.db.clone().unwrap() }; + assert!(crate::api::graph_read::count_graph_nodes(&database).unwrap() > 0); + } - garbage_collect_artifacts(&root, &manifest).unwrap(); + fn verify_committed_artifact_deferral(managed: bool) { + let (root, options, artifact_root) = committed_artifact_fixture(managed); + // This residue was introduced outside the producing protocol and must + // survive. It reproduced the old authorityless postcommit GC failure. + let stale_key = "b".repeat(64); + let foreign_payload = artifact_root + .join("bb") + .join(stale_key) + .join("partition.json"); + fs::create_dir_all(foreign_payload.parent().unwrap()).unwrap(); + fs::write(&foreign_payload, "foreign unvalidated payload").unwrap(); + fs::write(root.join("src/lib.rs"), "pub fn second_version() {}\n").unwrap(); + let (_, response) = execute_materialization(&options) + .expect("committed publication must survive deferred artifact GC"); + assert!(response.database_written); + assert_eq!( + response.storage_format, + if managed { "managed_v2" } else { "direct" } + ); + let summary = response.maintenance.as_ref().unwrap(); + assert!(summary.pending); + assert!(summary + .reasons + .iter() + .any(|reason| reason == "artifact_collection_requires_owned_maintenance")); + assert!(summary.last_attempt.is_none()); + assert!(summary.bytes.total.allocated_bytes.is_none()); + assert_eq!( + fs::read_to_string(&foreign_payload).unwrap(), + "foreign unvalidated payload" + ); + assert_published_graph_readable(&options, &response); + // The new optional field must decode old worker/native responses. + let mut old_payload = serde_json::to_value(&response).unwrap(); + old_payload.as_object_mut().unwrap().remove("maintenance"); + assert!( + serde_json::from_value::(old_payload) + .unwrap() + .maintenance + .is_none() + ); + fs::remove_dir_all(root).unwrap(); + } - assert!(retained_dir.exists()); - assert!(!stale_dir.exists()); - let _ = fs::remove_dir_all(root); + #[test] + fn committed_direct_graph_survives_deferred_artifact_collection() { + verify_committed_artifact_deferral(false); + } + + #[test] + fn committed_managed_graph_survives_deferred_artifact_collection() { + verify_committed_artifact_deferral(true); + } + + #[test] + fn committed_direct_graph_survives_real_workspace_cleanup_failure() { + let (root, options, _) = committed_artifact_fixture(false); + POST_DIRECT_CLEANUP_HOOK.with(|hook| { + hook.set(Some(|run_root| { + fs::write( + run_root.join("cleanup-barrier.json"), + "invalid barrier fixture", + ) + .unwrap(); + })) + }); + fs::write( + root.join("src/lib.rs"), + "pub fn after_cleanup_failure() {}\n", + ) + .unwrap(); + let (_, response) = execute_materialization(&options) + .expect("cleanup failure must not undo committed result"); + assert!(response.database_written); + assert!(response.cleanup_pending); + assert_eq!(response.pending_runs, 1); + let summary = response.maintenance.as_ref().unwrap(); + assert!(summary.pending); + assert!(summary + .errors + .iter() + .any(|error| error.contains("invalid run cleanup barrier"))); + assert!(response + .diagnostics + .iter() + .any(|diagnostic| diagnostic.starts_with("cleanup pending:"))); + assert_published_graph_readable(&options, &response); + fs::remove_dir_all(root).unwrap(); } #[test] @@ -1498,13 +1892,15 @@ mod tests { } #[test] - fn prepublication_failures_clean_managed_and_direct_run_state() { + fn prepublication_failures_close_managed_runs_and_clean_legacy_direct_run_state() { for phase in ["parsing", "staging", "database", "candidate_validation"] { let root = unique_temp_dir(&format!("codebase-graph-cleanup-{phase}")); let managed_root = root.join("managed"); let managed_store = GraphStorage::managed(&managed_root); let managed_session = managed_store.begin_write().unwrap(); - fs::write(managed_session.candidate_db_path(), "partial-db").unwrap(); + let managed_candidate = managed_session.candidate_db_path(); + let managed_run = run_root_from_candidate(managed_session.candidate.paths().root()); + fs::write(&managed_candidate, "partial-db").unwrap(); let managed_error = StorageExecution::Managed { artifact_root: managed_store.layout().artifacts_root(), session: managed_session, @@ -1513,10 +1909,29 @@ mod tests { } .abort_with_cleanup(format!("{phase} failed")); assert_eq!(managed_error, format!("{phase} failed")); - assert!(fs::read_dir(managed_store.layout().runs_root()) - .unwrap() - .next() - .is_none()); + // A registered owner publishes abort closure and leaves disposal + // to bounded collection. This manual, unreceipted payload conveys + // no deletion authority and must remain byte-for-byte intact. + assert!(managed_run.join(".workspace.binding").is_file()); + let journal: crate::storage::run_workspace::RunJournal = + serde_json::from_slice(&fs::read(managed_run.join("journal.json")).unwrap()) + .unwrap(); + assert_eq!(journal.phase, RunPhase::Failed); + assert_eq!(fs::read(&managed_candidate).unwrap(), b"partial-db"); + let domain = crate::storage::maintenance_ownership::ReferenceDomain::managed( + managed_store.layout(), + ) + .unwrap(); + let memory = crate::storage::maintenance_budget::CleanupMemory::new( + crate::storage::maintenance::MAINTENANCE_MEMORY_BYTES, + ) + .unwrap(); + crate::storage::maintenance_controls::prepare_terminal_workspace_boxed( + &managed_run, + &domain, + &memory, + ) + .unwrap(); let direct_layout = DirectLayout::new(root.join("graph.ldb"), root.join("manifest.json")); @@ -1624,8 +2039,6 @@ mod tests { "materialization": { "include": ["src/**/*.rs"], "exclude": ["target/**"], - "worker_memory_mib": 640, - "rust_memory_mib": 320, "spill_chunk_mib": 16, "max_parallelism": 1 } @@ -1676,8 +2089,6 @@ mod tests { ); assert_eq!(request.ignore_patterns, vec!["build/".to_string()]); assert!(request.candidate_paths.is_empty()); - assert_eq!(request.worker_memory_mib, 640); - assert_eq!(request.rust_memory_mib, 320); assert_eq!(request.spill_chunk_mib, 16); assert_eq!(request.max_parallelism, 1); let _ = fs::remove_dir_all(root); @@ -1692,8 +2103,6 @@ mod tests { state_dir.join("config.json"), serde_json::to_vec(&json!({ "materialization": { - "worker_memory_mib": 640, - "rust_memory_mib": 320, "spill_chunk_mib": 16, "max_parallelism": 1 } @@ -1704,16 +2113,12 @@ mod tests { let request = build_request(&MaterializeOptions { source_root: Some(root.clone()), - worker_memory_mib: Some(768), - rust_memory_mib: Some(384), spill_chunk_mib: Some(32), max_parallelism: Some(2), ..MaterializeOptions::default() }) .unwrap(); - assert_eq!(request.worker_memory_mib, 768); - assert_eq!(request.rust_memory_mib, 384); assert_eq!(request.spill_chunk_mib, 32); assert_eq!(request.max_parallelism, 2); let _ = fs::remove_dir_all(root); @@ -1747,4 +2152,269 @@ mod tests { ); let _ = fs::remove_dir_all(root); } + + #[test] + fn managed_committed_native_result_survives_real_run_disposal_failure() { + let (root, options, _) = committed_artifact_fixture(true); + crate::storage::managed::set_managed_cleanup_hook(|run| { + fs::write(run.join("cleanup-barrier.json"), b"invalid cleanup barrier").unwrap(); + }); + fs::write( + root.join("src/lib.rs"), + "pub fn managed_cleanup_pending_symbol() {}\n", + ) + .unwrap(); + let (_, response) = execute_materialization(&options) + .expect("verified committed graph must survive optional run disposal failure"); + assert!(response.database_written); + assert!(response.cleanup_pending); + assert_eq!( + response.pending_runs, 1, + "same run counted more than once across publication/reconcile" + ); + let summary = response.maintenance.as_ref().unwrap(); + assert!(summary.pending); + assert!(!summary.requested); + assert!(!summary.observation_complete); + assert!( + summary + .errors + .iter() + .any(|e| e.contains("invalid run cleanup barrier")), + "{summary:?}" + ); + assert!(response + .diagnostics + .iter() + .any(|d| d.starts_with("cleanup pending:"))); + assert_published_graph_readable(&options, &response); + let store = GraphStorage::managed(options.storage_root.as_ref().unwrap()); + for entry in fs::read_dir(store.layout().runs_root()).unwrap() { + let entry = entry.unwrap(); + if entry.path().join("cleanup-barrier.json").exists() { + fs::remove_file(entry.path().join("cleanup-barrier.json")).unwrap(); + } + } + store.cleanup().unwrap(); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn managed_native_pointer_error_is_primary_and_retains_publishing_evidence() { + use crate::storage::managed::{inject_managed_fault, ManagedFault}; + let (root, options, _) = committed_artifact_fixture(true); + fs::write( + root.join("src/lib.rs"), + "pub fn uncertain_managed_publication() {}\n", + ) + .unwrap(); + let fault = inject_managed_fault(ManagedFault::PointerAfterRename); + let error = execute_materialization(&options).unwrap_err(); + assert!( + error.starts_with("failed to publish managed graph generation:"), + "{error}" + ); + assert!(error.contains("after rename")); + drop(fault); + let store = GraphStorage::managed(options.storage_root.as_ref().unwrap()); + let active = store.read_active_generation().unwrap().unwrap(); + let generation = store.layout().generation(&active.generation_id).unwrap(); + assert!(!generation.retired_path().exists()); + let runs = fs::read_dir(store.layout().runs_root()) + .unwrap() + .map(|e| e.unwrap().path()) + .filter(|path| { + let journal: crate::storage::run_workspace::RunJournal = + serde_json::from_slice(&fs::read(path.join("journal.json")).unwrap()).unwrap(); + journal.phase == RunPhase::Publishing + }) + .collect::>(); + // Genuine closed terminal workspaces may await bounded collection; + // exactly one actual Publishing transaction must retain this failure. + assert_eq!(runs.len(), 1); + let journal = fs::read(runs[0].join("journal.json")).unwrap(); + assert_eq!( + serde_json::from_slice::(&journal) + .unwrap() + .phase, + RunPhase::Publishing + ); + assert!(crate::api::graph_read::count_graph_nodes(&generation.db_path()).unwrap() > 0); + assert_eq!(fs::read(runs[0].join("journal.json")).unwrap(), journal); + store.cleanup().unwrap(); + assert!(!runs[0].exists()); + fs::remove_dir_all(root).unwrap(); + } + + fn assert_managed_snapshot_released_with_writer_owned( + session: &ManagedWriteSession, + store: &ManagedStore, + generation: &str, + ) { + use crate::storage::locks::{try_open_existing_locked, LockMode}; + assert!(session.writer_lease().unwrap().is_current().unwrap()); + assert!( + try_open_existing_locked(&store.layout().writer_lock_path(), LockMode::Exclusive) + .unwrap() + .is_none(), + "actual publishing writer was released early" + ); + let paths = store.layout().generation(generation).unwrap(); + let generation_lease = + try_open_existing_locked(&paths.lease_path(), LockMode::Exclusive).unwrap(); + assert!( + generation_lease.is_some(), + "validation snapshot still owns its actual generation lease" + ); + drop(generation_lease); + MANAGED_SNAPSHOT_RELEASE_OBSERVED.with(|observed| observed.set(true)); + } + + #[test] + fn managed_native_postreconcile_active_change_remains_primary() { + let (root, options, _) = committed_artifact_fixture(true); + MANAGED_SNAPSHOT_RELEASE_OBSERVED.with(|observed| observed.set(false)); + POST_MANAGED_SNAPSHOT_DROP_HOOK + .with(|hook| hook.set(Some(assert_managed_snapshot_released_with_writer_owned))); + POST_MANAGED_RECONCILE_HOOK.with(|hook| { + hook.set(Some(|_, store| { + let _state = crate::storage::locks::open_locked( + store.layout().state_lock_path(), + crate::storage::locks::LockMode::Exclusive, + ) + .unwrap(); + crate::storage::atomic::write_json_atomically( + &store.layout().active_pointer_path(), + &crate::storage::managed::ActiveGeneration { + schema_version: 2, + generation_id: "unexpected-active".into(), + published_at: "fixture".into(), + activated_at_ms: 0, + }, + ) + .unwrap(); + })) + }); + fs::write( + root.join("src/lib.rs"), + "pub fn changed_before_report() {}\n", + ) + .unwrap(); + let error = execute_materialization(&options).unwrap_err(); + assert!(error.starts_with("managed publish activated"), "{error}"); + assert!(error.contains("unexpected-active"), "{error}"); + assert!( + MANAGED_SNAPSHOT_RELEASE_OBSERVED.with(|observed| observed.get()), + "error-path snapshot release was not observed" + ); + fs::remove_dir_all(root).unwrap(); + } + + #[cfg(unix)] + #[test] + fn bounded_measurement_counts_actual_native_graph_and_active_cache_without_following_foreign_link( + ) { + use crate::storage::maintenance_budget::CleanupMemory; + let (root, options, _) = committed_artifact_fixture(true); + let outside = unique_temp_dir("measure-outside"); + fs::create_dir_all(&outside).unwrap(); + let sentinel = outside.join("source-sentinel"); + fs::write(&sentinel, b"foreign source must remain untouched").unwrap(); + let state = root.join(".codebaseGraph"); + let target = crate::storage::inventory::MaintenanceTarget { + repository_root: root.clone(), + state_root: state.clone(), + storage_root: options.storage_root.clone(), + direct_pair: None, + config_path: None, + }; + let memory = + CleanupMemory::new(crate::storage::maintenance::MAINTENANCE_MEMORY_BYTES).unwrap(); + let (_, _, _, _, _, deferred_before) = + crate::storage::maintenance::observe_bytes_for_test(&target, &memory, &|| false) + .unwrap(); + std::os::unix::fs::symlink(&outside, state.join("foreign-link")).unwrap(); + let foreign_json = state.join("caller-owned.json"); + fs::write(&foreign_json, b"{\"caller\":\"preserve unknown state\"}").unwrap(); + let store = GraphStorage::managed(options.storage_root.as_ref().unwrap()); + let unknown_key = "f".repeat(64); + let unknown_root = store + .layout() + .artifacts_root() + .join("ff") + .join(&unknown_key); + fs::create_dir_all(&unknown_root).unwrap(); + let unknown_payload = unknown_root.join("artifact.json"); + fs::write(&unknown_payload, b"unreceipted caller payload").unwrap(); + let pointer_before = fs::read(store.layout().active_pointer_path()).unwrap(); + + let floor = memory.used(); + let (graph, cache, complete, references, unknown, deferred) = + crate::storage::maintenance::observe_bytes_for_test(&target, &memory, &|| false) + .unwrap(); + assert!(graph.logical_bytes.is_some_and(|bytes| bytes > 0)); + assert!(cache.logical_bytes.is_some_and(|bytes| bytes > 0)); + // Skipping the foreign symlink/unregistered subtree leaves the whole + // unknown category unmeasurable; observed leaf bytes are not its total. + assert_eq!(unknown.logical_bytes, None); + assert_eq!(unknown.allocated_bytes, None); + // The producer may already have genuine deferred terminal work. Adding + // unknown caller state must not reclassify its bytes as deferred work. + assert_eq!(deferred.logical_bytes, deferred_before.logical_bytes); + assert_eq!(deferred.allocated_bytes, deferred_before.allocated_bytes); + assert_eq!( + fs::read(&unknown_payload).unwrap(), + b"unreceipted caller payload" + ); + assert_eq!( + fs::read(&foreign_json).unwrap(), + b"{\"caller\":\"preserve unknown state\"}" + ); + assert!( + references, + "actual active manifest references were not observed" + ); + assert!(!complete, "foreign symlink uncertainty was hidden"); + assert_eq!(memory.used(), floor, "advisory buffers escaped observation"); + assert!(memory.peak() <= memory.limit()); + assert_eq!( + fs::read(store.layout().active_pointer_path()).unwrap(), + pointer_before + ); + assert_eq!( + fs::read(&sentinel).unwrap(), + b"foreign source must remain untouched" + ); + let read = store.open_read().unwrap(); + assert!(crate::api::graph_read::count_graph_nodes(&read.db_path).unwrap() > 0); + drop(read); + let tiny = CleanupMemory::new(4096).unwrap(); + let tiny_floor = tiny.used(); + assert!( + crate::storage::maintenance::observe_bytes_for_test(&target, &tiny, &|| false).is_err() + ); + assert_eq!(tiny.used(), tiny_floor); + let (_, _, cancelled, _, _, _) = + crate::storage::maintenance::observe_bytes_for_test(&target, &memory, &|| true) + .unwrap(); + assert!(!cancelled); + fs::remove_dir_all(root).unwrap(); + fs::remove_dir_all(outside).unwrap(); + } + + #[test] + fn managed_native_verification_snapshot_drops_before_writer_release() { + let (root, options, _) = committed_artifact_fixture(true); + MANAGED_SNAPSHOT_RELEASE_OBSERVED.with(|observed| observed.set(false)); + POST_MANAGED_SNAPSHOT_DROP_HOOK + .with(|hook| hook.set(Some(assert_managed_snapshot_released_with_writer_owned))); + fs::write(root.join("src/lib.rs"), "pub fn no_hidden_janitor() {}\n").unwrap(); + let (_, response) = execute_materialization(&options).unwrap(); + assert!( + MANAGED_SNAPSHOT_RELEASE_OBSERVED.with(|observed| observed.get()), + "snapshot release was not observed before writer release" + ); + assert_published_graph_readable(&options, &response); + fs::remove_dir_all(root).unwrap(); + } } diff --git a/src/api/mod.rs b/src/api/mod.rs index 8271321c..88ee6152 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -14,6 +14,8 @@ pub mod facade; pub(crate) mod graph_read; pub mod lifecycle; mod materialization; +#[cfg(all(test, debug_assertions))] +pub(crate) use materialization::build_request as build_materialization_request_for_test; mod normalization; pub mod presenter; mod refresh; @@ -40,8 +42,11 @@ pub use lifecycle::{ }; pub(crate) use materialization::{ execute_candidate_materialization, execute_materialization, read_manifest, MaterializeOptions, + NativeVerifiedGeneration, +}; +pub(crate) use refresh::{ + RefreshCancellation, RefreshConfigOverrides, RefreshRuntime, RefreshServiceConfig, RefreshState, }; -pub(crate) use refresh::{RefreshConfigOverrides, RefreshServiceConfig}; #[cfg(test)] pub(crate) use refresh::{ diff --git a/src/api/normalization.rs b/src/api/normalization.rs index e8b0c901..498c4b0d 100644 --- a/src/api/normalization.rs +++ b/src/api/normalization.rs @@ -23,6 +23,16 @@ pub(crate) fn prepare_operation_request( let arguments = &invocation.arguments; let request = match operation_id { "health" => OperationRequest::Health(HealthRequest { + confirm_legacy_quiescence: match arguments.get("confirm_legacy_quiescence") { + None => false, + Some(serde_json::Value::Bool(value)) => *value, + Some(_) => { + return Err(ApiError::new( + "invalid_request", + "confirm_legacy_quiescence must be a boolean", + )) + } + }, repo: invocation.repo.clone(), refresh_status: None, output_format: invocation.output_format, @@ -477,6 +487,37 @@ mod tests { }; use serde_json::json; + #[test] + fn health_confirmation_defaults_false_and_accepts_only_explicit_booleans() { + for (arguments, expected) in [ + (json!({}), false), + (json!({"confirm_legacy_quiescence": false}), false), + (json!({"confirm_legacy_quiescence": true}), true), + ] { + let invocation = OperationInvocation { + repo: RepoSelector::default(), + arguments, + output_format: OutputFormat::Typed, + }; + let OperationRequest::Health(request) = + prepare_operation_request("health", &invocation).unwrap() + else { + panic!("health request expected"); + }; + assert_eq!(request.confirm_legacy_quiescence, expected); + } + for value in [json!(null), json!("true"), json!(1), json!([]), json!({})] { + let invocation = OperationInvocation { + repo: RepoSelector::default(), + arguments: json!({"confirm_legacy_quiescence": value}), + output_format: OutputFormat::Typed, + }; + let error = prepare_operation_request("health", &invocation).unwrap_err(); + assert_eq!(error.code, "invalid_request"); + assert!(error.message.contains("must be a boolean")); + } + } + #[test] fn normalize_request_applies_canonical_defaults() { let normalized = normalize_request(&OperationRequest::Search(SearchRequest { @@ -513,8 +554,6 @@ mod tests { exclude_patterns: Vec::new(), candidate_paths: Vec::new(), parallel: true, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, progress: false, diff --git a/src/api/presentation_support.rs b/src/api/presentation_support.rs index be3e51b6..49f9f9fe 100644 --- a/src/api/presentation_support.rs +++ b/src/api/presentation_support.rs @@ -167,9 +167,100 @@ pub(crate) fn serialize_health_block(payload: &serde_json::Value) -> String { lines.push(format!("error {}", block_value(error))); } append_refresh_health_lines(&mut lines, payload); + append_maintenance_health_lines(&mut lines, payload); format!("{}\n", lines.join("\n")) } +fn append_maintenance_health_lines(lines: &mut Vec, payload: &serde_json::Value) { + let Some(summary) = payload.get("maintenance").filter(|value| value.is_object()) else { + return; + }; + let fields = [ + "requested", + "running", + "pending", + "observed_at_unix_ms", + "observation_age_ms", + "inventory_status", + "measurement_complete", + "observation_complete", + ]; + lines.push(format!( + "maintenance {}", + fields + .into_iter() + .map(|key| format!("{key}={}", block_optional_payload_scalar(summary, key))) + .collect::>() + .join(" ") + )); + if let Some(bytes) = summary.get("bytes") { + for name in [ + "total", + "retained", + "potential_reclamation", + "deferred", + "unknown", + ] { + append_maintenance_bucket(lines, name, &bytes[name]); + } + for name in [ + "active_graph", + "active_cache", + "leased_generation", + "live_run", + "recovery_evidence", + "artifact_temporary", + "legacy_staging", + "retired_direct", + "coordination", + "unknown", + ] { + append_maintenance_bucket( + lines, + &format!("by_category.{name}"), + &bytes["by_category"][name], + ); + } + } + for key in ["reasons", "errors"] { + if let Some(values) = summary.get(key).and_then(serde_json::Value::as_array) { + for value in values.iter().filter_map(serde_json::Value::as_str) { + lines.push(format!("maintenance.{key} {}", block_value(value))); + } + } + } + if let Some(attempt) = summary + .get("last_attempt") + .filter(|value| value.is_object()) + { + let fields = [ + "started_at_unix_ms", + "finished_at_unix_ms", + "status", + "attempted_entries", + "deleted_entries", + "reclaimed_allocated_bytes", + "continuation_phase", + ]; + lines.push(format!( + "maintenance.last_attempt {}", + fields + .into_iter() + .map(|key| format!("{key}={}", block_optional_payload_scalar(attempt, key))) + .collect::>() + .join(" ") + )); + } +} + +fn append_maintenance_bucket(lines: &mut Vec, name: &str, bucket: &serde_json::Value) { + lines.push(format!( + "maintenance.bytes.{name} logical_bytes={} allocated_bytes={}", + block_optional_payload_scalar(bucket, "logical_bytes"), + block_optional_payload_scalar(bucket, "allocated_bytes") + )); +} + fn append_refresh_health_lines(lines: &mut Vec, payload: &serde_json::Value) { let Some(refresh) = payload .get("refresh_health") diff --git a/src/api/presenter.rs b/src/api/presenter.rs index cfdc8029..ea4886ec 100644 --- a/src/api/presenter.rs +++ b/src/api/presenter.rs @@ -128,3 +128,45 @@ mod tests { assert_eq!(response.payload["structured"], payload); } } + +#[cfg(test)] +mod maintenance_tests { + use super::*; + use crate::protocol::{MaintenanceAttempt, MaintenanceInventoryStatus, MaintenanceSummary}; + + #[test] + fn maintenance_block_preserves_nulls_and_complete_structured_summary() { + let summary = MaintenanceSummary { + inventory_status: Some(MaintenanceInventoryStatus::Busy), + reasons: vec!["writer_in_use".into()], + errors: vec!["fixture permission failure".into()], + running: true, + last_attempt: Some(MaintenanceAttempt { + started_at_unix_ms: 42, + finished_at_unix_ms: None, + status: None, + attempted_entries: None, + deleted_entries: None, + reclaimed_allocated_bytes: None, + continuation_phase: None, + }), + ..MaintenanceSummary::default() + }; + let payload = + serde_json::json!({"ok": true, "cleanup_pending": false, "maintenance": summary}); + let block = present_operation_response( + OperationResponse::from_payload("health", OutputFormat::Typed, payload.clone()), + OutputFormat::Block, + ); + assert_eq!(block.payload["structured"], payload); + let text = block.payload["text"].as_str().unwrap(); + assert!(text.contains("maintenance.bytes.total logical_bytes=null allocated_bytes=null")); + assert!(text.contains( + "maintenance.bytes.by_category.active_cache logical_bytes=null allocated_bytes=null" + )); + assert!(text.contains("status=null attempted_entries=null deleted_entries=null reclaimed_allocated_bytes=null")); + assert!(text.contains("writer_in_use")); + assert!(text.contains("fixture permission failure")); + assert!(text.contains("cleanup_pending=false")); + } +} diff --git a/src/api/refresh.rs b/src/api/refresh.rs index 55724129..88bea20b 100644 --- a/src/api/refresh.rs +++ b/src/api/refresh.rs @@ -50,8 +50,6 @@ const REFRESH_ELECTION_INTERVAL: Duration = Duration::from_secs(1); pub(crate) struct RefreshServiceConfig { pub(crate) policy: GraphRefreshPolicy, pub(crate) include_fts: bool, - pub(crate) worker_memory_mib: u64, - pub(crate) rust_memory_mib: u64, pub(crate) spill_chunk_mib: u64, pub(crate) max_parallelism: usize, pub(crate) backend: GraphRefreshBackend, @@ -67,8 +65,6 @@ pub(crate) struct RefreshServiceConfig { pub(crate) struct RefreshConfigOverrides { pub(crate) policy: bool, pub(crate) include_fts: bool, - pub(crate) worker_memory_mib: bool, - pub(crate) rust_memory_mib: bool, pub(crate) spill_chunk_mib: bool, pub(crate) max_parallelism: bool, pub(crate) backend: bool, @@ -80,8 +76,6 @@ impl Default for RefreshServiceConfig { Self { policy: GraphRefreshPolicy::Leader, include_fts: true, - worker_memory_mib: crate::api::context::DEFAULT_WORKER_MEMORY_MIB, - rust_memory_mib: crate::api::context::DEFAULT_RUST_MEMORY_MIB, spill_chunk_mib: crate::api::context::DEFAULT_SPILL_CHUNK_MIB, max_parallelism: crate::api::context::DEFAULT_MAX_PARALLELISM, backend: GraphRefreshBackend::Auto, @@ -956,6 +950,7 @@ pub(crate) fn run_refresh_watch( let _refresh_lease = if config.once { None } else { + initialize_refresh_destination(&runtime)?; try_open_locked(refresh_lock_path(&runtime), LockMode::Exclusive) .map_err(|error| format!("failed to acquire refresh ownership: {error}"))? .ok_or_else(|| { @@ -1439,6 +1434,24 @@ impl RefreshExecutionPlan { } } +struct RefreshMaintenanceCompletion<'a>(&'a RefreshState); +impl Drop for RefreshMaintenanceCompletion<'_> { + fn drop(&mut self) { + let maintenance = self + .0 + .maintenance + .lock() + .unwrap_or_else(|error| error.into_inner()) + .clone(); + if let Some(maintenance) = maintenance { + maintenance.request( + crate::storage::maintenance_scheduler::Trigger::Completion, + false, + ); + } + } +} + #[derive(Clone, Copy, Debug)] struct RefreshServiceContext<'a> { state: &'a Arc, @@ -1462,6 +1475,9 @@ impl<'a> RefreshServiceContext<'a> { } fn refresh_batch(&self, backend: &'a str, batch: &WatchChangeBatch) -> Result { + // First local, last dropped: materialization workers, observer and all + // producer guards unwind before scheduling their completion opportunity. + let _maintenance_completion = RefreshMaintenanceCompletion(self.state); let mut observer = StateRefreshObserver::new( self.state, backend, @@ -1478,7 +1494,6 @@ impl<'a> RefreshServiceContext<'a> { |candidate_paths| self.execution.execute_isolated(candidate_paths, self.state), )?; if refresh_task_should_stop(self.state) { - self.state.mark_stopped(); return Ok(false); } if !result { @@ -1635,7 +1650,7 @@ pub(crate) struct RefreshStatus { /// Whether the refresh task is still running and able to make progress. /// /// This is deliberately separate from `enabled`: a configured service can - /// remain enabled after its detached task has stopped. + /// remain enabled after its owned task has stopped. pub(crate) task_alive: bool, /// Supervised task state. The values are part of the structured status /// contract: starting, running, retrying, blocked, stopped, standby, and @@ -1675,8 +1690,6 @@ pub(crate) struct RefreshStatus { pub(crate) overflow_count: usize, pub(crate) deduplicated_refresh_count: usize, pub(crate) last_noop_reason: Option, - pub(crate) worker_memory_mib: u64, - pub(crate) rust_memory_mib: u64, pub(crate) spill_chunk_mib: u64, pub(crate) max_parallelism: usize, pub(crate) phase_high_water_marks: BTreeMap, @@ -1720,8 +1733,6 @@ impl Default for RefreshStatus { overflow_count: 0, deduplicated_refresh_count: 0, last_noop_reason: None, - worker_memory_mib: crate::api::context::DEFAULT_WORKER_MEMORY_MIB, - rust_memory_mib: crate::api::context::DEFAULT_RUST_MEMORY_MIB, spill_chunk_mib: crate::api::context::DEFAULT_SPILL_CHUNK_MIB, max_parallelism: crate::api::context::DEFAULT_MAX_PARALLELISM, phase_high_water_marks: BTreeMap::new(), @@ -1735,13 +1746,24 @@ impl Default for RefreshStatus { #[derive(Debug)] pub(crate) struct RefreshState { status: Mutex, + cancellation: RefreshCancellation, + maintenance: Mutex< + Option< + crate::storage::maintenance::MaintenanceClient< + Arc, + >, + >, + >, } impl RefreshState { + #[cfg(test)] pub(crate) fn with_config(config: RefreshServiceConfig) -> Self { + Self::with_cancellation(config, RefreshCancellation::default()) + } + + fn with_cancellation(config: RefreshServiceConfig, cancellation: RefreshCancellation) -> Self { let status = RefreshStatus { - worker_memory_mib: config.worker_memory_mib, - rust_memory_mib: config.rust_memory_mib, spill_chunk_mib: config.spill_chunk_mib, max_parallelism: config.max_parallelism, reconcile_interval_ms: config @@ -1752,6 +1774,8 @@ impl RefreshState { }; Self { status: Mutex::new(status), + cancellation, + maintenance: Mutex::new(None), } } @@ -1790,8 +1814,6 @@ impl RefreshState { overflow_count: 0, deduplicated_refresh_count: 0, last_noop_reason: None, - worker_memory_mib: 0, - rust_memory_mib: 0, spill_chunk_mib: 0, max_parallelism: 0, phase_high_water_marks: BTreeMap::new(), @@ -1834,9 +1856,7 @@ impl RefreshState { "overflow_count": status.overflow_count, "deduplicated_refresh_count": status.deduplicated_refresh_count, "last_noop_reason": status.last_noop_reason, - "memory_limits": { - "worker_memory_mib": status.worker_memory_mib, - "rust_memory_mib": status.rust_memory_mib, + "execution_settings": { "spill_chunk_mib": status.spill_chunk_mib, "max_parallelism": status.max_parallelism, }, @@ -1881,10 +1901,8 @@ impl RefreshState { } } - pub(crate) fn set_materialization_limits(&self, config: RefreshServiceConfig) { + pub(crate) fn set_execution_settings(&self, config: RefreshServiceConfig) { if let Ok(mut status) = self.status.lock() { - status.worker_memory_mib = config.worker_memory_mib; - status.rust_memory_mib = config.rust_memory_mib; status.spill_chunk_mib = config.spill_chunk_mib; status.max_parallelism = config.max_parallelism; } @@ -1908,7 +1926,7 @@ impl RefreshState { status.backend = backend.to_string(); status.enabled = true; if backend == "failed" { - // The detached service calls this form only after its task + // The owned service calls this form only after its task // has actually terminated. Fallback errors use another // backend (currently `poll`) and must keep reporting the // still-live owner while the fallback loop takes over. @@ -2183,46 +2201,203 @@ impl RefreshState { } } -pub(crate) fn start_refresh_service( - selector: RepoSelector, - config: RefreshServiceConfig, -) -> Arc { - let state = Arc::new(RefreshState::with_config(config)); +/// Explicit control is independent of retained Core/status Arc clones. +#[derive(Clone, Debug, Default)] +pub(crate) struct RefreshCancellation { + stop: Arc, +} +impl RefreshCancellation { + pub(crate) fn request_stop(&self) { + self.stop.store(true, Ordering::Release); + } + fn is_stopping(&self) -> bool { + self.stop.load(Ordering::Acquire) + } +} + +/// The serving thread owns this value; the refresh worker captures only state. +#[derive(Debug)] +pub(crate) struct RefreshRuntime { + state: Arc, + thread: Option>, +} +impl RefreshRuntime { + pub(crate) fn state(&self) -> Arc { + Arc::clone(&self.state) + } + pub(crate) fn request_stop(&self) { + self.state.cancellation.request_stop(); + } + pub(crate) fn stop_and_join(&mut self) { + self.request_stop(); + if let Some(thread) = self.thread.take() { + let joined = thread.join(); + if joined.is_err() && self.state.snapshot().backend != "failed" { + self.state.set_error( + "failed", + "refresh supervisor thread panicked during teardown".into(), + ); + } + // No status/owner mutex is held across join or panic-payload disposal. + drop(joined); + } + } +} +impl Drop for RefreshRuntime { + fn drop(&mut self) { + self.stop_and_join(); + } +} + +enum RefreshServiceExit { + StoppedAfterDrain, + LegacyDisabled(String), +} +enum RefreshTerminal { + Stopped, + Disabled(String), + Failed(String), +} + +// First local in the worker, last to dispose. A panic during payload destruction +// cannot leave task_alive true or replace a real failure with ordinary stopped. +struct RefreshExitFinalizer<'a> { + state: &'a RefreshState, + terminal: Option, +} +impl Drop for RefreshExitFinalizer<'_> { + fn drop(&mut self) { + let terminal = self.terminal.take(); + if thread::panicking() { + drop(terminal); + self.state.set_error( + "failed", + "refresh supervisor panicked during teardown".into(), + ); + return; + } + match terminal { + Some(RefreshTerminal::Stopped) => self.state.mark_stopped(), + Some(RefreshTerminal::Disabled(reason)) => self.state.disable("disabled", reason), + Some(RefreshTerminal::Failed(error)) => self.state.set_error("failed", error), + None => self.state.set_error( + "failed", + "refresh supervisor exited without a disposition".into(), + ), + } + } +} + +#[cfg(test)] +thread_local! { + static INJECT_REFRESH_SPAWN_FAILURE: std::cell::Cell = const { std::cell::Cell::new(false) }; +} + +fn spawn_refresh_task(state: Arc, run: F) -> RefreshRuntime +where + F: FnOnce(&Arc) -> Result + Send + 'static, +{ let thread_state = Arc::clone(&state); - thread::spawn(move || { - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - run_refresh_service(selector, config, &thread_state) - })); + let body = move || { + let mut exit = RefreshExitFinalizer { + state: &thread_state, + terminal: None, + }; + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| run(&thread_state))); match result { + Ok(Ok(RefreshServiceExit::StoppedAfterDrain)) => { + exit.terminal = Some(RefreshTerminal::Stopped) + } + Ok(Ok(RefreshServiceExit::LegacyDisabled(reason))) => { + exit.terminal = Some(RefreshTerminal::Disabled(reason)); + } Ok(Err(error)) => { - thread_state.set_error("failed", error.clone()); eprintln!( "{}", - json!({"event": "repository.refresh_error", "message": error}) + json!({"event":"repository.refresh_error", "message":&error}) ); + exit.terminal = Some(RefreshTerminal::Failed(error)); + } + Err(payload) => { + exit.terminal = Some(RefreshTerminal::Failed( + "refresh supervisor panicked; retry requires service restart".into(), + )); + drop(payload); } - Ok(Ok(())) if refresh_task_should_stop(&thread_state) => thread_state.mark_stopped(), - Err(_) => thread_state.set_error( + } + }; + let spawned = spawn_refresh_thread(body); + match spawned { + Ok(thread) => RefreshRuntime { + state, + thread: Some(thread), + }, + Err(error) => { + state.set_error( "failed", - "refresh supervisor panicked; retry requires service restart".to_string(), - ), - _ => {} + format!("failed to start refresh supervisor: {error}"), + ); + RefreshRuntime { + state, + thread: None, + } } - }); - state + } +} + +fn spawn_refresh_thread( + body: F, +) -> std::io::Result> { + #[cfg(test)] + if INJECT_REFRESH_SPAWN_FAILURE.with(|flag| flag.replace(false)) { + drop(body); + return Err(std::io::Error::other("fixture refresh spawn failure")); + } + thread::Builder::new() + .name("codebase-graph-refresh".into()) + .spawn(body) +} + +#[cfg(test)] +pub(crate) fn start_refresh_service( + selector: RepoSelector, + config: RefreshServiceConfig, + cancellation: RefreshCancellation, +) -> RefreshRuntime { + start_refresh_service_with_maintenance(selector, config, cancellation, None) +} + +pub(crate) fn start_refresh_service_with_maintenance( + selector: RepoSelector, + config: RefreshServiceConfig, + cancellation: RefreshCancellation, + maintenance: Option< + crate::storage::maintenance::MaintenanceClient< + Arc, + >, + >, +) -> RefreshRuntime { + let state = Arc::new(RefreshState::with_cancellation(config, cancellation)); + *state + .maintenance + .lock() + .unwrap_or_else(|error| error.into_inner()) = maintenance; + spawn_refresh_task(state, move |state| { + run_refresh_service(selector, config, state) + }) } fn run_refresh_service( selector: RepoSelector, config: RefreshServiceConfig, state: &Arc, -) -> Result<(), String> { +) -> Result { let mut identity = None; let mut retry_delay = Duration::from_millis(250); let mut blocked_delay = Duration::from_secs(5); loop { if refresh_task_should_stop(state) { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } if identity.is_none() { match RepositoryIdentity::capture(&selector) { @@ -2232,7 +2407,7 @@ fn run_refresh_service( let delay = blocked_delay; state.mark_blocked_until(unix_ms().saturating_add(delay.as_millis())); if supervisor_wait(state, delay)? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } blocked_delay = blocked_delay.saturating_mul(2).min(Duration::from_secs(60)); continue; @@ -2244,7 +2419,7 @@ fn run_refresh_service( let delay = blocked_delay; state.mark_blocked_until(unix_ms().saturating_add(delay.as_millis())); if supervisor_wait(state, delay)? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } blocked_delay = blocked_delay.saturating_mul(2).min(Duration::from_secs(60)); continue; @@ -2256,7 +2431,7 @@ fn run_refresh_service( let delay = blocked_delay; state.mark_blocked_until(unix_ms().saturating_add(delay.as_millis())); if supervisor_wait(state, delay)? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } blocked_delay = blocked_delay.saturating_mul(2).min(Duration::from_secs(60)); continue; @@ -2270,40 +2445,42 @@ fn run_refresh_service( let delay = blocked_delay; state.mark_blocked_until(unix_ms().saturating_add(delay.as_millis())); if supervisor_wait(state, delay)? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } blocked_delay = blocked_delay.saturating_mul(2).min(Duration::from_secs(60)); continue; } }; state.set_reconcile_interval(effective_config.reconcile_interval); - state.set_materialization_limits(effective_config); + state.set_execution_settings(effective_config); if effective_config.policy == GraphRefreshPolicy::Off { let delay = Duration::from_secs(5); state.mark_policy_disabled_until(unix_ms().saturating_add(delay.as_millis())); if supervisor_wait(state, delay)? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } continue; } if let Err(error) = runtime.require_graph_write() { - state.disable("disabled", error); - return Ok(()); + return Ok(RefreshServiceExit::LegacyDisabled(error)); } let lock_path = refresh_lock_path(&runtime); - let lease_result = try_open_locked(&lock_path, LockMode::Exclusive).map_err(|error| { - format!( - "failed to acquire refresh ownership {}: {error}", - lock_path.display() - ) - }); + let lease_result = initialize_refresh_destination(&runtime) + .map_err(crate::error::NativeError::InvalidInput) + .and_then(|()| try_open_locked(&lock_path, LockMode::Exclusive)) + .map_err(|error| { + format!( + "failed to acquire refresh ownership {}: {error}", + lock_path.display() + ) + }); match lease_result { Err(error) => { state.set_error("lock", error); let delay = blocked_delay; state.mark_blocked_until(unix_ms().saturating_add(delay.as_millis())); if supervisor_wait(state, delay)? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } blocked_delay = blocked_delay.saturating_mul(2).min(Duration::from_secs(60)); } @@ -2315,7 +2492,7 @@ fn run_refresh_service( retry_delay = Duration::from_millis(250); blocked_delay = Duration::from_secs(5); if refresh_task_should_stop(state) { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } } Err(error) => { @@ -2325,7 +2502,7 @@ fn run_refresh_service( let delay = retry_delay; state.mark_retrying(unix_ms().saturating_add(delay.as_millis())); if supervisor_wait(state, delay)? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } retry_delay = retry_delay.saturating_mul(2).min(Duration::from_secs(5)); } else { @@ -2333,7 +2510,7 @@ fn run_refresh_service( let delay = blocked_delay; state.mark_blocked_until(unix_ms().saturating_add(delay.as_millis())); if supervisor_wait(state, delay)? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } blocked_delay = blocked_delay.saturating_mul(2).min(Duration::from_secs(60)); @@ -2344,7 +2521,7 @@ fn run_refresh_service( Ok(None) => { state.mark_standby(); if supervisor_wait(state, refresh_election_delay())? { - return Ok(()); + return Ok(RefreshServiceExit::StoppedAfterDrain); } } } @@ -2362,6 +2539,24 @@ fn supervisor_wait(state: &Arc, delay: Duration) -> Result Result<(), String> { + if let Some(root) = &runtime.storage_root { + crate::storage::maintenance_ownership::initialize_fresh_managed_domain( + &ManagedLayout::new(root), + ) + .map_err(|error| format!("failed to initialize fresh refresh destination: {error}"))?; + } else { + crate::storage::maintenance_ownership::initialize_fresh_direct_domain(&DirectLayout::new( + &runtime.db_path, + &runtime.manifest_path, + )) + .map_err(|error| format!("failed to initialize fresh refresh destination: {error}"))?; + } + Ok(()) +} + fn refresh_lock_path(runtime: &crate::api::context::RepoRuntime) -> PathBuf { match runtime.storage_root.as_ref() { Some(storage_root) => ManagedLayout::new(storage_root).refresh_lock_path(), @@ -2378,12 +2573,10 @@ fn default_reconcile_interval() -> Duration { Duration::from_millis(crate::api::context::DEFAULT_RECONCILE_INTERVAL_MS) } -/// The coordinator owns the externally visible `Arc`, while the -/// service thread owns one temporary strong reference. Once the coordinator -/// owner is dropped only that thread reference remains, so bounded watcher -/// waits can terminate without retaining the task forever. +/// Status observers never determine service lifetime. Cancellation also works +/// before startup and while Core/API clones retain the state for ordinary reads. fn refresh_task_should_stop(state: &Arc) -> bool { - Arc::strong_count(state) == 1 + state.cancellation.is_stopping() } fn run_refresh_leader( @@ -2395,7 +2588,7 @@ fn run_refresh_leader( ) -> Result<(), String> { let config = resolve_service_config(&runtime, config)?; state.set_reconcile_interval(config.reconcile_interval); - state.set_materialization_limits(config); + state.set_execution_settings(config); let materialize_options = MaterializeOptions { source_root: Some(runtime.repo_root.clone()), config: runtime.config_path.clone(), @@ -2405,8 +2598,6 @@ fn run_refresh_leader( mode: "changed".to_string(), include_fts: config.include_fts, use_git: false, - worker_memory_mib: Some(config.worker_memory_mib), - rust_memory_mib: Some(config.rust_memory_mib), spill_chunk_mib: Some(config.spill_chunk_mib), max_parallelism: Some(config.max_parallelism), intent: MaterializationIntent::Refresh, @@ -2531,12 +2722,6 @@ fn resolve_service_config( if !overrides.include_fts { configured.include_fts = install.materialization.include_fts; } - if !overrides.worker_memory_mib { - configured.worker_memory_mib = install.materialization.worker_memory_mib; - } - if !overrides.rust_memory_mib { - configured.rust_memory_mib = install.materialization.rust_memory_mib; - } if !overrides.spill_chunk_mib { configured.spill_chunk_mib = install.materialization.spill_chunk_mib; } @@ -2732,8 +2917,6 @@ mod tests { let state = RefreshState::with_config(RefreshServiceConfig { policy: GraphRefreshPolicy::Leader, include_fts: false, - worker_memory_mib: 640, - rust_memory_mib: 320, spill_chunk_mib: 16, max_parallelism: 1, backend: GraphRefreshBackend::Auto, @@ -2743,10 +2926,8 @@ mod tests { let status = state.as_json(); assert_eq!(status["worker_pid"], serde_json::Value::Null); - assert_eq!(status["memory_limits"]["worker_memory_mib"], 640); - assert_eq!(status["memory_limits"]["rust_memory_mib"], 320); - assert_eq!(status["memory_limits"]["spill_chunk_mib"], 16); - assert_eq!(status["memory_limits"]["max_parallelism"], 1); + assert_eq!(status["execution_settings"]["spill_chunk_mib"], 16); + assert_eq!(status["execution_settings"]["max_parallelism"], 1); assert_eq!(status["phase_high_water_marks"], serde_json::json!({})); assert_eq!(status["spill_bytes"], 0); assert_eq!(status["task_alive"], true); @@ -3350,7 +3531,7 @@ mod tests { ) .unwrap(); - let state = start_refresh_service( + let mut service = start_refresh_service( RepoSelector { repo_root: Some(root.clone()), config_path: None, @@ -3358,7 +3539,9 @@ mod tests { manifest_path: None, }, RefreshServiceConfig::default(), + RefreshCancellation::default(), ); + let state = service.state(); let mut snapshot = state.snapshot(); for _ in 0..50 { @@ -3373,6 +3556,7 @@ mod tests { assert_eq!(snapshot.backend, "disabled"); assert!(snapshot.last_error.as_deref().is_some_and(|error| error .contains("legacy installed graph storage requires reinstall before writes"))); + service.stop_and_join(); let _ = fs::remove_dir_all(root); } @@ -3406,7 +3590,7 @@ mod tests { db_path: None, manifest_path: None, }; - let state = start_refresh_service( + let mut service = start_refresh_service( selector.clone(), RefreshServiceConfig { backend: GraphRefreshBackend::Native, @@ -3418,7 +3602,9 @@ mod tests { }, ..RefreshServiceConfig::default() }, + RefreshCancellation::default(), ); + let state = service.state(); let startup_deadline = Instant::now() + Duration::from_secs(20); let snapshot = loop { @@ -3448,6 +3634,7 @@ mod tests { let runtime = resolve_refresh_runtime(&selector).unwrap(); let lock_path = refresh_lock_path(&runtime); drop(runtime); + service.stop_and_join(); drop(state); let release_deadline = Instant::now() + Duration::from_secs(5); @@ -3464,6 +3651,28 @@ mod tests { Err(error) => panic!("failed to inspect refresh lease: {error}"), } }; + // A refresh spawn failure is optional: the committed native graph + // remains readable even while API/Core/status clones retain its state. + INJECT_REFRESH_SPAWN_FAILURE.with(|flag| flag.set(true)); + let (api, mut failed_runtime) = crate::api::CodebaseGraphApi::for_coordinator_owner( + selector.clone(), + Some(RefreshServiceConfig::default()), + RefreshCancellation::default(), + ); + let failed_state = failed_runtime.as_ref().unwrap().state(); + assert_eq!(failed_state.snapshot().backend, "failed"); + let response = api + .execute_operation(&crate::api::OperationRequest::Health( + crate::api::HealthRequest { + repo: selector.clone(), + refresh_status: None, + output_format: crate::api::OutputFormat::Typed, + confirm_legacy_quiescence: false, + }, + )) + .unwrap(); + assert_eq!(response.payload["graph_readable"], true); + failed_runtime.as_mut().unwrap().stop_and_join(); let cleanup = fs::remove_dir_all(&root); assert!(released, "refresh service did not release its lease"); assert!(cleanup.is_ok(), "refresh test cleanup failed: {cleanup:?}"); @@ -3558,3 +3767,194 @@ mod tests { let _ = fs::remove_dir_all(root); } } + +#[cfg(test)] +mod owned_refresh_tests { + use super::*; + use crate::api::core::ApiCore; + use std::sync::mpsc; + + struct ReleaseOnDrop(Option>); + impl ReleaseOnDrop { + fn release(&mut self) { + if let Some(sender) = self.0.take() { + let _ = sender.send(()); + } + } + } + impl Drop for ReleaseOnDrop { + fn drop(&mut self) { + self.release(); + } + } + + #[test] + fn refresh_owned_join_ignores_retained_core_clones_and_has_no_cycle() { + let state = Arc::new(RefreshState::with_config(RefreshServiceConfig::default())); + let weak = Arc::downgrade(&state); + let core = ApiCore::for_coordinator(Some(state.clone())); + let clones = [core.clone(), core.clone()]; + let (started, ready) = mpsc::channel(); + let mut runtime = spawn_refresh_task(state.clone(), move |state| { + state.set_backend("poll"); + started.send(()).unwrap(); + while !refresh_task_should_stop(state) { + thread::yield_now(); + } + Ok(RefreshServiceExit::StoppedAfterDrain) + }); + ready.recv_timeout(Duration::from_secs(5)).unwrap(); + runtime.stop_and_join(); + assert!(!state.snapshot().task_alive); + assert_eq!(state.snapshot().state, "stopped"); + assert_eq!(clones[0].operations().len(), core.operations().len()); + drop(runtime); + drop(state); + drop(clones); + drop(core); + assert!( + weak.upgrade().is_none(), + "runtime/state/Core ownership cycle" + ); + } + + struct SlowResource { + entered: mpsc::Sender<()>, + release: mpsc::Receiver<()>, + } + impl Drop for SlowResource { + fn drop(&mut self) { + let _ = self.entered.send(()); + let _ = self.release.recv(); + } + } + #[test] + fn refresh_owned_join_waits_for_resource_drop_without_early_stopped_or_status_lock() { + let state = Arc::new(RefreshState::with_config(RefreshServiceConfig::default())); + let (started, ready) = mpsc::channel(); + let (entered, disposing) = mpsc::channel(); + let (release, released) = mpsc::channel(); + let mut runtime = spawn_refresh_task(state.clone(), move |state| { + let _resource = SlowResource { + entered, + release: released, + }; + state.set_backend("poll"); + started.send(()).unwrap(); + while !refresh_task_should_stop(state) { + thread::yield_now(); + } + Ok(RefreshServiceExit::StoppedAfterDrain) + }); + let mut release = ReleaseOnDrop(Some(release)); + ready.recv_timeout(Duration::from_secs(5)).unwrap(); + runtime.request_stop(); + disposing.recv_timeout(Duration::from_secs(5)).unwrap(); + let (done, drained) = mpsc::channel(); + let joiner = thread::spawn(move || { + runtime.stop_and_join(); + done.send(()).unwrap(); + }); + assert!(state.status.try_lock().is_ok()); + assert!(state.snapshot().task_alive); + assert_ne!(state.snapshot().state, "stopped"); + assert!(drained.try_recv().is_err()); + release.release(); + drained.recv_timeout(Duration::from_secs(5)).unwrap(); + joiner.join().unwrap(); + assert_eq!(state.snapshot().state, "stopped"); + assert!(!state.snapshot().task_alive); + } + + #[test] + fn refresh_owned_terminal_disposition_survives_stop_disable_failure_and_panic_races() { + for outcome in 0..3 { + let state = Arc::new(RefreshState::with_config(RefreshServiceConfig::default())); + let (entered, ready) = mpsc::channel(); + let (release, released) = mpsc::channel(); + let mut runtime = spawn_refresh_task(state.clone(), move |_| { + entered.send(()).unwrap(); + let _ = released.recv(); + match outcome { + 0 => Ok(RefreshServiceExit::LegacyDisabled("legacy witness".into())), + 1 => Err("primary failure witness".into()), + _ => panic!("primary panic witness"), + } + }); + let mut release = ReleaseOnDrop(Some(release)); + ready.recv_timeout(Duration::from_secs(5)).unwrap(); + runtime.request_stop(); + release.release(); + runtime.stop_and_join(); + let status = state.snapshot(); + assert!(!status.task_alive); + if outcome == 0 { + assert_eq!(status.state, "disabled"); + assert_eq!(status.backend, "disabled"); + assert!(!status.enabled); + assert_eq!(status.last_error.as_deref(), Some("legacy witness")); + } else { + assert_eq!(status.backend, "failed"); + assert!(status + .last_error + .as_deref() + .unwrap() + .contains(if outcome == 1 { + "primary failure witness" + } else { + "panicked" + })); + } + } + } + + struct PanicPayload; + impl Drop for PanicPayload { + fn drop(&mut self) { + panic!("refresh payload destructor"); + } + } + #[test] + fn refresh_owned_join_publishes_failure_if_panic_payload_disposal_panics() { + let state = Arc::new(RefreshState::with_config(RefreshServiceConfig::default())); + let mut runtime = + spawn_refresh_task(state.clone(), |_| std::panic::panic_any(PanicPayload)); + runtime.stop_and_join(); + assert_eq!(state.snapshot().backend, "failed"); + assert!(!state.snapshot().task_alive); + assert!(state + .snapshot() + .last_error + .as_deref() + .unwrap() + .contains("panicked")); + } + + #[test] + fn refresh_owned_precancelled_startup_skips_resolution_and_resources() { + let root = std::env::temp_dir().join(format!( + "refresh-precancelled-{}-{}", + std::process::id(), + unix_ms() + )); + fs::create_dir(&root).unwrap(); + let cancellation = RefreshCancellation::default(); + cancellation.request_stop(); + let mut runtime = start_refresh_service( + RepoSelector { + repo_root: Some(root.clone()), + config_path: None, + db_path: None, + manifest_path: None, + }, + RefreshServiceConfig::default(), + cancellation, + ); + let state = runtime.state(); + runtime.stop_and_join(); + assert_eq!(state.snapshot().state, "stopped"); + assert_eq!(state.snapshot().effective_root, None); + assert_eq!(fs::read_dir(&root).unwrap().count(), 0); + fs::remove_dir(root).unwrap(); + } +} diff --git a/src/artifact_store.rs b/src/artifact_store.rs index d2128521..844b90f8 100644 --- a/src/artifact_store.rs +++ b/src/artifact_store.rs @@ -1,16 +1,16 @@ -use crate::error::{MemoryBudgetExceeded, NativeError}; +use crate::error::NativeError; use crate::partition_builder::GraphPartition; use crate::protocol::{ LanguageProfile, NativeSyntaxMaterializationRequest, OntologySchema, SourceSnapshot, PROFILE_COMPATIBILITY_VERSION, }; -use crate::storage::atomic::sync_dir; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::fmt::Write as _; use std::fs::{self, File}; use std::io::{BufReader, BufWriter, Read, Write}; use std::path::PathBuf; +#[cfg(test)] use std::time::{SystemTime, UNIX_EPOCH}; pub(crate) const ARTIFACT_FORMAT_VERSION: u64 = 2; @@ -18,7 +18,6 @@ const ENVELOPE_FILE_NAME: &str = "partition.json"; const ARTIFACT_KEY_BYTES: usize = 32; const ARTIFACT_KEY_HEX_LEN: usize = ARTIFACT_KEY_BYTES * 2; const ARTIFACT_IO_BUFFER_BYTES: usize = 64 * 1024; -const ARTIFACT_DECODE_EXPANSION_FACTOR: u64 = 2; #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) enum ArtifactWriteOutcome { @@ -49,6 +48,9 @@ pub(crate) struct ArtifactExpectations<'a> { #[derive(Debug, Clone)] pub(crate) struct ArtifactStore { root: PathBuf, + // Parser workers share a store. Serialize their short publications so + // ordinary parallel parsing does not discard optional creation receipts. + publication: std::sync::Arc>, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -106,7 +108,10 @@ struct ArtifactKeyMaterial<'a> { impl ArtifactStore { pub(crate) fn new(root: impl Into) -> Self { - Self { root: root.into() } + Self { + root: root.into(), + publication: std::sync::Arc::new(std::sync::Mutex::new(())), + } } pub(crate) fn key_for_request( @@ -134,7 +139,14 @@ impl ArtifactStore { partition: &GraphPartition, ) -> Result { validate_artifact_key(artifact_key)?; - ensure_artifact_root(&self.root)?; + let _publication = self + .publication + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + // Hold actual root/use and reference activity through every producer + // write, publication, equality check and exact-created-leaf cleanup. + let mutation = + crate::storage::maintenance_ownership::ReferenceMutation::begin_producing(&self.root)?; partition .validate_raw_rows() .map_err(NativeError::InvalidInput)?; @@ -144,130 +156,272 @@ impl ArtifactStore { partition.entry.path ))); } - let artifact_dir = self.artifact_dir(artifact_key)?; - if let Some(parent) = artifact_dir.parent() { - fs::create_dir_all(parent)?; - ensure_artifact_root(&self.root)?; - ensure_directory_without_symlinks(parent)?; - sync_dir(parent)?; + use crate::storage::locks::{file_identity, path_identity, RootedDirectory}; + use crate::storage::maintenance_controls::try_begin_root_publication; + use crate::storage::maintenance_origin::{OriginWriter, ORIGIN_NAME}; + let root = RootedDirectory::open(&self.root, path_identity(&self.root)?)?; + let mut capability = mutation.artifact_root_producer_capability().unwrap_or(None); + // Tracking is optional. Primary no-replace/equality semantics and actual + // anchored transport are identical when controls are missing or busy. + let publication = match capability.as_mut() { + Some(cap) => try_begin_root_publication(cap).unwrap_or(None), + None => None, + }; + let prefix_name = &artifact_key.as_bytes()[..2]; + let prefix = match root.create_directory(prefix_name) { + Ok(dir) => { + root.sync_namespace()?; + dir + } + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => { + root.open_directory(prefix_name)? + } + Err(error) => return Err(error.into()), + }; + let key_bytes = decode_key(artifact_key); + let partition_digest = partition_payload_sha256(partition)?; + let mut nonce = [0; 32]; + getrandom::fill(&mut nonce) + .map_err(|_| NativeError::InvalidInput("artifact_creation_nonce_unavailable".into()))?; + let temp_name = format!(".tmp-{artifact_key}-{}", hex_lower(&nonce)); + let temp = root.create_directory(temp_name.as_bytes())?; + let mut payload = match temp.create_new(ENVELOPE_FILE_NAME.as_bytes()) { + Ok(file) => file, + Err(error) => { + let _ = root.remove_empty_directory(temp_name.as_bytes(), temp.identity()); + return Err(error.into()); + } + }; + let payload_identity = file_identity(&payload)?; + #[cfg(test)] + producer_hook("created", &temp); + let mut origin = publication.as_ref().and_then(|lease| { + OriginWriter::create(lease, &temp, &payload, key_bytes, true, true).ok() + }); + let receipt_identity = origin.as_ref().map(OriginWriter::receipt_identity); + if let Some(origin) = origin.as_mut() { + let _ = origin.writing(); } - - let partition_payload_sha256 = partition_payload_sha256(partition)?; - let temp_dir = self.temp_dir_path(artifact_key); - fs::create_dir(&temp_dir)?; - let payload_path = temp_dir.join(ENVELOPE_FILE_NAME); - if let Err(error) = write_envelope_and_sync( - &payload_path, - artifact_key, - &partition_payload_sha256, - partition, - ) { - let _ = remove_tree_confined(&self.root, &temp_dir); + let prepared = (|| -> Result<(u64, [u8; 32]), NativeError> { + let bytes = write_envelope_file_and_sync( + &mut payload, + artifact_key, + &partition_digest, + partition, + )?; + temp.sync_namespace()?; + root.sync_namespace()?; + if let Some(origin) = origin.as_mut() { + let _ = origin.complete(&temp, &payload, bytes.0, bytes.1); + } + Ok(bytes) + })(); + #[cfg(test)] + producer_hook("prepared", &temp); + let accepted = match prepared { + Ok(accepted) => accepted, + Err(error) => { + cleanup_created( + &root, + &temp, + temp_name.as_bytes(), + payload_identity, + receipt_identity, + ); + return Err(error); + } + }; + let current = (|| -> Result<(), NativeError> { + if path_identity(&self.root)? != root.identity() + || root.open_directory(prefix_name)?.identity() != prefix.identity() + { + return Err(NativeError::InvalidInput( + "artifact_publication_parent_changed".into(), + )); + } + verify_created_shape(&temp, payload_identity, receipt_identity) + })(); + if let Err(error) = current { + cleanup_created( + &root, + &temp, + temp_name.as_bytes(), + payload_identity, + receipt_identity, + ); return Err(error); } - sync_dir(&temp_dir)?; - sync_dir(temp_dir.parent().expect("temp dir parent should exist"))?; - - match fs::rename(&temp_dir, &artifact_dir) { + // Windows cannot rename a directory containing open child files. Keep + // real root/publication ownership and the pinned directory, while the + // consumed creator supplies the only possible receipt continuation. + let origin_publication = origin.take().and_then(OriginWriter::into_publication); + drop(payload); + match root.rename_directory_no_replace( + temp_name.as_bytes(), + &prefix, + artifact_key.as_bytes(), + temp.identity(), + ) { Ok(()) => { - sync_dir( - artifact_dir - .parent() - .expect("artifact directory parent should exist"), - )?; + #[cfg(test)] + producer_hook("published", &temp); + if path_identity(&self.root)? != root.identity() + || root.open_directory(prefix_name)?.identity() != prefix.identity() + || prefix.open_directory(artifact_key.as_bytes())?.identity() != temp.identity() + { + return Err(NativeError::InvalidInput( + "artifact_published_incarnation_changed".into(), + )); + } + verify_created_shape(&temp, payload_identity, receipt_identity)?; + // A real publication durability error remains a primary error; + // optional origin completion never changes an accepted result. + prefix.sync_namespace()?; + root.sync_namespace()?; + if let Some(lease) = publication.as_ref() { + if let Some(origin) = origin_publication { + let _ = origin.published(lease, &temp); + } + } Ok(ArtifactWriteOutcome::Written) } - Err(_error) if artifact_dir.exists() => { - if existing_artifact_matches(&artifact_dir, &payload_path)? { - remove_tree_confined(&self.root, &temp_dir)?; - sync_dir( - artifact_dir - .parent() - .expect("artifact directory parent should exist"), - )?; + Err(rename_error) => { + let existing = (|| -> Result { + let directory = match prefix.open_directory(artifact_key.as_bytes()) { + Ok(dir) => dir, + Err(_) => return Err(NativeError::Io(rename_error)), + }; + let existing = match directory.open_readonly_file(ENVELOPE_FILE_NAME.as_bytes()) + { + Ok(existing) => existing, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + let Some(lease) = publication.as_ref() else { + return Err(error.into()); + }; + if !crate::storage::maintenance_origin::observe_published_origin_without_payload( + lease, &root, &directory, key_bytes, + )? { + return Err(error.into()); + } + // Retirement can leave this exact key directory and + // its old receipt behind after removing the payload. + // Publish only our newly created inode into the absent + // leaf; existing provenance and neighbors stay intact. + if path_identity(&self.root)? != root.identity() + || root.open_directory(prefix_name)?.identity() != prefix.identity() + || prefix.open_directory(artifact_key.as_bytes())?.identity() + != directory.identity() + { + return Err(NativeError::InvalidInput( + "artifact_publication_parent_changed".into(), + )); + } + temp.rename_file_no_replace( + ENVELOPE_FILE_NAME.as_bytes(), + &directory, + ENVELOPE_FILE_NAME.as_bytes(), + payload_identity, + )?; + if path_identity(&self.root)? != root.identity() + || root.open_directory(prefix_name)?.identity() != prefix.identity() + || prefix.open_directory(artifact_key.as_bytes())?.identity() + != directory.identity() + || file_identity( + &directory.open_readonly_file(ENVELOPE_FILE_NAME.as_bytes())?, + )? != payload_identity + { + return Err(NativeError::InvalidInput( + "artifact_published_incarnation_changed".into(), + )); + } + directory.sync_namespace()?; + temp.sync_namespace()?; + // The temporary receipt binds its original directory. + // It cannot attest this existing directory, and any + // older receipt cannot attest the new payload inode. + return Ok(ArtifactWriteOutcome::Written); + } + Err(error) => return Err(error.into()), + }; + let payload = temp.open_readonly_file(ENVELOPE_FILE_NAME.as_bytes())?; + if file_identity(&payload)? != payload_identity { + return Err(NativeError::InvalidInput( + "artifact_temporary_payload_changed".into(), + )); + } + if !same_file_bytes(&existing, &payload)? { + return Err(NativeError::InvalidInput( + "artifact_existing_payload_differs_or_unproven".into(), + )); + } + if prefix.open_directory(artifact_key.as_bytes())?.identity() + != directory.identity() + { + return Err(NativeError::InvalidInput( + "artifact_existing_directory_changed".into(), + )); + } + // A genuine fresh byte-equal producer can attest this one + // payload, never the existing directory or its neighbors. + if let Some(lease) = publication.as_ref() { + match directory.open_readonly_file(ORIGIN_NAME) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + if let Ok(mut reused) = OriginWriter::create( + lease, &directory, &existing, key_bytes, false, false, + ) { + if reused.writing().is_ok() + && reused + .complete(&directory, &existing, accepted.0, accepted.1) + .is_ok() + { + let _ = reused.published(lease, &directory); + } + } + } + _ => {} + } + } Ok(ArtifactWriteOutcome::Reused) - } else { - replace_existing_artifact(&self.root, &artifact_dir, &temp_dir)?; - Ok(ArtifactWriteOutcome::Written) - } - } - Err(error) => { - let _ = remove_tree_confined(&self.root, &temp_dir); - Err(NativeError::Io(error)) + })(); + cleanup_created( + &root, + &temp, + temp_name.as_bytes(), + payload_identity, + receipt_identity, + ); + existing } } } - #[cfg(test)] pub(crate) fn load_partition( &self, artifact_key: &str, expected: &ArtifactExpectations<'_>, - ) -> Result, NativeError> { - self.load_partition_with_budget(artifact_key, expected, u64::MAX) - } - - pub(crate) fn load_partition_with_budget( - &self, - artifact_key: &str, - expected: &ArtifactExpectations<'_>, - memory_limit_bytes: u64, ) -> Result, NativeError> { if validate_artifact_key(artifact_key).is_err() { return Ok(None); } - ensure_artifact_root(&self.root)?; - let artifact_dir = self.artifact_dir(artifact_key)?; - if artifact_dir.exists() { - ensure_directory_without_symlinks(&artifact_dir)?; - } - let payload_path = self.payload_path(artifact_key)?; - let file = match File::open(&payload_path) { - Ok(file) => file, - Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), - Err(error) => return Err(NativeError::Io(error)), - }; - let file_bytes = file.metadata()?.len(); - let accounted_bytes = artifact_decode_accounted_bytes(file_bytes); - if accounted_bytes > memory_limit_bytes { - return Err(NativeError::MemoryBudgetExceeded( - MemoryBudgetExceeded::new("artifact_load", memory_limit_bytes, accounted_bytes, 0), - )); - } - let mut reader = BufReader::with_capacity(ARTIFACT_IO_BUFFER_BYTES, file); - let envelope: ArtifactEnvelope = - match serde_json::from_reader(&mut reader.by_ref().take(file_bytes)) { - Ok(envelope) => envelope, - Err(_) => return Ok(None), - }; - if reader.get_ref().metadata()?.len() != file_bytes { + let Some(_use_lease) = + crate::storage::maintenance_ownership::ArtifactUseLease::try_existing_shared_read( + &self.root, + )? + else { return Ok(None); - } - if envelope.format_version != ARTIFACT_FORMAT_VERSION - || envelope.artifact_key != artifact_key - { - return Ok(None); - } - if envelope.partition.entry.path != expected.path - || envelope.partition.entry.content_hash != expected.content_hash - || envelope.partition.entry.language != expected.language + }; + use crate::storage::locks::{path_identity, RootedDirectory}; + let root = RootedDirectory::open_readonly_root(&self.root, path_identity(&self.root)?)?; + let file = match root + .open_directory_readonly(&artifact_key.as_bytes()[..2]) + .and_then(|prefix| prefix.open_directory_readonly(artifact_key.as_bytes())) + .and_then(|directory| directory.open_readonly_file(ENVELOPE_FILE_NAME.as_bytes())) { - return Ok(None); - } - if envelope.partition.entry.artifact_key.as_deref() != Some(artifact_key) { - return Ok(None); - } - let payload_sha256 = match partition_payload_sha256(&envelope.partition) { - Ok(payload_sha256) => payload_sha256, - Err(_) => return Ok(None), + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(error.into()), }; - if envelope.partition_payload_sha256 != payload_sha256 { - return Ok(None); - } - if envelope.partition.validate_raw_rows().is_err() { - return Ok(None); - } - Ok(Some(envelope.partition)) + load_partition_file(file, artifact_key, expected) } pub(crate) fn list_keys(&self) -> Result, NativeError> { @@ -316,39 +470,14 @@ impl ArtifactStore { Ok(keys) } + #[cfg(test)] pub(crate) fn delete_key(&self, artifact_key: &str) -> Result { if validate_artifact_key(artifact_key).is_err() { return Ok(false); } - let artifact_dir = self.artifact_dir(artifact_key)?; - if !artifact_dir.exists() { - return Ok(false); - } - ensure_artifact_root(&self.root)?; - remove_tree_confined(&self.root, &artifact_dir)?; - sync_dir( - artifact_dir - .parent() - .expect("artifact directory parent should exist"), - )?; - Ok(true) - } - - fn artifact_dir(&self, artifact_key: &str) -> Result { - validate_artifact_key(artifact_key)?; - Ok(self.root.join(&artifact_key[..2]).join(artifact_key)) - } - - fn payload_path(&self, artifact_key: &str) -> Result { - Ok(self.artifact_dir(artifact_key)?.join(ENVELOPE_FILE_NAME)) - } - - fn temp_dir_path(&self, artifact_key: &str) -> PathBuf { - let nonce = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|duration| duration.as_nanos()) - .unwrap_or_default(); - self.root.join(format!(".tmp-{artifact_key}-{nonce}")) + Err(NativeError::InvalidInput( + "artifact_collection_requires_current_maintenance_authority".into(), + )) } } @@ -376,16 +505,209 @@ fn is_valid_artifact_prefix(prefix: &str) -> bool { .all(|byte| matches!(byte, b'0'..=b'9' | b'a'..=b'f')) } -fn write_envelope_and_sync( - path: &std::path::Path, +fn load_partition_file( + file: File, + artifact_key: &str, + expected: &ArtifactExpectations<'_>, +) -> Result, NativeError> { + let file_bytes = file.metadata()?.len(); + let mut reader = BufReader::with_capacity(ARTIFACT_IO_BUFFER_BYTES, file); + let envelope: ArtifactEnvelope = + match serde_json::from_reader(&mut reader.by_ref().take(file_bytes)) { + Ok(envelope) => envelope, + Err(_) => return Ok(None), + }; + if reader.get_ref().metadata()?.len() != file_bytes { + return Ok(None); + } + if envelope.format_version != ARTIFACT_FORMAT_VERSION || envelope.artifact_key != artifact_key { + return Ok(None); + } + if envelope.partition.entry.path != expected.path + || envelope.partition.entry.content_hash != expected.content_hash + || envelope.partition.entry.language != expected.language + { + return Ok(None); + } + if envelope.partition.entry.artifact_key.as_deref() != Some(artifact_key) { + return Ok(None); + } + let payload_sha256 = match partition_payload_sha256(&envelope.partition) { + Ok(payload_sha256) => payload_sha256, + Err(_) => return Ok(None), + }; + if envelope.partition_payload_sha256 != payload_sha256 { + return Ok(None); + } + if envelope.partition.validate_raw_rows().is_err() { + return Ok(None); + } + Ok(Some(envelope.partition)) +} + +// Only this specific primary publication conflict permits a pipeline-local +// rebuild copy. IO, source changes and other guards propagate. +pub(crate) fn is_canonical_payload_conflict(error: &NativeError) -> bool { + matches!(error, NativeError::InvalidInput(reason) + if reason == "artifact_existing_payload_differs_or_unproven") +} + +/// An actual create_new owner for one per-run rebuild copy. It never adopts a +/// canonical cache entry or grants reference/DB/cleanup authority. Files are +/// closed between planning and staging; only fixed creator facts are retained. +pub(crate) struct TransientPartition { + path: PathBuf, + parent: crate::storage::locks::FileIdentity, + identity: crate::storage::locks::FileIdentity, + nonce: Option<[u8; 32]>, + stamp: Option, + key: String, +} +impl TransientPartition { + pub(crate) fn plan_bytes<'a>(staging: &str, paths: impl Iterator) -> u64 { + // Owner slots, conservative path/key capacities and the extra retained + // conflict diagnostic are reserved before parse-worker fanout. + let fixed = (std::mem::size_of::>() as u64) + .saturating_add((staging.len() as u64).saturating_add(80).saturating_mul(2)) + .saturating_add(512); + paths.fold( + std::mem::size_of::>>() as u64, + |bytes, path| { + bytes + .saturating_add(fixed) + .saturating_add((path.len() as u64).saturating_mul(2)) + }, + ) + } + pub(crate) fn create( + staging: &std::path::Path, + key: &str, + partition: &GraphPartition, + ) -> Result { + use crate::process_lifecycle::FreshLifecycleFile; + use crate::storage::{locks, maintenance_controls::WorkspaceControlFile}; + validate_artifact_key(key)?; + let parent = locks::path_identity(staging)?; + let mut operation = [0; 32]; + getrandom::fill(&mut operation).map_err(|_| { + NativeError::InvalidInput("transient_artifact_nonce_unavailable".into()) + })?; + let path = staging.join(format!( + ".partition-fallback-{}.json", + hex_lower(&operation) + )); + let fresh = FreshLifecycleFile::create(&path).map_err(NativeError::InvalidInput)?; + let mut owner = Self { + path: path.clone(), + parent, + identity: locks::file_identity(fresh.file())?, + nonce: None, + stamp: None, + key: key.into(), + }; + let mut output = WorkspaceControlFile::from_created(fresh, &path); + owner.nonce = match &output { + WorkspaceControlFile::Tracked(tracked) => { + let _ = + crate::storage::maintenance_origin::record_created_workspace_control(tracked); + Some(tracked.origin_binding().operation) + } + WorkspaceControlFile::Plain(fresh) => { + match locks::set_created_file_nonce(fresh.file(), &operation) { + Ok(()) => { + if locks::created_file_nonce(fresh.file())? != Some(operation) { + return Err(NativeError::InvalidInput( + "transient_artifact_creator_changed".into(), + )); + } + Some(operation) + } + Err(error) if error.kind() == std::io::ErrorKind::Unsupported => None, + Err(error) => return Err(error.into()), + } + } + }; + let digest = partition_payload_sha256(partition)?; + { + let mut writer = BufWriter::with_capacity(ARTIFACT_IO_BUFFER_BYTES, &mut output); + write_envelope(&mut writer, key, &digest, partition)?; + writer.flush()?; + } + if let WorkspaceControlFile::Plain(fresh) = &output { + owner.stamp = Some( + locks::FixedFileStamp::capture(fresh.file(), locks::FixedControlKind::File) + .map_err(|_| { + NativeError::InvalidInput("transient_artifact_creator_changed".into()) + })?, + ); + } + output.finish_created_recorded()?; + let file = owner.open_created()?; + owner.stamp = Some( + locks::FixedFileStamp::capture(&file, locks::FixedControlKind::File).map_err(|_| { + NativeError::InvalidInput("transient_artifact_creator_changed".into()) + })?, + ); + Ok(owner) + } + fn open_created(&self) -> Result { + use crate::storage::locks::{self, RootedDirectory}; + let parent = RootedDirectory::open_readonly_root(self.path.parent().unwrap(), self.parent)?; + let file = parent.open_readonly_file(self.path.file_name().unwrap().as_encoded_bytes())?; + if locks::file_identity(&file)? != self.identity + || self + .nonce + .is_some_and(|nonce| locks::created_file_nonce(&file).ok().flatten() != Some(nonce)) + { + return Err(NativeError::InvalidInput( + "transient_artifact_creator_changed".into(), + )); + } + if let Some(stamp) = self.stamp { + stamp + .recheck(&file) + .map_err(|_| NativeError::InvalidInput("transient_artifact_changed".into()))?; + } + Ok(file) + } + pub(crate) fn load( + &self, + expected: &ArtifactExpectations<'_>, + ) -> Result { + let file = self.open_created()?; + let partition = load_partition_file(file, &self.key, expected)? + .ok_or_else(|| NativeError::InvalidInput("transient_artifact_invalid".into()))?; + drop(self.open_created()?); + Ok(partition) + } +} +impl Drop for TransientPartition { + fn drop(&mut self) { + // Replacements/aliases remain at their own paths. Never recurse or move + // the parent or a neighbor; only the exact file we exclusively created. + if (self.nonce.is_none() && self.stamp.is_none()) || self.open_created().is_err() { + return; + } + if let Ok(parent) = + crate::storage::locks::RootedDirectory::open(self.path.parent().unwrap(), self.parent) + { + let _ = parent.unlink_file( + self.path.file_name().unwrap().as_encoded_bytes(), + self.identity, + ); + let _ = parent.sync_namespace(); + } + } +} + +fn write_envelope( + writer: &mut impl Write, artifact_key: &str, partition_payload_sha256: &str, partition: &GraphPartition, ) -> Result<(), NativeError> { - let file = File::create(path)?; - let mut writer = BufWriter::with_capacity(ARTIFACT_IO_BUFFER_BYTES, file); serde_json::to_writer_pretty( - &mut writer, + writer, &ArtifactEnvelopeRef { format_version: ARTIFACT_FORMAT_VERSION, artifact_key, @@ -393,175 +715,215 @@ fn write_envelope_and_sync( partition, }, )?; - writer.flush()?; - writer.get_ref().sync_all()?; Ok(()) } -fn partition_payload_sha256(partition: &GraphPartition) -> Result { - let mut writer = DigestWriter::default(); - serde_json::to_writer(&mut writer, partition)?; - Ok(writer.finish()) +struct AcceptedWriter<'a> { + file: &'a mut File, + hash: Sha256, + len: u64, } - -fn artifact_decode_accounted_bytes(file_bytes: u64) -> u64 { - file_bytes - .checked_mul(ARTIFACT_DECODE_EXPANSION_FACTOR) - .and_then(|bytes| bytes.checked_add(ARTIFACT_IO_BUFFER_BYTES as u64)) - .unwrap_or(u64::MAX) +impl Write for AcceptedWriter<'_> { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + let n = self.file.write(bytes)?; + self.hash.update(&bytes[..n]); + self.len = self + .len + .checked_add(n as u64) + .ok_or(std::io::ErrorKind::InvalidData)?; + Ok(n) + } + fn flush(&mut self) -> std::io::Result<()> { + self.file.flush() + } } - -fn existing_artifact_matches( - artifact_dir: &std::path::Path, - candidate_payload_path: &std::path::Path, -) -> Result { - ensure_directory_without_symlinks(artifact_dir)?; - let payload_path = artifact_dir.join(ENVELOPE_FILE_NAME); - let existing = match File::open(&payload_path) { - Ok(file) => file, - Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false), - Err(error) => return Err(NativeError::Io(error)), +fn write_envelope_file_and_sync( + file: &mut File, + artifact_key: &str, + partition_payload_sha256: &str, + partition: &GraphPartition, +) -> Result<(u64, [u8; 32]), NativeError> { + let accepted = AcceptedWriter { + file, + hash: Sha256::new(), + len: 0, }; - let candidate = File::open(candidate_payload_path)?; - if existing.metadata()?.len() != candidate.metadata()?.len() { - return Ok(false); + let mut writer = BufWriter::with_capacity(ARTIFACT_IO_BUFFER_BYTES, accepted); + write_envelope( + &mut writer, + artifact_key, + partition_payload_sha256, + partition, + )?; + writer.flush()?; + writer.get_mut().file.sync_all()?; + let accepted = writer + .into_inner() + .map_err(|error| NativeError::Io(error.into_error()))?; + Ok((accepted.len, accepted.hash.finalize().into())) +} +fn decode_key(text: &str) -> [u8; 32] { + let mut key = [0; 32]; + for (i, pair) in text.as_bytes().chunks_exact(2).enumerate() { + let digit = |b: u8| if b <= b'9' { b - b'0' } else { b - b'a' + 10 }; + key[i] = (digit(pair[0]) << 4) | digit(pair[1]); } - let mut existing = BufReader::with_capacity(ARTIFACT_IO_BUFFER_BYTES, existing); - let mut candidate = BufReader::with_capacity(ARTIFACT_IO_BUFFER_BYTES, candidate); - let mut existing_buffer = [0_u8; 8 * 1024]; - let mut candidate_buffer = [0_u8; 8 * 1024]; - loop { - let existing_read = existing.read(&mut existing_buffer)?; - let candidate_read = candidate.read(&mut candidate_buffer)?; - if existing_read != candidate_read - || existing_buffer[..existing_read] != candidate_buffer[..candidate_read] + key +} +fn verify_created_shape( + directory: &crate::storage::locks::RootedDirectory, + payload: crate::storage::locks::FileIdentity, + receipt: Option, +) -> Result<(), NativeError> { + use crate::storage::locks::file_identity; + let mut bytes = [0; 4096]; + let mut found_payload = false; + let mut found_receipt = false; + directory.visit_entries(&mut bytes, &mut |name| { + let expected = if name == ENVELOPE_FILE_NAME.as_bytes() { + found_payload = true; + Some(payload) + } else if name == crate::storage::maintenance_origin::ORIGIN_NAME { + found_receipt = true; + receipt + } else { + None + }; + let Some(expected) = expected else { + return Err(std::io::ErrorKind::InvalidData.into()); + }; + let file = directory.open_readonly_file(name)?; + if file_identity(&file) + .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))? + != expected { - return Ok(false); - } - if existing_read == 0 { - return Ok(true); + return Err(std::io::ErrorKind::InvalidData.into()); } + Ok(()) + })?; + if !found_payload || found_receipt != receipt.is_some() { + return Err(NativeError::InvalidInput( + "artifact_created_leaf_changed".into(), + )); } + Ok(()) } - -fn replace_existing_artifact( - artifact_root: &std::path::Path, - artifact_dir: &std::path::Path, - temp_dir: &std::path::Path, +#[cfg(test)] +type ProducerHook = Box; +#[cfg(test)] +thread_local! {static PRODUCER_HOOK:std::cell::RefCell> =std::cell::RefCell::new(None);} +#[cfg(test)] +fn producer_hook(stage: &str, directory: &crate::storage::locks::RootedDirectory) { + PRODUCER_HOOK.with(|hook| { + if let Some(hook) = hook.borrow_mut().as_mut() { + hook(stage, directory); + } + }); +} +/// Fixture-only interruption after the real producer has durably prepared its +/// payload and receipt. No test constructs authority or receipt bytes. +#[cfg(test)] +pub(crate) fn leave_prepared_temporary_for_test( + store: &ArtifactStore, + key: &str, + partition: &GraphPartition, ) -> Result<(), NativeError> { - ensure_directory_without_symlinks(artifact_dir)?; - ensure_directory_without_symlinks(temp_dir)?; - let parent = artifact_dir.parent().ok_or_else(|| { - NativeError::InvalidInput("artifact directory must have a parent".to_string()) - })?; - let backup_dir = parent.join(format!( - ".replaced-{}-{}", - artifact_dir - .file_name() - .and_then(|value| value.to_str()) - .unwrap_or("artifact"), - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|duration| duration.as_nanos()) - .unwrap_or_default() - )); - fs::rename(artifact_dir, &backup_dir)?; - match fs::rename(temp_dir, artifact_dir) { - Ok(()) => { - sync_dir(parent)?; - remove_tree_confined(artifact_root, &backup_dir)?; - sync_dir(parent)?; - Ok(()) + struct PreparedInterruption; + struct Reset; + impl Drop for Reset { + fn drop(&mut self) { + PRODUCER_HOOK.with(|hook| { + hook.borrow_mut().take(); + }); } - Err(error) => { - if !artifact_dir.exists() { - let _ = fs::rename(&backup_dir, artifact_dir); - let _ = sync_dir(parent); + } + PRODUCER_HOOK.with(|hook| { + assert!( + hook.borrow().is_none(), + "producer fixture hooks cannot nest" + ); + *hook.borrow_mut() = Some(Box::new(|stage, _| { + if stage == "prepared" { + std::panic::panic_any(PreparedInterruption); } - let _ = remove_tree_confined(artifact_root, temp_dir); - Err(NativeError::Io(error)) - } + })); + }); + let reset = Reset; + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + store.store_partition(key, partition) + })); + drop(reset); + match result { + Err(payload) if payload.is::() => Ok(()), + Err(payload) => std::panic::resume_unwind(payload), + Ok(Err(error)) => Err(error), + Ok(Ok(_)) => Err(NativeError::InvalidInput( + "producer_preparation_fault_not_reached".into(), + )), } } - -fn ensure_artifact_root(root: &std::path::Path) -> Result<(), NativeError> { - if !root.exists() { - return Ok(()); - } - let metadata = fs::symlink_metadata(root)?; - if metadata.file_type().is_symlink() || !metadata.is_dir() { - return Err(NativeError::InvalidInput(format!( - "artifact root must be a real directory: {}", - root.display() - ))); +fn cleanup_created( + root: &crate::storage::locks::RootedDirectory, + temp: &crate::storage::locks::RootedDirectory, + name: &[u8], + payload: crate::storage::locks::FileIdentity, + receipt: Option, +) { + let _ = temp.unlink_file(ENVELOPE_FILE_NAME.as_bytes(), payload); + if let Some(receipt) = receipt { + let _ = temp.unlink_file(crate::storage::maintenance_origin::ORIGIN_NAME, receipt); } - Ok(()) + let _ = temp.sync_namespace(); + let _ = root.remove_empty_directory(name, temp.identity()); + let _ = root.sync_namespace(); } - -fn ensure_directory_without_symlinks(path: &std::path::Path) -> Result<(), NativeError> { - let metadata = fs::symlink_metadata(path)?; - if metadata.file_type().is_symlink() || !metadata.is_dir() { - return Err(NativeError::InvalidInput(format!( - "refusing to use symlinked or non-directory artifact path {}", - path.display() - ))); +fn same_file_bytes(existing: &File, candidate: &File) -> Result { + use crate::storage::locks::{FixedControlKind, FixedFileStamp}; + use std::io::{Seek, SeekFrom}; + let invalid = + || NativeError::InvalidInput("artifact_equality_source_changed_or_aliased".into()); + let old = FixedFileStamp::capture(existing, FixedControlKind::File).map_err(|_| invalid())?; + let new = FixedFileStamp::capture(candidate, FixedControlKind::File).map_err(|_| invalid())?; + if old.len() != new.len() { + return Ok(false); } - for entry in fs::read_dir(path)? { - let entry = entry?; - let metadata = fs::symlink_metadata(entry.path())?; - if metadata.file_type().is_symlink() { - return Err(NativeError::InvalidInput(format!( - "refusing to use symlinked artifact path {}", - entry.path().display() - ))); + let (mut left, mut right) = (existing, candidate); + left.seek(SeekFrom::Start(0))?; + right.seek(SeekFrom::Start(0))?; + let mut a = [0; 8192]; + let mut b = [0; 8192]; + loop { + let na = left.read(&mut a)?; + let nb = right.read(&mut b)?; + if na != nb || a[..na] != b[..nb] { + return Ok(false); } - if metadata.is_dir() { - ensure_directory_without_symlinks(&entry.path())?; + if na == 0 { + break; } } - Ok(()) + old.recheck(existing).map_err(|_| invalid())?; + new.recheck(candidate).map_err(|_| invalid())?; + Ok(true) } -fn remove_tree_confined( - artifact_root: &std::path::Path, - target: &std::path::Path, -) -> Result<(), NativeError> { - ensure_artifact_root(artifact_root)?; - let canonical_root = fs::canonicalize(artifact_root)?; - let metadata = fs::symlink_metadata(target)?; - if metadata.file_type().is_symlink() { - return Err(NativeError::InvalidInput(format!( - "refusing to remove symlinked artifact path {}", - target.display() - ))); - } - let canonical_target = fs::canonicalize(target)?; - if canonical_target == canonical_root || !canonical_target.starts_with(&canonical_root) { - return Err(NativeError::InvalidInput(format!( - "refusing to remove artifact path outside {}: {}", - canonical_root.display(), - canonical_target.display() - ))); - } - remove_path_without_symlinks(&canonical_target) +fn partition_payload_sha256(partition: &GraphPartition) -> Result { + let mut writer = DigestWriter::default(); + serde_json::to_writer(&mut writer, partition)?; + Ok(writer.finish()) } -fn remove_path_without_symlinks(path: &std::path::Path) -> Result<(), NativeError> { - let metadata = fs::symlink_metadata(path)?; - if metadata.file_type().is_symlink() { +fn ensure_artifact_root(root: &std::path::Path) -> Result<(), NativeError> { + if !root.exists() { + return Ok(()); + } + let metadata = fs::symlink_metadata(root)?; + if metadata.file_type().is_symlink() || !metadata.is_dir() { return Err(NativeError::InvalidInput(format!( - "refusing to remove symlinked artifact path {}", - path.display() + "artifact root must be a real directory: {}", + root.display() ))); } - if metadata.is_dir() { - for entry in fs::read_dir(path)? { - remove_path_without_symlinks(&entry?.path())?; - } - fs::remove_dir(path)?; - } else { - fs::remove_file(path)?; - } Ok(()) } @@ -660,8 +1022,6 @@ mod tests { atomic_rebuild: false, strict: false, parallel: true, - worker_memory_mib: 768, - rust_memory_mib: 384, spill_chunk_mib: 32, max_parallelism: 2, progress: false, @@ -817,6 +1177,53 @@ mod tests { ); } + #[test] + fn transient_partition_loads_only_actual_created_file_and_preserves_replacement() { + let root = unique_temp_dir("codebase-graph-transient-partition"); + fs::create_dir_all(&root).unwrap(); + let key = "a".repeat(64); + let partition = sample_partition_for_key(&key); + let expected = ArtifactExpectations { + path: "src/lib.rs", + content_hash: "content-hash", + language: "rust", + }; + let owner = TransientPartition::create(&root, &key, &partition).unwrap(); + let created = owner.path.clone(); + owner.load(&expected).unwrap().validate_raw_rows().unwrap(); + assert!(owner.load(&expected).is_ok()); + drop(owner); + assert!(!created.exists()); + let owner = TransientPartition::create(&root, &key, &partition).unwrap(); + let replaced = owner.path.clone(); + let retained = root.join("retained-own-copy"); + fs::rename(&replaced, &retained).unwrap(); + fs::write(&replaced, b"foreign replacement must remain").unwrap(); + let id = crate::storage::locks::path_identity(&replaced).unwrap(); + assert!(owner.load(&expected).is_err()); + drop(owner); + assert_eq!(crate::storage::locks::path_identity(&replaced).unwrap(), id); + assert_eq!( + fs::read(&replaced).unwrap(), + b"foreign replacement must remain" + ); + assert!(retained.is_file()); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn transient_fallback_selects_only_explicit_canonical_payload_conflict() { + assert!(is_canonical_payload_conflict(&NativeError::InvalidInput( + "artifact_existing_payload_differs_or_unproven".into(), + ))); + assert!(!is_canonical_payload_conflict(&NativeError::InvalidInput( + "artifact_publication_parent_changed".into(), + ))); + assert!(!is_canonical_payload_conflict(&NativeError::Io( + std::io::ErrorKind::NotFound.into() + ))); + } + #[test] fn artifact_store_roundtrips_raw_partition_and_reuses_existing_payload() { let root = unique_temp_dir("codebase-graph-artifacts"); @@ -830,14 +1237,13 @@ mod tests { let first = store.store_partition(&key, &partition).unwrap(); let second = store.store_partition(&key, &partition).unwrap(); let loaded = store - .load_partition_with_budget( + .load_partition( &key, &ArtifactExpectations { path: "src/lib.rs", content_hash: "content-hash", language: "rust", }, - 16 * 1024 * 1024, ) .unwrap() .unwrap(); @@ -862,50 +1268,6 @@ mod tests { ); } - #[test] - fn oversized_artifact_is_rejected_before_deserialization() { - let root = unique_temp_dir("codebase-graph-artifacts-budget"); - let store = ArtifactStore::new(&root); - let key = "d".repeat(64); - let artifact_dir = root.join(&key[..2]).join(&key); - fs::create_dir_all(&artifact_dir).unwrap(); - File::create(artifact_dir.join(ENVELOPE_FILE_NAME)) - .unwrap() - .set_len(2 * 1024 * 1024) - .unwrap(); - - let error = store - .load_partition_with_budget( - &key, - &ArtifactExpectations { - path: "src/lib.rs", - content_hash: "content-hash", - language: "rust", - }, - 1024 * 1024, - ) - .unwrap_err(); - - let NativeError::MemoryBudgetExceeded(error) = error else { - panic!("oversized artifact should fail with a structured memory budget error"); - }; - assert_eq!(error.phase, "artifact_load"); - assert_eq!(error.limit_bytes, 1024 * 1024); - assert!(error.accounted_bytes > error.limit_bytes); - assert_eq!(error.observed_rss_bytes, 0); - let _ = fs::remove_dir_all(root); - } - - #[test] - fn reference_sized_artifact_fits_the_default_per_worker_budget() { - let reference_artifact_bytes = 77 * 1024 * 1024; - let per_worker_budget_bytes = 192 * 1024 * 1024; - - assert!( - artifact_decode_accounted_bytes(reference_artifact_bytes) <= per_worker_budget_bytes - ); - } - #[test] fn artifact_store_treats_corrupt_or_mismatched_payloads_as_cache_miss() { let root = unique_temp_dir("codebase-graph-artifacts-corrupt"); @@ -1039,14 +1401,18 @@ mod tests { .count(), 0 ); - assert!(store.delete_key(&key).unwrap()); - assert!(store.list_keys().unwrap().is_empty()); + assert!(store + .delete_key(&key) + .unwrap_err() + .to_string() + .contains("requires_current_maintenance_authority")); + assert_eq!(store.list_keys().unwrap(), vec![key.clone()]); let _ = fs::remove_dir_all(root); } #[test] - fn corrupt_existing_artifact_is_replaced_atomically_on_rebuild() { + fn corrupt_existing_artifact_is_preserved_on_prepublication_failure() { let root = unique_temp_dir("codebase-graph-artifacts-replace"); let store = ArtifactStore::new(&root); let request = sample_request(); @@ -1071,24 +1437,29 @@ mod tests { ) .unwrap(); + let before = fs::read(&payload_path).unwrap(); + let before_identity = crate::storage::locks::path_identity(&payload_path).unwrap(); + assert!(store + .store_partition(&key, &sample_partition_for_key(&key)) + .unwrap_err() + .to_string() + .contains("differs_or_unproven")); + assert_eq!(fs::read(&payload_path).unwrap(), before); assert_eq!( - store - .store_partition(&key, &sample_partition_for_key(&key)) - .unwrap(), - ArtifactWriteOutcome::Written + crate::storage::locks::path_identity(&payload_path).unwrap(), + before_identity ); - let loaded = store + assert!(store .load_partition( &key, &ArtifactExpectations { path: "src/lib.rs", content_hash: "content-hash", - language: "rust", - }, + language: "rust" + } ) .unwrap() - .unwrap(); - assert_eq!(loaded.entry.artifact_key.as_deref(), Some(key.as_str())); + .is_none()); let _ = fs::remove_dir_all(root); } @@ -1109,11 +1480,544 @@ mod tests { symlink(&outside, prefix.join(&key)).unwrap(); let error = store.delete_key(&key).unwrap_err(); - assert!(error.to_string().contains("symlinked artifact path")); + assert!(error + .to_string() + .contains("requires_current_maintenance_authority")); assert_eq!( fs::read_to_string(outside.join("keep.txt")).unwrap(), "keep" ); let _ = fs::remove_dir_all(root); } + + #[test] + fn dc_s3_artifact_producing_guard_covers_write_reuse_replace_and_failure() { + use crate::storage::layout::{create_fresh_reference_root, ManagedLayout}; + use crate::storage::locks::{open_locked, LockMode}; + use crate::storage::maintenance_ownership::{ + initialize_fresh_reference_domain, observe_reference_epoch, EpochObservation, + ReferenceDomain, + }; + let root = unique_temp_dir("cleanup-s3-real-artifact-epoch"); + fs::create_dir_all(&root).unwrap(); + let layout = ManagedLayout::new(root.join("storage")); + let fresh = create_fresh_reference_root(layout.storage_root()) + .unwrap() + .unwrap(); + layout.ensure_roots().unwrap(); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + let observe = || match observe_reference_epoch(&domain).unwrap() { + EpochObservation::Verified(epoch) => epoch, + other => panic!("producer did not release reference activity: {other:?}"), + }; + let store = ArtifactStore::new(layout.artifacts_root()); + let key = "a".repeat(64); + let partition = sample_partition_for_key(&key); + let before = observe(); + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Written + ); + let written = observe(); + assert!(written.counter() > before.counter()); + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Reused + ); + let reused = observe(); + assert!(reused.counter() > written.counter()); + let payload = layout + .artifacts_root() + .join("aa") + .join(&key) + .join(ENVELOPE_FILE_NAME); + fs::write(&payload, b"corrupt old payload").unwrap(); + assert!(store.store_partition(&key, &partition).is_err()); + let rejected = observe(); + assert!(rejected.counter() > reused.counter()); + assert_eq!(fs::read(&payload).unwrap(), b"corrupt old payload"); + assert!(store + .load_partition( + &key, + &ArtifactExpectations { + path: "src/lib.rs", + content_hash: "content-hash", + language: "rust" + } + ) + .unwrap() + .is_none()); + let blocked_key = "b".repeat(64); + let blocked_prefix = layout.artifacts_root().join("bb"); + fs::write(&blocked_prefix, b"foreign prefix sentinel").unwrap(); + assert!(store + .store_partition(&blocked_key, &sample_partition_for_key(&blocked_key)) + .is_err()); + let failed = observe(); + assert!(failed.counter() > rejected.counter()); + assert_eq!( + fs::read(blocked_prefix).unwrap(), + b"foreign prefix sentinel" + ); + // Key validation precedes even a creating producer guard. + let absent = root.join("invalid-key-must-not-create"); + assert!(ArtifactStore::new(&absent) + .store_partition("invalid", &partition) + .is_err()); + assert!(!absent.exists()); + assert_eq!(observe(), failed); + drop(writer); + fs::remove_dir_all(root).unwrap(); + } + fn tracked_origin_fixture(label: &str) -> (PathBuf, PathBuf) { + use crate::storage::{ + layout::{create_fresh_reference_root, ManagedLayout}, + locks::{open_locked, LockMode}, + maintenance_ownership::{initialize_fresh_reference_domain, ReferenceDomain}, + }; + let base = unique_temp_dir(label); + fs::create_dir_all(&base).unwrap(); + // Darwin's temp_dir may spell /var through a symlink. Positive + // capability fixtures use the actual created root, as production gates + // intentionally reject unresolved alias components. + let base = fs::canonicalize(base).unwrap(); + let layout = ManagedLayout::new(base.join("storage")); + let fresh = create_fresh_reference_root(layout.storage_root()) + .unwrap() + .unwrap(); + layout.ensure_roots().unwrap(); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + drop(writer); + (base, layout.artifacts_root().to_path_buf()) + } + fn observed_origin( + root: &std::path::Path, + key: &str, + ) -> crate::storage::maintenance_origin::OriginObservation { + use crate::storage::{ + locks::{path_identity, RootedDirectory}, + maintenance_budget::CleanupMemory, + maintenance_origin::observe_artifact_origin, + }; + let root = RootedDirectory::open_readonly_root(root, path_identity(root).unwrap()).unwrap(); + let directory = root + .open_directory_readonly(&key.as_bytes()[..2]) + .unwrap() + .open_directory_readonly(key.as_bytes()) + .unwrap(); + let payload = directory + .open_readonly_file(ENVELOPE_FILE_NAME.as_bytes()) + .unwrap(); + let memory = CleanupMemory::new(128 * 1024).unwrap(); + let floor = memory.used(); + let result = + observe_artifact_origin(&root, &directory, &payload, decode_key(key), &memory).unwrap(); + assert_eq!(memory.used(), floor); + result + } + #[test] + fn dc_origin_parallel_parser_publications_both_retain_real_creation_receipts() { + use crate::storage::maintenance_origin::{OriginObservation, OriginPhase}; + let (base, root) = tracked_origin_fixture("dc-origin-parallel"); + let store = ArtifactStore::new(&root); + let barrier = std::sync::Arc::new(std::sync::Barrier::new(2)); + let mut workers = Vec::new(); + for key in ["a".repeat(64), "b".repeat(64)] { + let store = store.clone(); + let barrier = barrier.clone(); + workers.push(std::thread::spawn(move || { + let partition = sample_partition_for_key(&key); + barrier.wait(); + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Written + ); + key + })); + } + for worker in workers { + let key = worker.join().unwrap(); + assert!( + matches!(observed_origin(&root,&key),OriginObservation::Observed(facts) if facts.phase==OriginPhase::Published && facts.directory_created && facts.payload_created) + ); + } + fs::remove_dir_all(base).unwrap(); + } + #[test] + fn dc_origin_actual_fresh_publication_reuse_bounded_receipt_and_old_load_no_attestation() { + use crate::storage::maintenance_origin::{OriginObservation, OriginPhase}; + let (base, root) = tracked_origin_fixture("dc-origin-real-fresh"); + let store = ArtifactStore::new(&root); + let key = "e".repeat(64); + let partition = sample_partition_for_key(&key); + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Written + ); + let OriginObservation::Observed(facts) = observed_origin(&root, &key) else { + panic!("fresh real producer must attest") + }; + assert_eq!(facts.phase, OriginPhase::Published); + assert!(facts.directory_created && facts.payload_created); + assert!(!facts.torn_tail); + let dir = root.join("ee").join(&key); + let receipt = dir.join(".entry-origin"); + let bytes = fs::read(&receipt).unwrap(); + assert_eq!(bytes.len(), 2048); + let identity = crate::storage::locks::path_identity(&receipt).unwrap(); + for _ in 0..3 { + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Reused + ); + assert_eq!(fs::read(&receipt).unwrap(), bytes); + assert_eq!( + crate::storage::locks::path_identity(&receipt).unwrap(), + identity + ); + } + fs::remove_file(&receipt).unwrap(); + fs::write(dir.join("foreign-neighbor"), b"keep").unwrap(); + assert!(store + .load_partition( + &key, + &ArtifactExpectations { + path: "src/lib.rs", + content_hash: "content-hash", + language: "rust" + } + ) + .unwrap() + .is_some()); + assert!(!receipt.exists()); + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Reused + ); + let OriginObservation::Observed(reused) = observed_origin(&root, &key) else { + panic!("fresh exact equal reuse must attest leaf") + }; + assert_eq!(reused.phase, OriginPhase::Published); + assert!(!reused.directory_created && !reused.payload_created); + assert_eq!(reused.payload, facts.payload); + assert_eq!(fs::read(dir.join("foreign-neighbor")).unwrap(), b"keep"); + fs::remove_dir_all(base).unwrap(); + } + #[test] + fn dc_origin_missing_payload_is_republished_without_adopting_retained_directory() { + use crate::storage::maintenance_origin::OriginObservation; + let (base, root) = tracked_origin_fixture("dc-origin-republish-missing"); + let store = ArtifactStore::new(&root); + let key = "b".repeat(64); + let partition = sample_partition_for_key(&key); + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Written + ); + let dir = root.join("bb").join(&key); + let receipt = dir.join(".entry-origin"); + let payload = dir.join("partition.json"); + let directory_id = crate::storage::locks::path_identity(&dir).unwrap(); + let receipt_id = crate::storage::locks::path_identity(&receipt).unwrap(); + let receipt_bytes = fs::read(&receipt).unwrap(); + let OriginObservation::Observed(facts) = observed_origin(&root, &key) else { + panic!("actual producer must establish the original receipt"); + }; + assert_eq!( + crate::storage::locks::created_file_nonce(&File::open(&receipt).unwrap()).unwrap(), + Some(facts.operation_nonce) + ); + + // Keep the removed inode open so the fresh producer cannot receive its + // identity through immediate filesystem inode reuse. + let old_payload = File::open(&payload).unwrap(); + let old_payload_id = crate::storage::locks::file_identity(&old_payload).unwrap(); + fs::remove_file(&payload).unwrap(); + let neighbor = dir.join("foreign-neighbor"); + fs::write(&neighbor, b"keep at original path").unwrap(); + let neighbor_id = crate::storage::locks::path_identity(&neighbor).unwrap(); + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Written + ); + assert_eq!( + crate::storage::locks::path_identity(&dir).unwrap(), + directory_id + ); + assert_eq!( + crate::storage::locks::path_identity(&receipt).unwrap(), + receipt_id + ); + assert_eq!(fs::read(&receipt).unwrap(), receipt_bytes); + assert_eq!( + crate::storage::locks::path_identity(&neighbor).unwrap(), + neighbor_id + ); + assert_eq!(fs::read(&neighbor).unwrap(), b"keep at original path"); + assert_ne!( + crate::storage::locks::path_identity(&payload).unwrap(), + old_payload_id + ); + assert!(matches!( + observed_origin(&root, &key), + OriginObservation::Unknown(_) + )); + assert!(store + .load_partition( + &key, + &ArtifactExpectations { + path: "src/lib.rs", + content_hash: "content-hash", + language: "rust", + } + ) + .unwrap() + .is_some()); + drop(old_payload); + fs::remove_dir_all(base).unwrap(); + } + + #[test] + fn dc_origin_missing_payload_refuses_unstamped_or_spoofed_receipt_birth() { + use crate::storage::locks::{file_identity, FileIdentity}; + let (base, root) = tracked_origin_fixture("dc-origin-republish-spoof"); + let store = ArtifactStore::new(&root); + let seed = "a".repeat(64); + store + .store_partition(&seed, &sample_partition_for_key(&seed)) + .unwrap(); + let seed_receipt = root.join("aa").join(&seed).join(".entry-origin"); + let original = fs::read(&seed_receipt).unwrap(); + for forged_marker in [false, true] { + let key = if forged_marker { + "c".repeat(64) + } else { + "b".repeat(64) + }; + let dir = root.join(&key[..2]).join(&key); + fs::create_dir_all(&dir).unwrap(); + let directory_id = crate::storage::locks::path_identity(&dir).unwrap(); + let receipt_path = dir.join(".entry-origin"); + let mut receipt = File::create(&receipt_path).unwrap(); + let receipt_id = file_identity(&receipt).unwrap(); + let mut spoofed = original.clone(); + let put_identity = |out: &mut [u8], id: FileIdentity| { + let (volume, index) = id.fixed_words(); + out[..8].copy_from_slice(&volume.to_le_bytes()); + out[8..16].copy_from_slice(&index.to_le_bytes()); + }; + for image in spoofed.chunks_exact_mut(512) { + image[64..96].copy_from_slice(&decode_key(&key)); + put_identity(&mut image[208..224], directory_id); + put_identity(&mut image[240..256], receipt_id); + let checksum = Sha256::digest(&image[..480]); + image[480..].copy_from_slice(&checksum); + } + receipt.write_all(&spoofed).unwrap(); + if forged_marker { + // A marker exists, but the actual creator nonce does not match + // the operation copied into this otherwise rebound chain. + crate::storage::locks::set_created_file_nonce(&receipt, &[91; 32]).unwrap(); + } + let neighbor = dir.join("foreign-neighbor"); + fs::write(&neighbor, b"keep").unwrap(); + let neighbor_id = crate::storage::locks::path_identity(&neighbor).unwrap(); + assert!(store + .store_partition(&key, &sample_partition_for_key(&key)) + .is_err()); + assert!(!dir.join("partition.json").exists()); + assert_eq!( + crate::storage::locks::path_identity(&dir).unwrap(), + directory_id + ); + assert_eq!( + crate::storage::locks::path_identity(&receipt_path).unwrap(), + receipt_id + ); + assert_eq!(fs::read(&receipt_path).unwrap(), spoofed); + assert_eq!( + crate::storage::locks::path_identity(&neighbor).unwrap(), + neighbor_id + ); + assert_eq!(fs::read(&neighbor).unwrap(), b"keep"); + } + fs::remove_dir_all(base).unwrap(); + } + + #[test] + fn dc_origin_unproven_empty_or_different_canonical_is_never_moved_or_overwritten() { + let (base, root) = tracked_origin_fixture("dc-origin-unproven"); + let store = ArtifactStore::new(&root); + let seed = "a".repeat(64); + store + .store_partition(&seed, &sample_partition_for_key(&seed)) + .unwrap(); + let key = "b".repeat(64); + let dir = root.join("bb").join(&key); + fs::create_dir_all(&dir).unwrap(); + let id = crate::storage::locks::path_identity(&dir).unwrap(); + assert!(store + .store_partition(&key, &sample_partition_for_key(&key)) + .is_err()); + assert_eq!(crate::storage::locks::path_identity(&dir).unwrap(), id); + assert_eq!(fs::read_dir(&dir).unwrap().count(), 0); + fs::write(dir.join("partition.json"), b"foreign payload").unwrap(); + fs::write(dir.join("neighbor"), b"foreign sibling").unwrap(); + let payload_id = crate::storage::locks::path_identity(&dir.join("partition.json")).unwrap(); + assert!(store + .store_partition(&key, &sample_partition_for_key(&key)) + .is_err()); + assert_eq!( + fs::read(dir.join("partition.json")).unwrap(), + b"foreign payload" + ); + assert_eq!( + crate::storage::locks::path_identity(&dir.join("partition.json")).unwrap(), + payload_id + ); + assert_eq!(fs::read(dir.join("neighbor")).unwrap(), b"foreign sibling"); + assert!(!dir.join(".entry-origin").exists()); + fs::remove_dir_all(base).unwrap(); + } + #[test] + fn dc_origin_created_temp_foreign_neighbor_stays_at_original_name_on_failure() { + let (base, root) = tracked_origin_fixture("dc-origin-temp-neighbor"); + let store = ArtifactStore::new(&root); + let key = "c".repeat(64); + PRODUCER_HOOK.with(|hook| { + *hook.borrow_mut() = Some(Box::new(|stage, directory| { + if stage == "prepared" { + let mut foreign = directory.create_new(b"foreign-neighbor").unwrap(); + foreign.write_all(b"keep at original address").unwrap(); + } + })) + }); + let result = store.store_partition(&key, &sample_partition_for_key(&key)); + PRODUCER_HOOK.with(|hook| hook.borrow_mut().take()); + assert!(result.is_err()); + assert!(!root.join("cc").join(&key).exists()); + let temp = fs::read_dir(&root) + .unwrap() + .map(|e| e.unwrap().path()) + .find(|p| { + p.file_name() + .unwrap() + .to_string_lossy() + .starts_with(".tmp-") + }) + .unwrap(); + assert_eq!( + fs::read(temp.join("foreign-neighbor")).unwrap(), + b"keep at original address" + ); + assert!(!temp.join("partition.json").exists()); + fs::remove_dir_all(base).unwrap(); + } + #[test] + fn dc_origin_optional_busy_publisher_still_writes_rooted_untracked_payload() { + use crate::storage::locks::open_existing_exclusive; + use crate::storage::maintenance_origin::OriginObservation; + let (base, root) = tracked_origin_fixture("dc-origin-optional-publisher"); + let store = ArtifactStore::new(&root); + let first = "d".repeat(64); + store + .store_partition(&first, &sample_partition_for_key(&first)) + .unwrap(); + let selector = open_existing_exclusive(&root.join(".publication.selector")).unwrap(); + let key = "f".repeat(64); + assert_eq!( + store + .store_partition(&key, &sample_partition_for_key(&key)) + .unwrap(), + ArtifactWriteOutcome::Written + ); + drop(selector); + assert_eq!(observed_origin(&root, &key), OriginObservation::Missing); + assert!(store + .load_partition( + &key, + &ArtifactExpectations { + path: "src/lib.rs", + content_hash: "content-hash", + language: "rust" + } + ) + .unwrap() + .is_some()); + assert!(!root.join("ff").join(&key).join(".entry-origin").exists()); + fs::remove_dir_all(base).unwrap(); + } + #[test] + fn dc_origin_optional_receipt_completion_failure_preserves_actual_published_payload() { + let (base, root) = tracked_origin_fixture("dc-origin-postpublish-tracking"); + let store = ArtifactStore::new(&root); + let key = "a".repeat(64); + PRODUCER_HOOK.with(|hook| { + *hook.borrow_mut() = Some(Box::new(|stage, directory| { + if stage == "published" { + directory + .open_existing(b".entry-origin") + .unwrap() + .set_len(0) + .unwrap(); + } + })) + }); + let result = store.store_partition(&key, &sample_partition_for_key(&key)); + PRODUCER_HOOK.with(|hook| hook.borrow_mut().take()); + assert_eq!(result.unwrap(), ArtifactWriteOutcome::Written); + assert!(store + .load_partition( + &key, + &ArtifactExpectations { + path: "src/lib.rs", + content_hash: "content-hash", + language: "rust" + } + ) + .unwrap() + .is_some()); + assert!(matches!( + observed_origin(&root, &key), + crate::storage::maintenance_origin::OriginObservation::Unknown(_) + )); + fs::remove_dir_all(base).unwrap(); + } + #[test] + fn dc_origin_completed_receipt_remains_origin_only_for_nonfinite_loader_failure() { + let (base, root) = tracked_origin_fixture("dc-origin-nonfinite"); + let store = ArtifactStore::new(&root); + let key = "9".repeat(64); + let mut partition = sample_partition_for_key(&key); + partition.edges[0].confidence = f64::NAN; + assert_eq!( + store.store_partition(&key, &partition).unwrap(), + ArtifactWriteOutcome::Written + ); + assert!(matches!( + observed_origin(&root, &key), + crate::storage::maintenance_origin::OriginObservation::Observed( + crate::storage::maintenance_origin::OriginFacts { + phase: crate::storage::maintenance_origin::OriginPhase::Published, + .. + } + ) + )); + assert!(store + .load_partition( + &key, + &ArtifactExpectations { + path: "src/lib.rs", + content_hash: "content-hash", + language: "rust" + } + ) + .unwrap() + .is_none()); + fs::remove_dir_all(base).unwrap(); + } } diff --git a/src/coordinator.rs b/src/coordinator.rs index 69d53eed..63466148 100644 --- a/src/coordinator.rs +++ b/src/coordinator.rs @@ -4,6 +4,7 @@ use crate::api::{ ApiError, CodebaseGraphApi, OperationInvocation, OperationResponse, RefreshServiceConfig, RepoSelector, }; +use crate::api::{RefreshCancellation, RefreshRuntime, RefreshState}; use crate::storage::atomic::write_json_atomically; use crate::storage::layout::{DirectLayout, ManagedLayout}; use crate::storage::locks::{try_open_locked, CoordinatorLease, LockMode}; @@ -49,8 +50,21 @@ impl CoordinatorApiConfig { } } - fn build_api(&self) -> CodebaseGraphApi { - CodebaseGraphApi::for_coordinator_owner(self.selector.clone(), self.refresh) + fn build_api( + &self, + cancellation: RefreshCancellation, + maintenance: Option< + crate::storage::maintenance::MaintenanceClient>, + >, + unavailable: Option, + ) -> (CodebaseGraphApi, Option) { + CodebaseGraphApi::for_coordinator_owner_with_maintenance( + self.selector.clone(), + self.refresh, + cancellation, + maintenance, + unavailable, + ) } } @@ -64,19 +78,98 @@ struct ClientInner { control: CoordinatorControlPaths, api_config: CoordinatorApiConfig, route: Mutex, - monitor_stop: AtomicBool, - monitor: Mutex>>, + monitor_stop: Arc, + monitor: JoinedThread, } impl Drop for ClientInner { fn drop(&mut self) { self.monitor_stop.store(true, Ordering::Release); - if let Ok(mut monitor) = self.monitor.lock() { - // The last strong reference can be released by the monitor itself. - // Dropping the handle detaches safely and the weak loop then exits. - monitor.take(); + // The final strong reference may be released by this monitor itself. + // That case returns to monitor_route's immediate post-drop exit tail. + // External callers always join; no shared mutex spans the wait. + self.monitor.join(); + } +} + +#[derive(Debug)] +enum JoinState { + Empty, + Running(JoinHandle<()>), + // Preserve target identity even after another drainer takes the handle. + Joining(thread::ThreadId), + Joined, +} + +#[derive(Debug)] +struct JoinedThread { + state: Mutex, + completed: Condvar, +} +impl JoinedThread { + fn new(handle: Option>) -> Self { + Self { + state: Mutex::new(handle.map_or(JoinState::Empty, JoinState::Running)), + completed: Condvar::new(), } } + fn install(&self, handle: JoinHandle<()>) { + let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner()); + assert!(matches!(*state, JoinState::Empty)); + *state = JoinState::Running(handle); + } + fn is_running(&self) -> bool { + match &*self.state.lock().unwrap_or_else(|e| e.into_inner()) { + JoinState::Running(handle) => !handle.is_finished(), + JoinState::Joining(_) => true, + _ => false, + } + } + /// False means the target is the calling thread, never completed drain. + fn join(&self) -> bool { + let handle = { + let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner()); + loop { + match &*state { + JoinState::Empty | JoinState::Joined => return true, + JoinState::Running(handle) => { + let target = handle.thread().id(); + if target == thread::current().id() { + return false; + } + let JoinState::Running(handle) = + std::mem::replace(&mut *state, JoinState::Joining(target)) + else { + unreachable!() + }; + break handle; + } + JoinState::Joining(target) if *target == thread::current().id() => { + return false + } + JoinState::Joining(_) => { + state = self + .completed + .wait(state) + .unwrap_or_else(|e| e.into_inner()) + } + } + } + }; + // First local, last to drop, including if a join panic payload panics. + // Concurrent drainers cannot pass until all join-owned disposal ends. + let _completion = JoinCompletion(self); + let result = handle.join(); + drop(result); + true + } +} +struct JoinCompletion<'a>(&'a JoinedThread); +impl Drop for JoinCompletion<'_> { + fn drop(&mut self) { + *self.0.state.lock().unwrap_or_else(|e| e.into_inner()) = JoinState::Joined; + self.0.completed.notify_all(); + } } #[derive(Debug, Default)] @@ -88,38 +181,28 @@ struct CoordinatorRoute { #[derive(Debug)] struct CoordinatorOwner { stop: Arc, - thread: Mutex>>, + thread: JoinedThread, dispatcher: Arc, + refresh_cancel: RefreshCancellation, } impl Drop for CoordinatorOwner { fn drop(&mut self) { - self.stop.store(true, Ordering::Release); - self.dispatcher.stop_admission(); - if let Ok(mut thread) = self.thread.lock() { - if let Some(thread) = thread.take() { - let _ = thread.join(); - } - } + self.stop_and_drain(); } } - impl CoordinatorOwner { fn is_running(&self) -> bool { - self.thread - .lock() - .map(|thread| thread.as_ref().is_some_and(|thread| !thread.is_finished())) - .unwrap_or(false) + self.thread.is_running() } - fn stop_and_drain(&self) { self.stop.store(true, Ordering::Release); + self.refresh_cancel.request_stop(); self.dispatcher.stop_admission(); - if let Ok(mut thread) = self.thread.lock() { - if let Some(thread) = thread.take() { - let _ = thread.join(); - } - } + assert!( + self.thread.join(), + "coordinator owner cannot drain its own serving thread" + ); } } @@ -127,6 +210,8 @@ impl CoordinatorOwner { struct CoordinatorControlPaths { lock: PathBuf, state: PathBuf, + managed_initialization: Option, + direct_initialization: Option, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -153,6 +238,7 @@ struct ExecutionJob { operation_id: String, invocation: OperationInvocation, context: ExecutionContext, + legacy_confirmation: Option, stream: TcpStream, _connection: ConnectionPermit, } @@ -165,13 +251,66 @@ struct ExecutionDispatchState { } #[derive(Debug, Default)] -struct ExecutionDispatcher { +pub(crate) struct ExecutionDispatcher { state: Mutex, ready: Condvar, active_snapshot: AtomicBool, } +/// Noncloneable ownership of the dispatcher's real execution slot. It has no +/// caller deadline: only completion/drop of the actual maintenance work releases +/// admission, including when its initiating caller has gone away. +#[derive(Debug)] +pub(crate) struct MaintenanceExecutionPermit { + dispatcher: Arc, +} + +impl Drop for MaintenanceExecutionPermit { + fn drop(&mut self) { + self.dispatcher.finish_one(); + } +} + +// Driver-only boundary: advisory work never owns this permit. Production +// collection wiring remains gated on the accepted storage controller contract. +impl crate::storage::maintenance_scheduler::MaintenanceAdmission for Arc { + type Permit = MaintenanceExecutionPermit; + + fn try_acquire(&self) -> Option { + self.try_acquire_maintenance() + } + + fn stop_admission(&self) { + ExecutionDispatcher::stop_admission(self); + } + + #[cfg(test)] + fn run_recovery( + &self, + operation: &mut dyn FnMut() + -> Result<(), crate::storage::maintenance_scheduler::DriverError>, + ) -> Result { + let Some(_permit) = self.try_acquire_maintenance() else { + return Ok(false); + }; + operation()?; + Ok(true) + } +} + impl ExecutionDispatcher { + fn try_acquire_maintenance(self: &Arc) -> Option { + let mut state = self.state.lock().unwrap_or_else(|error| error.into_inner()); + if state.stopping || state.active || state.job.is_some() { + return None; + } + state.active = true; + self.active_snapshot.store(true, Ordering::Release); + Some(MaintenanceExecutionPermit { + dispatcher: Arc::clone(self), + }) + } + fn try_submit(&self, job: ExecutionJob) -> Result<(), Box> { let mut state = self .state @@ -358,8 +497,8 @@ impl CoordinatorClient { control, api_config: config, route: Mutex::new(CoordinatorRoute::default()), - monitor_stop: AtomicBool::new(false), - monitor: Mutex::new(None), + monitor_stop: Arc::new(AtomicBool::new(false)), + monitor: JoinedThread::new(None), }), }; client.refresh_route()?; @@ -369,15 +508,12 @@ impl CoordinatorClient { fn start_monitor(&self) -> Result<(), String> { let weak = Arc::downgrade(&self.inner); + let stop = Arc::clone(&self.inner.monitor_stop); let monitor = thread::Builder::new() .name("codebase-graph-coordinator-monitor".to_string()) - .spawn(move || monitor_route(weak)) + .spawn(move || monitor_route(weak, stop)) .map_err(|error| format!("failed to start coordinator monitor: {error}"))?; - *self - .inner - .monitor - .lock() - .map_err(|_| "coordinator monitor lock is poisoned".to_string())? = Some(monitor); + self.inner.monitor.install(monitor); Ok(()) } @@ -421,6 +557,20 @@ impl CoordinatorClient { ); loop { context.remaining()?; + if let Some(layout) = &self.inner.control.managed_initialization { + // Only the actual mutating acquisition may create this root. + // Existing roots are never enrolled by observation or emptiness. + crate::storage::maintenance_ownership::initialize_fresh_managed_domain(layout) + .map_err(|error| { + ApiError::new("coordinator_unavailable", error.to_string()).retryable(true) + })?; + } + if let Some(layout) = &self.inner.control.direct_initialization { + crate::storage::maintenance_ownership::initialize_fresh_direct_domain(layout) + .map_err(|error| { + ApiError::new("coordinator_unavailable", error.to_string()).retryable(true) + })?; + } if let Some(lease) = try_open_locked(&self.inner.control.lock, LockMode::Exclusive) .map_err(|error| { ApiError::new("coordinator_unavailable", error.to_string()).retryable(true) @@ -601,13 +751,10 @@ impl CoordinatorClient { pub(crate) fn drain_owned_operation(&self) { self.inner.monitor_stop.store(true, Ordering::Release); - if let Ok(mut monitor) = self.inner.monitor.lock() { - if let Some(monitor) = monitor.take() { - if monitor.thread().id() != thread::current().id() { - let _ = monitor.join(); - } - } - } + assert!( + self.inner.monitor.join(), + "an external monitor drain cannot join itself" + ); let owner = loop { match self.inner.route.try_lock() { Ok(route) => break route.owner.clone(), @@ -656,30 +803,56 @@ impl CoordinatorClient { } } -fn monitor_route(inner: Weak) { +fn monitor_route(weak: Weak, stop: Arc) { + monitor_route_with_probe(weak, stop, |_| {}); +} + +enum MonitorPhase { + BeforeWait, + Observed, +} +fn monitor_route_with_probe( + weak: Weak, + stop: Arc, + mut probe: P, +) { loop { - thread::sleep(MONITOR_INTERVAL); - let Some(inner) = inner.upgrade() else { - return; - }; - if inner.monitor_stop.load(Ordering::Acquire) { + if stop.load(Ordering::Acquire) || weak.strong_count() == 0 { return; } - let (state, owner) = inner - .route - .lock() - .ok() - .map(|route| (route.state.clone(), route.owner.clone())) - .unwrap_or((None, None)); - let route_is_reachable = match owner { - Some(owner) => owner.is_running(), - None => state - .as_ref() - .is_some_and(|state| ping_state(state).is_ok()), - }; - if !route_is_reachable { - let client = CoordinatorClient { inner }; - let _ = client.refresh_route(); + probe(MonitorPhase::BeforeWait); + thread::sleep(MONITOR_INTERVAL); + { + let Some(inner) = weak.upgrade() else { + return; + }; + if stop.load(Ordering::Acquire) { + return; + } + let (state, owner) = inner + .route + .lock() + .ok() + .map(|route| (route.state.clone(), route.owner.clone())) + .unwrap_or((None, None)); + let reachable = match owner { + Some(owner) => owner.is_running(), + None => state + .as_ref() + .is_some_and(|state| ping_state(state).is_ok()), + }; + if !reachable { + let client = CoordinatorClient { + inner: Arc::clone(&inner), + }; + let _ = client.refresh_route(); + } + probe(MonitorPhase::Observed); + } // All iteration strong references, including a temporary client, end. + // A final self-drop can set stop during that destruction. Check the + // independent token/Weak only AFTER it, before another sleep or work. + if stop.load(Ordering::Acquire) || weak.strong_count() == 0 { + return; } } } @@ -689,6 +862,27 @@ fn start_owner( lease: CoordinatorLease, config: CoordinatorApiConfig, ) -> Result<(CoordinatorState, CoordinatorOwner), String> { + start_owner_with_probe(control, lease, config, |stage| { + if let OwnerStartup::RefreshStarted(state) = stage { + drop(state); + } + }) +} + +enum OwnerStartup { + BeforeRefresh, + RefreshStarted(Option>), +} + +fn start_owner_with_probe

( + control: CoordinatorControlPaths, + lease: CoordinatorLease, + config: CoordinatorApiConfig, + mut started: P, +) -> Result<(CoordinatorState, CoordinatorOwner), String> +where + P: FnMut(OwnerStartup) + Send + 'static, +{ let listener = TcpListener::bind(("127.0.0.1", 0)) .map_err(|error| format!("failed to bind repository coordinator: {error}"))?; listener @@ -709,6 +903,8 @@ fn start_owner( let stop = Arc::new(AtomicBool::new(false)); let server_stop = Arc::clone(&stop); + let refresh_cancel = RefreshCancellation::default(); + let server_refresh_cancel = refresh_cancel.clone(); let server_state = state.clone(); let state_path = control.state.clone(); let dispatcher = Arc::new(ExecutionDispatcher::default()); @@ -716,7 +912,37 @@ fn start_owner( let thread = thread::Builder::new() .name("codebase-graph-coordinator".to_string()) .spawn(move || { - let api = config.build_api(); + started(OwnerStartup::BeforeRefresh); + let maintenance = (|| { + let memory = crate::storage::maintenance_budget::CleanupMemory::new( + crate::storage::maintenance::MAINTENANCE_MEMORY_BYTES, + ) + .map_err(|_| crate::storage::maintenance_scheduler::DriverError::ResourceLimit)?; + let target = config + .identity + .as_ref() + .expect("captured coordinator identity") + .maintenance_target(&memory) + .map_err(|_| { + crate::storage::maintenance_scheduler::DriverError::ResourceLimit + })?; + crate::storage::maintenance::start_controller( + target, + memory, + Arc::clone(&server_dispatcher), + false, + ) + })(); + let (mut maintenance_runtime, maintenance_client, maintenance_error) = match maintenance + { + Ok((runtime, client)) => (Some(runtime), Some(client), None), + Err(error) => (None, None, Some(error)), + }; + let (api, mut refresh_runtime) = + config.build_api(server_refresh_cancel, maintenance_client, maintenance_error); + started(OwnerStartup::RefreshStarted( + refresh_runtime.as_ref().map(RefreshRuntime::state), + )); let identity = config .identity .as_ref() @@ -732,6 +958,15 @@ fn start_owner( &server_stop, &server_dispatcher, ); + if let Some(runtime) = maintenance_runtime.as_ref() { + runtime.request_stop(); + } + if let Some(runtime) = refresh_runtime.as_mut() { + runtime.stop_and_join(); + } + if let Some(runtime) = maintenance_runtime.as_mut() { + runtime.stop_and_join(); + } remove_owned_state(&state_path, &server_state.token); drop(lease); }) @@ -740,8 +975,9 @@ fn start_owner( state, CoordinatorOwner { stop, - thread: Mutex::new(Some(thread)), + thread: JoinedThread::new(Some(thread)), dispatcher, + refresh_cancel, }, )) } @@ -764,16 +1000,22 @@ fn serve_owner( state, stop, dispatcher, - move |mut invocation| { + move |operation_id, mut invocation| { identity .validate() .map_err(|error| ApiError::new("repository_identity_changed", error))?; invocation.repo = selector.clone(); - Ok(invocation) + let confirmation = + api.prepare_legacy_confirmation_request(operation_id, &invocation)?; + Ok((invocation, confirmation)) }, - move |operation_id, invocation, context| { + move |operation_id, invocation, context, confirmation| { context.remaining()?; - owner_api.execute_invocation(operation_id, invocation) + owner_api.execute_invocation_with_legacy_confirmation( + operation_id, + invocation, + confirmation, + ) }, ); } @@ -786,8 +1028,16 @@ fn serve_owner_with_executor( prepare: P, execute: E, ) where - P: Fn(OperationInvocation) -> Result + Send + Sync + 'static, - E: Fn(&str, &OperationInvocation, ExecutionContext) -> Result + P: Fn(&str, OperationInvocation) -> Result<(OperationInvocation, Option), ApiError> + + Send + + Sync + + 'static, + E: Fn( + &str, + &OperationInvocation, + ExecutionContext, + Option, + ) -> Result + Send + Sync + 'static, @@ -798,37 +1048,40 @@ fn serve_owner_with_executor( let prepare = Arc::new(prepare); let execute = Arc::new(execute); let worker_execute = Arc::clone(&execute); - let worker = thread::Builder::new() - .name("codebase-graph-coordinator-executor".to_string()) - .spawn(move || { - while let Some(job) = worker_dispatcher.take() { - let reply = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - if job.context.remaining().is_err() { - None - } else { - match worker_execute(&job.operation_id, &job.invocation, job.context) { - Ok(mut response) if job.context.remaining().is_ok() => { - attach_coordinator_status(&mut response, &worker_state); - Some(CoordinatorReply::Success(response)) - } - Ok(_) if job.context.remaining().is_err() => None, - Ok(_) => None, - Err(_) if job.context.remaining().is_err() => None, - Err(error) => Some(CoordinatorReply::Failure(error)), + let worker = spawn_execution_worker(move || { + while let Some(job) = worker_dispatcher.take() { + let reply = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + if job.context.remaining().is_err() { + None + } else { + match worker_execute( + &job.operation_id, + &job.invocation, + job.context, + job.legacy_confirmation, + ) { + Ok(mut response) if job.context.remaining().is_ok() => { + attach_coordinator_status(&mut response, &worker_state); + Some(CoordinatorReply::Success(response)) } + Ok(_) if job.context.remaining().is_err() => None, + Ok(_) => None, + Err(_) if job.context.remaining().is_err() => None, + Err(error) => Some(CoordinatorReply::Failure(error)), } - })) - .unwrap_or(None); - worker_dispatcher.finish_one(); - if let Some(reply) = reply { - let registry = Arc::clone(&job._connection.registry); - registry.send_response(job.stream, reply, job._connection); - } else { - let _ = job.stream.shutdown(std::net::Shutdown::Both); - drop(job._connection); } + })) + .unwrap_or(None); + worker_dispatcher.finish_one(); + if let Some(reply) = reply { + let registry = Arc::clone(&job._connection.registry); + registry.send_response(job.stream, reply, job._connection); + } else { + let _ = job.stream.shutdown(std::net::Shutdown::Both); + drop(job._connection); } - }); + } + }); let Ok(worker) = worker else { dispatcher.stop_admission(); @@ -891,6 +1144,23 @@ fn serve_owner_with_executor( registry.join_writers(); } +#[cfg(test)] +thread_local! { + static INJECT_EXECUTOR_SPAWN_FAILURE: std::cell::Cell = const { std::cell::Cell::new(false) }; +} +fn spawn_execution_worker( + body: F, +) -> std::io::Result> { + #[cfg(test)] + if INJECT_EXECUTOR_SPAWN_FAILURE.with(|flag| flag.replace(false)) { + drop(body); + return Err(std::io::Error::other("fixture executor spawn failure")); + } + thread::Builder::new() + .name("codebase-graph-coordinator-executor".into()) + .spawn(body) +} + fn handle_connection_dispatch

( stream: &mut TcpStream, accepted_at: Instant, @@ -899,7 +1169,10 @@ fn handle_connection_dispatch

( connection: ConnectionPermit, prepare: Arc

, ) where - P: Fn(OperationInvocation) -> Result + Send + Sync + 'static, + P: Fn(&str, OperationInvocation) -> Result<(OperationInvocation, Option), ApiError> + + Send + + Sync + + 'static, { let request_deadline = accepted_at + COORDINATOR_FRAME_TIMEOUT; let request: CoordinatorRequest = match read_frame_until(stream, request_deadline) { @@ -952,8 +1225,8 @@ fn handle_connection_dispatch

( return; } }; - let invocation = match prepare(invocation) { - Ok(invocation) => invocation, + let (invocation, legacy_confirmation) = match prepare(&operation_id, invocation) { + Ok(prepared) => prepared, Err(error) => { let reply = CoordinatorReply::Failure(error); let _ = write_frame_until( @@ -977,6 +1250,7 @@ fn handle_connection_dispatch

( operation_id, invocation, context, + legacy_confirmation, stream: job_stream, _connection: connection, }; @@ -1187,12 +1461,16 @@ fn coordinator_control_paths(selector: &RepoSelector) -> Result CoordinatorCl control: CoordinatorControlPaths { lock: root.join("coordinator.lock"), state: root.join("coordinator.json"), + managed_initialization: None, + direct_initialization: None, }, api_config: direct_config(root), route: Mutex::new(CoordinatorRoute { state: Some(state), owner: None, }), - monitor_stop: AtomicBool::new(true), - monitor: Mutex::new(None), + monitor_stop: Arc::new(AtomicBool::new(true)), + monitor: JoinedThread::new(None), }), } } @@ -525,8 +595,9 @@ fn test_client_with_owner( let mut client = test_client_with_state(root, state.clone()); let owner = Arc::new(CoordinatorOwner { stop, - thread: Mutex::new(Some(server)), + thread: JoinedThread::new(Some(server)), dispatcher, + refresh_cancel: RefreshCancellation::default(), }); Arc::get_mut(&mut client.inner) .expect("test client has a unique inner") @@ -585,3 +656,874 @@ fn temp_dir(label: &str) -> PathBuf { fs::create_dir_all(&path).unwrap(); path } + +fn maintenance_test_job() -> ExecutionJob { + let listener = TcpListener::bind(("127.0.0.1", 0)).unwrap(); + let stream = TcpStream::connect(listener.local_addr().unwrap()).unwrap(); + let (_server, _) = listener.accept().unwrap(); + let registry = Arc::new(ConnectionRegistry::default()); + let connection = registry.register(&stream).unwrap(); + ExecutionJob { + operation_id: "health".into(), + invocation: test_invocation(), + context: ExecutionContext::with_timeout(Instant::now(), Duration::from_secs(60)), + legacy_confirmation: None, + stream, + _connection: connection, + } +} + +#[test] +fn maintenance_permit_blocks_real_execution_until_actual_drop() { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + let permit = dispatcher.try_acquire_maintenance().unwrap(); + assert!(dispatcher.active()); + assert!(dispatcher.try_acquire_maintenance().is_none()); + let rejected = dispatcher.try_submit(maintenance_test_job()).unwrap_err(); + assert!(dispatcher.active()); + drop(permit); + assert!(!dispatcher.active()); + dispatcher.try_submit(*rejected).unwrap(); + // Taking the queued job does not release actual operation ownership. + let job = dispatcher.take().unwrap(); + assert!(dispatcher.try_acquire_maintenance().is_none()); + drop(job); + dispatcher.finish_one(); + assert!(dispatcher.try_acquire_maintenance().is_some()); +} + +#[test] +fn maintenance_cannot_bypass_queued_or_active_real_execution() { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + dispatcher.try_submit(maintenance_test_job()).unwrap(); + assert!(dispatcher.try_acquire_maintenance().is_none()); + let job = dispatcher.take().unwrap(); + assert!(dispatcher.try_acquire_maintenance().is_none()); + drop(job); + dispatcher.finish_one(); + let permit = dispatcher.try_acquire_maintenance().unwrap(); + dispatcher.stop_admission(); + assert!(dispatcher.active()); + drop(permit); + assert!(!dispatcher.active()); + assert!(dispatcher.try_acquire_maintenance().is_none()); + assert!(dispatcher.try_submit(maintenance_test_job()).is_err()); +} + +#[test] +fn maintenance_permit_is_exclusive_during_simultaneous_grant_requests() { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + let start = Arc::new(std::sync::Barrier::new(9)); + let acquired = Arc::new(std::sync::Barrier::new(9)); + let release = Arc::new(std::sync::Barrier::new(9)); + let mut workers = Vec::new(); + for _ in 0..8 { + let dispatcher = Arc::clone(&dispatcher); + let start = Arc::clone(&start); + let acquired = Arc::clone(&acquired); + let release = Arc::clone(&release); + workers.push(thread::spawn(move || { + start.wait(); + let permit = dispatcher.try_acquire_maintenance(); + let won = permit.is_some(); + acquired.wait(); + release.wait(); + drop(permit); + won + })); + } + start.wait(); + acquired.wait(); + assert!(dispatcher.active()); + release.wait(); + assert_eq!( + workers + .into_iter() + .map(|worker| worker.join().unwrap()) + .filter(|won| *won) + .count(), + 1 + ); + assert!(!dispatcher.active()); +} + +#[test] +fn maintenance_panic_releases_slot_without_resurrecting_stopped_admission() { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + let result = std::panic::catch_unwind({ + let dispatcher = Arc::clone(&dispatcher); + move || { + let _permit = dispatcher.try_acquire_maintenance().unwrap(); + dispatcher.stop_admission(); + panic!("fixture maintenance panic"); + } + }); + assert!(result.is_err()); + assert!(!dispatcher.active()); + assert!(dispatcher.try_acquire_maintenance().is_none()); +} + +// Uses the actual ExecutionDispatcher, not a parallel scheduler occupancy flag. +mod joined_maintenance_driver { + use super::*; + use crate::storage::maintenance_budget::{CleanupMemory, MutationGrant, ReservedBytes}; + use crate::storage::maintenance_scheduler::{ + AttemptOutcome, Cancellation, DriverError, DriverLiveness, MaintenanceDriver, + MaintenanceRuntime, RequestDisposition, TargetBinding, Trigger, + }; + use std::rc::Rc; + use std::sync::mpsc::{self, Receiver, Sender}; + + struct Work { + dispose_entered: Sender<()>, + dispose_release: Receiver<()>, + _bytes: ReservedBytes, + _not_send: Rc<()>, + } + impl Drop for Work { + fn drop(&mut self) { + self.dispose_entered.send(()).unwrap(); + self.dispose_release.recv().unwrap(); + } + } + struct Driver { + prepare_entered: Sender<()>, + prepare_release: Receiver<()>, + disposal: Option<(Sender<()>, Receiver<()>)>, + confirmations: Sender, + mode: u8, + _not_send: Rc<()>, + } + impl MaintenanceDriver for Driver { + type Work = Work; + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + memory: &CleanupMemory, + ) -> Result, DriverError> { + self.prepare_entered.send(()).unwrap(); + self.prepare_release.recv().unwrap(); + let (dispose_entered, dispose_release) = self.disposal.take().unwrap(); + Ok(Some(Work { + dispose_entered, + dispose_release, + _bytes: memory.reserve(32).unwrap().bytes(32).unwrap(), + _not_send: Rc::new(()), + })) + } + fn requires_legacy_confirmation(&self, _: &Work) -> bool { + true + } + fn mutate( + &mut self, + _: &Work, + grant: &mut MutationGrant, + confirmation: Option< + &crate::storage::maintenance_scheduler::LegacyQuiescenceConfirmation<'_>, + >, + ) -> Result { + grant.take_entry().unwrap(); + self.confirmations.send(confirmation.is_some()).unwrap(); + match self.mode { + 0 => Ok(AttemptOutcome::Complete), + 1 => Err(DriverError::Mutation), + _ => panic!("fixture mutation panic"), + } + } + } + + fn real_request_progresses(dispatcher: &Arc) { + dispatcher.try_submit(maintenance_test_job()).unwrap(); + let job = dispatcher.take().unwrap(); + assert!(dispatcher.active()); + drop(job); + dispatcher.finish_one(); + } + + #[test] + fn dc_joined_driver_releases_real_permit_before_slow_drop_on_success_error_and_panic() { + struct ReleaseGates { + prepare: Option>, + dispose: Option>, + } + impl ReleaseGates { + fn prepare(&mut self) { + if let Some(tx) = self.prepare.take() { + let _ = tx.send(()); + } + } + fn dispose(&mut self) { + if let Some(tx) = self.dispose.take() { + let _ = tx.send(()); + } + } + } + impl Drop for ReleaseGates { + fn drop(&mut self) { + self.prepare(); + self.dispose(); + } + } + for mode in 0..3 { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + let (prepare_entered, prepared) = mpsc::channel(); + let (release_prepare, prepare_release) = mpsc::channel(); + let (dispose_entered, disposing) = mpsc::channel(); + let (release_dispose, dispose_release) = mpsc::channel(); + let (confirmations, confirmation) = mpsc::channel(); + // Confirmation must be part of the preparation snapshot. A later + // true request cannot authorize work prepared without that source. + let mut runtime = MaintenanceRuntime::start_with_confirmation( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + dispatcher.clone(), + move |_| { + Ok(Driver { + prepare_entered, + prepare_release, + disposal: Some((dispose_entered, dispose_release)), + confirmations, + mode, + _not_send: Rc::new(()), + }) + }, + true, + None, + ) + .unwrap(); + // Declared after runtime: unwind opens both gates before runtime + // Drop joins a preparer or slow Work destructor. + let mut release = ReleaseGates { + prepare: Some(release_prepare), + dispose: Some(release_dispose), + }; + prepared.recv_timeout(Duration::from_secs(5)).unwrap(); + real_request_progresses(&dispatcher); + assert_eq!( + runtime + .handle() + .request(Trigger::Health, Some(TargetBinding([1; 32]))), + RequestDisposition::Enqueued + ); + // Duplicate true coalesces; a later ordinary request cannot clear it. + assert_eq!( + runtime + .handle() + .request(Trigger::Health, Some(TargetBinding([1; 32]))), + RequestDisposition::Enqueued + ); + assert_eq!( + runtime.handle().request(Trigger::Health, None), + RequestDisposition::Enqueued + ); + release.prepare(); + assert!(confirmation.recv_timeout(Duration::from_secs(5)).unwrap()); + disposing.recv_timeout(Duration::from_secs(5)).unwrap(); + assert!(!dispatcher.active()); + real_request_progresses(&dispatcher); + let handle = runtime.handle(); + // Joining must wait for actual disposal, but cannot hold the mailbox. + let (joined_tx, joined) = mpsc::channel(); + let joiner = thread::spawn(move || { + runtime.stop_and_join(); + joined_tx.send(()).unwrap(); + }); + let stopping_deadline = Instant::now() + Duration::from_secs(5); + while handle.snapshot().liveness == DriverLiveness::Running { + assert!( + Instant::now() < stopping_deadline, + "join did not publish stopping" + ); + thread::yield_now(); + } + assert_eq!( + handle.request(Trigger::Health, None), + RequestDisposition::Stopped + ); + assert!(joined.try_recv().is_err()); + release.dispose(); + joined.recv_timeout(Duration::from_secs(5)).unwrap(); + joiner.join().unwrap(); + assert_eq!( + handle.snapshot().liveness, + if mode == 2 { + DriverLiveness::Failed + } else { + DriverLiveness::Stopped + } + ); + } + } + + struct AdvisoryFinishWork { + disposed: Arc, + _not_send: Rc<()>, + } + impl Drop for AdvisoryFinishWork { + fn drop(&mut self) { + self.disposed.store(true, Ordering::Release); + } + } + struct AdvisoryFinishDriver { + entered: Sender<()>, + release: Receiver<()>, + disposed: Arc, + dispatcher: Arc, + recovered: Arc, + } + impl MaintenanceDriver for AdvisoryFinishDriver { + type Work = AdvisoryFinishWork; + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + Ok(Some(AdvisoryFinishWork { + disposed: Arc::clone(&self.disposed), + _not_send: Rc::new(()), + })) + } + fn mutate( + &mut self, + _: &Self::Work, + _: &mut MutationGrant, + _: Option<&crate::storage::maintenance_scheduler::LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + assert!(self.dispatcher.active()); + Ok(AttemptOutcome::Complete) + } + fn finish( + &mut self, + _: &mut Self::Work, + outcome: AttemptOutcome, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result { + self.entered.send(()).unwrap(); + self.release.recv().unwrap(); + Ok(outcome) + } + fn after_disposal( + &mut self, + outcome: AttemptOutcome, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result { + assert!( + self.disposed.load(Ordering::Acquire), + "old prepared work survived into required recovery" + ); + use crate::storage::maintenance_scheduler::MaintenanceAdmission; + let dispatcher = Arc::clone(&self.dispatcher); + let entered = dispatcher.run_recovery(&mut || { + assert!( + dispatcher.active(), + "required recovery bypassed actual execution admission" + ); + assert!(dispatcher.try_submit(maintenance_test_job()).is_err()); + self.recovered.store(true, Ordering::Release); + Ok(()) + })?; + assert!(entered); + Ok(outcome) + } + } + #[test] + fn dc_joined_driver_advisory_finish_allows_real_requests_then_drops_work_before_recovery() { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + let (entered, enter_rx) = mpsc::channel(); + let (release_tx, release) = mpsc::channel(); + let disposed = Arc::new(AtomicBool::new(false)); + let recovered = Arc::new(AtomicBool::new(false)); + let driver_disposed = Arc::clone(&disposed); + let driver_recovered = Arc::clone(&recovered); + let driver_dispatcher = Arc::clone(&dispatcher); + let mut runtime = MaintenanceRuntime::start( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([7; 32]), + Arc::clone(&dispatcher), + move |_| { + Ok(AdvisoryFinishDriver { + entered, + release, + disposed: driver_disposed, + dispatcher: driver_dispatcher, + recovered: driver_recovered, + }) + }, + ) + .unwrap(); + enter_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + assert!(!dispatcher.active()); + real_request_progresses(&dispatcher); + assert!(runtime.handle().snapshot().last_attempt.is_none()); + assert!(!disposed.load(Ordering::Acquire)); + let handle = runtime.handle(); + let waiter = thread::spawn(move || handle.wait_for_first_attempt()); + release_tx.send(()).unwrap(); + let completed = waiter.join().unwrap(); + assert_eq!( + completed.last_attempt.unwrap().outcome, + AttemptOutcome::Complete + ); + assert!(disposed.load(Ordering::Acquire)); + assert!(recovered.load(Ordering::Acquire)); + runtime.stop_and_join(); + assert!(!dispatcher.active()); + } + + #[test] + fn dc_joined_driver_rebind_and_stop_during_preparation_prevent_mutation() { + for stop in [false, true] { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + let (prepare_entered, prepared) = mpsc::channel(); + let (release_prepare, prepare_release) = mpsc::channel(); + let (dispose_entered, disposing) = mpsc::channel(); + let (release_dispose, dispose_release) = mpsc::channel(); + let (confirmations, confirmation) = mpsc::channel(); + let mut runtime = MaintenanceRuntime::start( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + dispatcher.clone(), + move |_| { + Ok(Driver { + prepare_entered, + prepare_release, + disposal: Some((dispose_entered, dispose_release)), + confirmations, + mode: 0, + _not_send: Rc::new(()), + }) + }, + ) + .unwrap(); + prepared.recv_timeout(Duration::from_secs(5)).unwrap(); + let handle = runtime.handle(); + assert_eq!( + handle.request(Trigger::Health, Some(TargetBinding([9; 32]))), + RequestDisposition::UnboundConfirmation + ); + assert_eq!( + handle.request(Trigger::Health, Some(TargetBinding([1; 32]))), + RequestDisposition::Enqueued + ); + assert_eq!( + handle.rebind(TargetBinding([1; 32])), + RequestDisposition::Enqueued + ); + assert_eq!(handle.snapshot().generation, 0); + if stop { + runtime.request_stop(); + } else { + handle.rebind(TargetBinding([2; 32])); + } + release_prepare.send(()).unwrap(); + disposing.recv_timeout(Duration::from_secs(5)).unwrap(); + assert!(confirmation.try_recv().is_err()); + if !stop { + real_request_progresses(&dispatcher); + } + release_dispose.send(()).unwrap(); + runtime.stop_and_join(); + assert!(!dispatcher.active()); + assert!(handle + .snapshot() + .last_attempt + .is_none_or(|record| record.generation == handle.snapshot().generation)); + } + } + + #[test] + fn dc_joined_driver_start_limit_and_factory_failure_are_truthful() { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + assert!(MaintenanceRuntime::start::( + CleanupMemory::new(128).unwrap(), + TargetBinding([1; 32]), + dispatcher.clone(), + |_| unreachable!() + ) + .is_err()); + let mut runtime = MaintenanceRuntime::start::( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + dispatcher, + |_| Err(DriverError::ResourceLimit), + ) + .unwrap(); + let handle = runtime.handle(); + let deadline = Instant::now() + Duration::from_secs(5); + while handle.snapshot().liveness != DriverLiveness::Failed { + assert!(Instant::now() < deadline); + thread::yield_now(); + } + assert_eq!(handle.snapshot().failure, Some(DriverError::ResourceLimit)); + assert_eq!( + handle.request(Trigger::Health, None), + RequestDisposition::Failed + ); + runtime.stop_and_join(); + } + + struct GateAdmission { + dispatcher: Arc, + acquired: Sender<()>, + release: Mutex>, + } + impl crate::storage::maintenance_scheduler::MaintenanceAdmission for GateAdmission { + type Permit = MaintenanceExecutionPermit; + fn try_acquire(&self) -> Option { + let permit = self.dispatcher.try_acquire_maintenance()?; + self.acquired.send(()).unwrap(); + self.release.lock().unwrap().recv().unwrap(); + Some(permit) + } + fn stop_admission(&self) { + self.dispatcher.stop_admission(); + } + } + + #[test] + fn dc_joined_driver_stop_or_rebind_winning_postpermit_gate_has_no_mutation() { + for stop in [false, true] { + let dispatcher = Arc::new(ExecutionDispatcher::default()); + let (prepare_entered, prepared) = mpsc::channel(); + let (release_prepare, prepare_release) = mpsc::channel(); + let (dispose_entered, disposing) = mpsc::channel(); + let (release_dispose, dispose_release) = mpsc::channel(); + let (confirmations, confirmation) = mpsc::channel(); + let (acquired, acquired_rx) = mpsc::channel(); + let (release_gate, release) = mpsc::channel(); + let mut runtime = MaintenanceRuntime::start( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + GateAdmission { + dispatcher: dispatcher.clone(), + acquired, + release: Mutex::new(release), + }, + move |_| { + Ok(Driver { + prepare_entered, + prepare_release, + disposal: Some((dispose_entered, dispose_release)), + confirmations, + mode: 0, + _not_send: Rc::new(()), + }) + }, + ) + .unwrap(); + prepared.recv_timeout(Duration::from_secs(5)).unwrap(); + release_prepare.send(()).unwrap(); + acquired_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + assert!(dispatcher.active()); + assert!(dispatcher.try_submit(maintenance_test_job()).is_err()); + if stop { + runtime.request_stop(); + } else { + runtime.handle().rebind(TargetBinding([2; 32])); + } + release_gate.send(()).unwrap(); + disposing.recv_timeout(Duration::from_secs(5)).unwrap(); + assert!(!dispatcher.active()); + assert!(confirmation.try_recv().is_err()); + if !stop { + real_request_progresses(&dispatcher); + } + release_dispose.send(()).unwrap(); + runtime.stop_and_join(); + } + } +} + +mod refresh_owned_join { + use super::*; + use std::sync::mpsc; + + struct Release(Option>); + impl Release { + fn release(&mut self) { + if let Some(tx) = self.0.take() { + let _ = tx.send(()); + } + } + } + impl Drop for Release { + fn drop(&mut self) { + self.release(); + } + } + + struct Payload { + entered: mpsc::Sender<()>, + release: mpsc::Receiver<()>, + panic_again: bool, + } + impl Drop for Payload { + fn drop(&mut self) { + let _ = self.entered.send(()); + let _ = self.release.recv(); + if self.panic_again { + panic!("fixture join payload destructor"); + } + } + } + #[test] + fn refresh_join_two_drainers_wait_for_payload_drop_and_wake_after_unwind() { + for panic_again in [false, true] { + let (entered, disposing) = mpsc::channel(); + let (release, released) = mpsc::channel(); + let target = thread::spawn(move || { + std::panic::panic_any(Payload { + entered, + release: released, + panic_again, + }) + }); + let joined = Arc::new(JoinedThread::new(Some(target))); + let mut release = Release(Some(release)); + let first = joined.clone(); + let (first_tx, first_rx) = mpsc::channel(); + let first = thread::spawn(move || { + let result = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| first.join())); + first_tx.send(result.is_ok()).unwrap(); + }); + disposing.recv_timeout(Duration::from_secs(5)).unwrap(); + assert!( + joined.state.try_lock().is_ok(), + "mutex held during join/payload drop" + ); + assert!(matches!( + *joined.state.lock().unwrap(), + JoinState::Joining(_) + )); + let second = joined.clone(); + let (second_tx, second_rx) = mpsc::channel(); + let second = thread::spawn(move || second_tx.send(second.join()).unwrap()); + assert!( + second_rx.recv_timeout(Duration::from_millis(50)).is_err(), + "concurrent drain returned before disposal" + ); + assert!(first_rx.try_recv().is_err()); + release.release(); + assert_eq!( + first_rx.recv_timeout(Duration::from_secs(5)).unwrap(), + !panic_again + ); + assert!(second_rx.recv_timeout(Duration::from_secs(5)).unwrap()); + first.join().unwrap(); + second.join().unwrap(); + assert!(matches!(*joined.state.lock().unwrap(), JoinState::Joined)); + } + } + + #[test] + fn refresh_join_self_identity_survives_handle_taken_by_another_drainer() { + let joined = Arc::new(JoinedThread::new(None)); + let weak = Arc::downgrade(&joined); + let (release, released) = mpsc::channel(); + let (result, result_rx) = mpsc::channel(); + let target = thread::spawn(move || { + let _ = released.recv(); + result.send(weak.upgrade().unwrap().join()).unwrap(); + }); + joined.install(target); + let mut release = Release(Some(release)); + let drainer = joined.clone(); + let drainer = thread::spawn(move || assert!(drainer.join())); + let deadline = Instant::now() + Duration::from_secs(5); + while !matches!(*joined.state.lock().unwrap(), JoinState::Joining(_)) { + assert!(Instant::now() < deadline); + thread::yield_now(); + } + release.release(); + assert!(!result_rx.recv_timeout(Duration::from_secs(5)).unwrap()); + drainer.join().unwrap(); + assert!(joined.join()); + } + + fn await_join_transition(joined: &JoinedThread) { + let deadline = Instant::now() + Duration::from_secs(5); + loop { + // Condition synchronization only: the actual unlocked-join probe + // below executes once, after admission's short guard has gone. + if let Ok(state) = joined.state.try_lock() { + if matches!(*state, JoinState::Joining(_)) { + break; + } + } + assert!( + Instant::now() < deadline, + "drainer did not release its joining transition" + ); + thread::yield_now(); + } + } + + #[test] + fn refresh_owner_and_monitor_release_state_and_route_mutexes_before_join() { + let stop = Arc::new(AtomicBool::new(false)); + let owner = Arc::new(CoordinatorOwner { + stop: stop.clone(), + thread: JoinedThread::new(None), + dispatcher: Arc::new(ExecutionDispatcher::default()), + refresh_cancel: RefreshCancellation::default(), + }); + let weak = Arc::downgrade(&owner); + let (observed, observed_rx) = mpsc::channel(); + let target = thread::spawn(move || { + while !stop.load(Ordering::Acquire) { + thread::yield_now(); + } + let owner = weak.upgrade().unwrap(); + await_join_transition(&owner.thread); + observed + .send(owner.thread.state.try_lock().is_ok()) + .unwrap(); + }); + owner.thread.install(target); + owner.stop_and_drain(); + assert!(observed_rx.recv_timeout(Duration::from_secs(5)).unwrap()); + + let root = temp_dir("monitor-unlocked-drain"); + let client = test_client_with_state(&root, test_state("127.0.0.1:1".parse().unwrap())); + client.inner.monitor_stop.store(false, Ordering::Release); + let weak = Arc::downgrade(&client.inner); + let stop = client.inner.monitor_stop.clone(); + let (observed, observed_rx) = mpsc::channel(); + let target = thread::spawn(move || { + while !stop.load(Ordering::Acquire) { + thread::yield_now(); + } + let inner = weak.upgrade().unwrap(); + await_join_transition(&inner.monitor); + observed + .send(( + inner.monitor.state.try_lock().is_ok(), + inner.route.try_lock().is_ok(), + )) + .unwrap(); + }); + client.inner.monitor.install(target); + client.drain_owned_operation(); + assert_eq!( + observed_rx.recv_timeout(Duration::from_secs(5)).unwrap(), + (true, true) + ); + } + + #[test] + fn refresh_monitor_final_self_drop_exits_before_another_wait_or_work() { + let root = temp_dir("monitor-self-drop-tail"); + let owner_stop = Arc::new(AtomicBool::new(false)); + let server_stop = owner_stop.clone(); + let (server_done, server_drained) = mpsc::channel(); + let server = thread::spawn(move || { + while !server_stop.load(Ordering::Acquire) { + thread::yield_now(); + } + server_done.send(()).unwrap(); + }); + let client = test_client_with_owner( + &root, + test_state("127.0.0.1:1".parse().unwrap()), + owner_stop, + Arc::new(ExecutionDispatcher::default()), + server, + ); + client.inner.monitor_stop.store(false, Ordering::Release); + let stop = client.inner.monitor_stop.clone(); + let weak = Arc::downgrade(&client.inner); + let probe_weak = weak.clone(); + let (observed, observation) = mpsc::channel(); + let (release, released) = mpsc::channel(); + let waits = Arc::new(AtomicU64::new(0)); + let probe_waits = waits.clone(); + let (done, drained) = mpsc::channel(); + let monitor = thread::spawn(move || { + monitor_route_with_probe(probe_weak, stop, |phase| match phase { + MonitorPhase::BeforeWait => { + probe_waits.fetch_add(1, Ordering::SeqCst); + } + MonitorPhase::Observed => { + observed.send(()).unwrap(); + let _ = released.recv(); + } + }); + done.send(()).unwrap(); + }); + client.inner.monitor.install(monitor); + let mut release = Release(Some(release)); + observation.recv_timeout(Duration::from_secs(5)).unwrap(); + drop(client); // monitor's iteration now owns the last strong reference + release.release(); + drained.recv_timeout(Duration::from_secs(5)).unwrap(); + server_drained.recv_timeout(Duration::from_secs(5)).unwrap(); + assert!(weak.upgrade().is_none()); + assert_eq!(waits.load(Ordering::SeqCst), 1); + } + + #[test] + fn refresh_owner_executor_start_failure_and_stop_before_api_start_join_refresh() { + for stop_before_api in [false, true] { + let root = temp_dir("owner-refresh-startup"); + let control = CoordinatorControlPaths { + lock: root.join("coordinator.lock"), + state: root.join("coordinator.json"), + managed_initialization: None, + direct_initialization: None, + }; + let lease = try_open_locked(&control.lock, LockMode::Exclusive) + .unwrap() + .unwrap(); + let mut config = direct_config(&root); + config.identity = Some(RepositoryIdentity::capture(&config.selector).unwrap()); + config.refresh = Some(RefreshServiceConfig::default()); + let (before, before_rx) = mpsc::channel(); + let (release, released) = mpsc::channel(); + let (started, started_rx) = mpsc::channel(); + let (_, owner) = + start_owner_with_probe(control.clone(), lease, config, move |stage| match stage { + OwnerStartup::BeforeRefresh => { + if stop_before_api { + before.send(()).unwrap(); + let _ = released.recv(); + } + } + OwnerStartup::RefreshStarted(state) => { + INJECT_EXECUTOR_SPAWN_FAILURE.with(|flag| flag.set(true)); + started.send(state.unwrap()).unwrap(); + } + }) + .unwrap(); + let mut release = Release(Some(release)); + if stop_before_api { + before_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + // This is the exact owner token created before its serving thread. + owner.stop.store(true, Ordering::Release); + owner.refresh_cancel.request_stop(); + owner.dispatcher.stop_admission(); + release.release(); + } + let refresh = started_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + owner.stop_and_drain(); + assert!(!refresh.snapshot().task_alive); + assert!( + !control.state.exists(), + "serving state survived completed cleanup" + ); + assert!( + try_open_locked(&control.lock, LockMode::Exclusive) + .unwrap() + .is_some(), + "coordinator lease escaped drain" + ); + if stop_before_api { + assert_eq!(refresh.snapshot().effective_root, None); + } + } + } +} diff --git a/src/db_writer/access.rs b/src/db_writer/access.rs index 0c3d9cfa..d3360931 100644 --- a/src/db_writer/access.rs +++ b/src/db_writer/access.rs @@ -1,6 +1,12 @@ use crate::error::NativeError; +use crate::storage::maintenance_ownership::DatabaseUseLease; use lbug::{Connection, Database, SystemConfig}; -use std::{path::Path, thread, time::Duration}; +use std::{ + ops::Deref, + path::{Component, Path, PathBuf}, + thread, + time::Duration, +}; #[derive(Clone, Copy, Debug)] pub struct RetryPolicy { @@ -23,7 +29,6 @@ pub const READ_RETRY_POLICY: RetryPolicy = RetryPolicy::new(3, Duration::from_millis(40), Duration::from_millis(160)); pub const WRITE_RETRY_POLICY: RetryPolicy = RetryPolicy::new(8, Duration::from_millis(100), Duration::from_millis(1_000)); -const DEFAULT_READ_BUFFER_POOL_BYTES: u64 = 128 * 1024 * 1024; const DEFAULT_READ_THREADS: u64 = 2; pub fn is_transient_database_error(message: &str) -> bool { @@ -62,33 +67,259 @@ pub fn retry_transient_database( unreachable!("retry loop always returns") } -pub fn open_ladybug_database(db_path: &Path, read_only: bool) -> Result { - open_ladybug_database_with_limits( - db_path, - read_only, - DEFAULT_READ_BUFFER_POOL_BYTES, - DEFAULT_READ_THREADS, - ) +/// A native database and its physical-resource exclusion, owned together. +/// +/// Connections borrow the native handle through this wrapper, so safe Rust +/// cannot release the exclusion while a connection remains in use. +/// +/// ```no_run +/// use codebase_graph::db_writer::{connect_ladybug_database, open_ladybug_database}; +/// let database = open_ladybug_database(std::path::Path::new("graph.lbug"), true)?; +/// let connection = connect_ladybug_database(&database)?; +/// connection.query("RETURN 1")?; +/// drop(connection); +/// drop(database); +/// # Ok::<(), Box>(()) +/// ``` +/// +/// ```compile_fail +/// use codebase_graph::db_writer::{connect_ladybug_database, open_ladybug_database}; +/// let database = open_ladybug_database(std::path::Path::new("graph.lbug"), true).unwrap(); +/// let connection = connect_ladybug_database(&database).unwrap(); +/// drop(database); +/// connection.query("RETURN 1").unwrap(); +/// ``` +/// +/// ```compile_fail +/// use codebase_graph::db_writer::open_ladybug_database; +/// let database = open_ladybug_database(std::path::Path::new("graph.lbug"), true).unwrap(); +/// let native: lbug::Database = *database; +/// ``` +#[derive(Debug)] +pub struct GuardedDatabase { + // Rust drops fields in declaration order: close the native database before + // releasing either family or resource exclusion, including during unwind. + database: Database, + _lease: DatabaseUseLease, } -pub fn open_ladybug_database_with_limits( +impl Deref for GuardedDatabase { + type Target = Database; + + fn deref(&self) -> &Self::Target { + &self.database + } +} + +pub fn open_ladybug_database( + db_path: &Path, + read_only: bool, +) -> Result { + open_ladybug_database_with_threads(db_path, read_only, DEFAULT_READ_THREADS) +} + +pub fn open_ladybug_database_with_threads( db_path: &Path, read_only: bool, - buffer_pool_bytes: u64, max_num_threads: u64, -) -> Result { +) -> Result { let config = SystemConfig::default() .read_only(read_only) - .buffer_pool_size(buffer_pool_bytes) .max_num_threads(max_num_threads); - Database::new(db_path, config).map_err(|error| { + open_ladybug_database_with_config(db_path, config) +} + +pub(crate) fn open_ladybug_database_with_config( + db_path: &Path, + config: SystemConfig, +) -> Result { + // Acquire before native open, including absent-to-created databases. An + // open error drops the lease; successful construction moves it with the DB. + let native_path = guarded_native_path(db_path)?; + let lease = DatabaseUseLease::acquire_shared(&native_path)?; + #[cfg(windows)] + ensure_windows_native_encoding(&native_path)?; + let database = Database::new(&native_path, config).map_err(|error| { NativeError::Database(format!( "failed to open graph database {}: {error}", db_path.display() )) + })?; + Ok(GuardedDatabase { + database, + _lease: lease, }) } +/// Bind the resource using the same local, absolute UTF-8 spelling sent to +/// Ladybug. Its Rust SDK converts paths through `display()`, and its pinned +/// native StorageUtils::expandPath expands a leading '~' and lexically removes +/// '..'. Reject those ambiguous forms before creating any coordination files; +/// resolving once also keeps guard and native open independent of later CWD +/// changes. This does not change persisted Direct writer/journal addresses. +pub(super) fn guarded_native_path(db_path: &Path) -> Result { + let spelling = db_path + .to_str() + .ok_or_else(|| NativeError::InvalidInput("database_path_not_utf8".into()))?; + if spelling.is_empty() + || spelling.contains('\0') + || spelling == ":memory:" + || spelling.starts_with('~') + { + return Err(NativeError::InvalidInput( + "database_path_requires_literal_disk_resource".into(), + )); + } + if db_path + .components() + .any(|part| matches!(part, Component::ParentDir)) + { + return Err(NativeError::InvalidInput( + "database_path_parent_traversal_unsupported".into(), + )); + } + // A terminal separator or '/.' denotes a directory to the native opener. + // Do not normalize it into a different, valid database filename. + if spelling.ends_with('/') || spelling.ends_with("/.") || spelling == "." { + return Err(NativeError::InvalidInput( + "database_path_requires_file_name".into(), + )); + } + #[cfg(windows)] + { + use std::path::Prefix; + if spelling.ends_with('\\') || spelling.ends_with("\\.") { + return Err(NativeError::InvalidInput( + "database_path_requires_file_name".into(), + )); + } + if db_path.components().any(|part| match part { + Component::Prefix(prefix) => !matches!( + prefix.kind(), + Prefix::Disk(_) + | Prefix::UNC(_, _) + | Prefix::VerbatimDisk(_) + | Prefix::VerbatimUNC(_, _) + ), + _ => false, + }) || (!db_path.is_absolute() && db_path.has_root()) + || (!db_path.is_absolute() + && matches!(db_path.components().next(), Some(Component::Prefix(_)))) + { + return Err(NativeError::InvalidInput( + "database_path_prefix_unsupported".into(), + )); + } + } + let absolute = if db_path.is_absolute() { + db_path.to_path_buf() + } else { + std::env::current_dir()?.join(db_path) + }; + if absolute.to_str().is_none() || !absolute.is_absolute() { + return Err(NativeError::InvalidInput( + "database_path_absolute_utf8_required".into(), + )); + } + #[cfg(windows)] + { + let native_path = normalized_windows_native_path(absolute)?; + ensure_windows_native_encoding(&native_path)?; + return Ok(native_path); + } + #[cfg(not(windows))] + { + // ParentDir is excluded above. Removing CurDir/repeated separators + // agrees with native lexical normalization without erasing symlink/.. + // proof. + Ok(absolute.components().collect()) + } +} + +#[cfg(windows)] +fn normalized_windows_native_path(absolute: PathBuf) -> Result { + use std::path::Prefix; + let Some(Component::Prefix(prefix)) = absolute.components().next() else { + return Err(NativeError::InvalidInput( + "database_path_prefix_unsupported".into(), + )); + }; + let spelling = absolute + .to_str() + .ok_or_else(|| NativeError::InvalidInput("database_path_absolute_utf8_required".into()))?; + match prefix.kind() { + Prefix::VerbatimDisk(_) | Prefix::VerbatimUNC(_, _) => { + // fs::canonicalize returns these ordinary Windows filesystem + // namespaces. Preserve them exactly; removing the prefix changes + // length/parsing semantics. Reject tails native lexical cleanup + // would reinterpret, including '/' (literal in verbatim paths). + let tail = &spelling[prefix.as_os_str().len()..]; + if spelling.contains('/') + || !tail.starts_with('\\') + || tail[1..] + .split('\\') + .any(|part| part.is_empty() || part == "." || part == "..") + { + return Err(NativeError::InvalidInput( + "database_path_verbatim_normalization_unsupported".into(), + )); + } + Ok(absolute) + } + Prefix::Disk(_) | Prefix::UNC(_, _) => { + // Ordinary Win32 paths normalize these components differently from + // extended namespaces; do not rely on wide/narrow API equivalence. + if absolute.components().any(|part| matches!(part, + Component::Normal(name) if name.to_str().map_or(true, |name| name.ends_with('.') || name.ends_with(' ')))) + { + return Err(NativeError::InvalidInput("database_path_component_normalization_unsupported".into())); + } + Ok(absolute.components().collect()) + } + _ => Err(NativeError::InvalidInput( + "database_path_prefix_unsupported".into(), + )), + } +} + +// CP_UTF8 is 65001. This byte-policy helper is tested without changing the +// process-global ANSI/OEM FileAPI mode. The whole absolute path participates. +#[cfg(any(windows, test))] +pub(super) fn windows_native_encoding_matches(spelling: &str, effective_code_page: u32) -> bool { + spelling.is_ascii() || effective_code_page == 65001 +} + +#[cfg(windows)] +pub(super) fn windows_effective_file_api_code_page() -> u32 { + use windows_sys::Win32::{ + Globalization::{GetACP, GetOEMCP}, + Storage::FileSystem::AreFileApisANSI, + }; + // lbug 0.19's LocalFileSystem calls CreateFileA, whose process FileAPI + // ANSI/OEM mode determines narrow filename decoding. Console code pages + // and Rust's wide filesystem calls do not establish this equivalence. + unsafe { + if AreFileApisANSI() != 0 { + GetACP() + } else { + GetOEMCP() + } + } +} + +#[cfg(windows)] +fn ensure_windows_native_encoding(path: &Path) -> Result<(), NativeError> { + let spelling = path + .to_str() + .ok_or_else(|| NativeError::InvalidInput("database_path_not_utf8".into()))?; + if !windows_native_encoding_matches(spelling, windows_effective_file_api_code_page()) { + return Err(NativeError::InvalidInput( + "database_path_windows_native_encoding_unsupported".into(), + )); + } + Ok(()) +} + pub fn connect_ladybug_database(database: &Database) -> Result, NativeError> { Connection::new(database) .map_err(|error| NativeError::Database(format!("failed to connect to graph: {error}"))) diff --git a/src/db_writer/extensions.rs b/src/db_writer/extensions.rs index 21a8766f..73004f46 100644 --- a/src/db_writer/extensions.rs +++ b/src/db_writer/extensions.rs @@ -73,13 +73,7 @@ fn ladybug_home_dir() -> Result { } fn ladybug_platform() -> Option<&'static str> { - if cfg!(all(target_os = "linux", target_arch = "x86_64")) { - Some("linux_amd64") - } else if cfg!(all(target_os = "linux", target_arch = "aarch64")) { - Some("linux_arm64") - } else if cfg!(all(target_os = "macos", target_arch = "x86_64")) { - Some("osx_amd64") - } else if cfg!(all(target_os = "macos", target_arch = "aarch64")) { + if cfg!(all(target_os = "macos", target_arch = "aarch64")) { Some("osx_arm64") } else if cfg!(all(target_os = "windows", target_arch = "x86_64")) { Some("win_amd64") @@ -88,45 +82,6 @@ fn ladybug_platform() -> Option<&'static str> { } } -#[cfg(all(target_os = "linux", target_arch = "x86_64"))] -fn bundled_extension_xz(extension: &str) -> Option<&'static [u8]> { - match extension { - "json" => Some(include_bytes!( - "../../assets/ladybug-extensions/0.19.0/linux_amd64/json/libjson.lbug_extension.xz" - )), - "fts" => Some(include_bytes!( - "../../assets/ladybug-extensions/0.19.0/linux_amd64/fts/libfts.lbug_extension.xz" - )), - _ => None, - } -} - -#[cfg(all(target_os = "linux", target_arch = "aarch64"))] -fn bundled_extension_xz(extension: &str) -> Option<&'static [u8]> { - match extension { - "json" => Some(include_bytes!( - "../../assets/ladybug-extensions/0.19.0/linux_arm64/json/libjson.lbug_extension.xz" - )), - "fts" => Some(include_bytes!( - "../../assets/ladybug-extensions/0.19.0/linux_arm64/fts/libfts.lbug_extension.xz" - )), - _ => None, - } -} - -#[cfg(all(target_os = "macos", target_arch = "x86_64"))] -fn bundled_extension_xz(extension: &str) -> Option<&'static [u8]> { - match extension { - "json" => Some(include_bytes!( - "../../assets/ladybug-extensions/0.19.0/osx_amd64/json/libjson.lbug_extension.xz" - )), - "fts" => Some(include_bytes!( - "../../assets/ladybug-extensions/0.19.0/osx_amd64/fts/libfts.lbug_extension.xz" - )), - _ => None, - } -} - #[cfg(all(target_os = "macos", target_arch = "aarch64"))] fn bundled_extension_xz(extension: &str) -> Option<&'static [u8]> { match extension { @@ -158,9 +113,6 @@ fn bundled_extension_xz(extension: &str) -> Option<&'static [u8]> { } #[cfg(not(any( - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - all(target_os = "macos", target_arch = "x86_64"), all(target_os = "macos", target_arch = "aarch64"), all( target_os = "windows", diff --git a/src/db_writer/mod.rs b/src/db_writer/mod.rs index b5effb86..36636c83 100644 --- a/src/db_writer/mod.rs +++ b/src/db_writer/mod.rs @@ -6,14 +6,16 @@ mod rss; mod schema; mod write; +pub(crate) use access::open_ladybug_database_with_config; pub use access::{ connect_ladybug_database, is_transient_database_error, open_ladybug_database, - open_ladybug_database_with_limits, retry_transient_database, READ_RETRY_POLICY, - WRITE_RETRY_POLICY, + open_ladybug_database_with_threads, retry_transient_database, GuardedDatabase, + READ_RETRY_POLICY, WRITE_RETRY_POLICY, }; pub use extensions::preseed_ladybug_extensions; pub(crate) use phase::{ - execute_phase_file, register_phase_worker_executable, with_phase_workspace_root, + execute_phase_file, phase_has_parent_workspace, register_phase_worker_executable, + with_phase_workspace_scope, }; pub use request::{LadybugWriteMetrics, LadybugWriteRequest}; pub(crate) use rss::sample_process_rss; diff --git a/src/db_writer/phase.rs b/src/db_writer/phase.rs index a86b3f99..0e9a9b92 100644 --- a/src/db_writer/phase.rs +++ b/src/db_writer/phase.rs @@ -1,12 +1,12 @@ use super::rss::sample_process_rss; -use super::{connect_ladybug_database, open_ladybug_database_with_limits}; -use crate::error::{MemoryBudgetExceeded, NativeError}; +use super::{connect_ladybug_database, open_ladybug_database_with_threads}; +use crate::error::NativeError; use crate::process_lifecycle::{self, OutputMode, PreparedChild, SpawnSpec}; use lbug::Connection; use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::fs::{self, OpenOptions}; -use std::io::{BufReader, BufWriter, Write}; +use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::atomic::{AtomicU64, Ordering}; @@ -14,13 +14,12 @@ use std::sync::OnceLock; const PHASE_PROTOCOL_VERSION: u64 = 2; const MAX_CHILD_ERROR_BYTES: usize = 64 * 1024; -const MEMORY_HEADROOM_BYTES: u64 = 16 * 1024 * 1024; -const RSS_SAMPLE_INTERVAL: std::time::Duration = std::time::Duration::from_millis(25); static PHASE_WORKER_EXECUTABLE: OnceLock = OnceLock::new(); static PHASE_SEQUENCE: AtomicU64 = AtomicU64::new(0); thread_local! { static PHASE_WORKSPACE_ROOT: RefCell> = const { RefCell::new(None) }; + static PHASE_PARENT_RUN: RefCell> = const { RefCell::new(None) }; } // Synchronous API execution owns this scope. Keep it local to the calling @@ -39,12 +38,30 @@ pub(crate) fn with_phase_workspace_root(root: PathBuf, execute: impl FnOnce() execute() } +pub(crate) fn phase_has_parent_workspace() -> bool { + PHASE_PARENT_RUN.with(|slot| slot.borrow().is_some()) +} + +struct PhaseParentScope(Option); +impl Drop for PhaseParentScope { + fn drop(&mut self) { + PHASE_PARENT_RUN.with(|slot| *slot.borrow_mut() = self.0.take()); + } +} +pub(crate) fn with_phase_workspace_scope( + root: PathBuf, + parent: Option, + execute: impl FnOnce() -> T, +) -> T { + let previous = PHASE_PARENT_RUN.with(|slot| slot.replace(parent)); + let _parent = PhaseParentScope(previous); + with_phase_workspace_root(root, execute) +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub(super) struct LadybugWritePhaseRequest { version: u64, db_path: PathBuf, - worker_memory_bytes: u64, - buffer_pool_bytes: u64, max_num_threads: u64, phase: LadybugWritePhase, } @@ -73,16 +90,12 @@ pub(super) enum LadybugWritePhase { impl LadybugWritePhaseRequest { pub(super) fn new( db_path: impl Into, - worker_memory_bytes: u64, - buffer_pool_bytes: u64, max_num_threads: u64, phase: LadybugWritePhase, ) -> Self { Self { version: PHASE_PROTOCOL_VERSION, db_path: db_path.into(), - worker_memory_bytes, - buffer_pool_bytes, max_num_threads, phase, } @@ -98,6 +111,8 @@ pub(super) fn phase_worker_available() -> bool { } pub(super) fn run_isolated_phase(request: &LadybugWritePhaseRequest) -> Result { + let _database_use = + crate::storage::maintenance_ownership::DatabaseUseLease::acquire_shared(&request.db_path)?; let executable = PHASE_WORKER_EXECUTABLE.get().ok_or_else(|| { NativeError::InvalidInput("Ladybug phase worker executable is not registered".to_string()) })?; @@ -123,13 +138,23 @@ pub(super) fn run_isolated_phase(request: &LadybugWritePhaseRequest) -> Result Result Result Result break status, @@ -178,55 +204,30 @@ pub(super) fn run_isolated_phase(request: &LadybugWritePhaseRequest) -> Result { let _ = child.kill(); let _ = child.wait(); - // The wrapper may have a surviving native child. The outer - // build must return this error so its guardian cleans the tree. return Err(NativeError::Database(format!( "failed to supervise Ladybug phase: {error}; retain {} until build containment is empty", request_path.display() ))); } } - let child_rss = sample_process_rss(child.id()).unwrap_or(0); - let parent_rss = sample_process_rss(std::process::id()).unwrap_or(0); - let observed = child_rss.saturating_add(parent_rss); + let observed = sample_process_rss(child.id()) + .unwrap_or(0) + .saturating_add(sample_process_rss(std::process::id()).unwrap_or(0)); high_water_bytes = high_water_bytes.max(observed); - if observed > kill_threshold { - let _ = child.kill(); - let _ = child.wait(); - // Retain artifacts until the root guardian proves the whole build - // empty; killing a timing wrapper alone cannot prove that. - return Err(NativeError::MemoryBudgetExceeded( - MemoryBudgetExceeded::new( - phase_memory_label(request), - request.worker_memory_bytes, - observed, - observed, - ) - .with_process_rss(parent_rss, child_rss), - )); - } - std::thread::sleep(RSS_SAMPLE_INTERVAL); + std::thread::sleep(std::time::Duration::from_millis(25)); }; - let stderr = fs::read(&stderr_path).unwrap_or_default(); if !status.success() { - let stderr = bounded_child_error(&stderr); - return Err(NativeError::Database(format!( - "isolated Ladybug write phase exited with {}: {stderr}", - status + let stderr = read_error_tail(&stderr_path).unwrap_or_default(); + return Err(NativeError::Database(isolated_phase_error( + request, + &status.to_string(), + &stderr, ))); } cleanup_phase_files(&request_path, &stderr_path); Ok(high_water_bytes) } -fn phase_memory_label(request: &LadybugWritePhaseRequest) -> String { - format!( - "ladybug_{}_pool_{}_mib", - request.phase.trace_label(), - request.buffer_pool_bytes / (1024 * 1024) - ) -} - fn run_guarded_phase( request: &LadybugWritePhaseRequest, executable: &Path, @@ -242,8 +243,6 @@ fn run_guarded_phase( workspace: workspace.clone(), stderr_path: stderr_path.to_path_buf(), stdout: OutputMode::Null, - memory_limit_bytes: Some(request.worker_memory_bytes), - memory_phase: Some(phase_memory_label(request)), parent_rss_pid: Some(std::process::id()), }; let mut child = PreparedChild::prepare(spec).map_err(NativeError::Database)?; @@ -254,7 +253,7 @@ fn run_guarded_phase( .is_ok_and(|outcome| outcome.containment_empty) { cleanup_phase_files(request_path, stderr_path); - let _ = fs::remove_dir_all(&workspace); + cleanup_phase_workspace(&workspace); } return Err(NativeError::Database(format!( "failed to start guarded Ladybug phase: {error}; cleanup: {cleanup:?}" @@ -271,39 +270,57 @@ fn run_guarded_phase( .is_ok_and(|outcome| outcome.containment_empty) { cleanup_phase_files(request_path, stderr_path); - let _ = fs::remove_dir_all(&workspace); + cleanup_phase_workspace(&workspace); } return Err(NativeError::Database(format!( "failed to supervise guarded Ladybug phase: {error}; cleanup: {cleanup:?}" ))); } }; - let stderr = fs::read(stderr_path).unwrap_or_default(); if !outcome.containment_empty { return Err(NativeError::Database(format!( "{outcome}; retain {} until containment is proven empty", workspace.display() ))); } - cleanup_phase_files(request_path, stderr_path); - let _ = fs::remove_dir_all(&workspace); - if let Some(reason) = &outcome.budget_reason { - let failure: MemoryBudgetExceeded = serde_json::from_str(reason).map_err(|error| { - NativeError::Database(format!( - "invalid guardian memory failure: {error}: {reason}" - )) - })?; - return Err(NativeError::MemoryBudgetExceeded(failure)); - } if !outcome.success { - return Err(NativeError::Database(format!( - "isolated Ladybug write phase exited with {outcome}: {}", - bounded_child_error(&stderr) + let stderr = read_error_tail(stderr_path).unwrap_or_default(); + cleanup_phase_files(request_path, stderr_path); + cleanup_phase_workspace(&workspace); + return Err(NativeError::Database(isolated_phase_error( + request, + &outcome.to_string(), + &stderr, ))); } + cleanup_phase_files(request_path, stderr_path); + cleanup_phase_workspace(&workspace); Ok(outcome.high_water_bytes) } +fn cleanup_phase_workspace(workspace: &Path) { + // A registered root is disposed only through the bounded local lane. + if !workspace.join(".workspace.binding").exists() { + let _ = fs::remove_dir_all(workspace); + } +} + +struct PhaseWorkspaceOwner { + registration: crate::storage::maintenance_controls::WorkspaceRegistration, + owner: crate::storage::locks::LockedFile, + mutation: crate::storage::maintenance_ownership::ReferenceMutation<'static>, + _parent: crate::storage::run_workspace::ParentRunScope, +} +impl PhaseWorkspaceOwner { + fn finish_if_empty(self) { + let _ = self.registration.terminal( + &self.owner, + &self.mutation, + crate::storage::maintenance_controls::WorkspaceDisposition::AbortedNotPublished, + ); + } +} + fn cleanup_phase_files(request_path: &Path, stderr_path: &Path) { let _ = fs::remove_file(request_path); let _ = fs::remove_file(stderr_path); @@ -341,9 +358,111 @@ pub(crate) fn execute_phase_file(path: &Path) -> Result<(), String> { let request: LadybugWritePhaseRequest = serde_json::from_reader(BufReader::new(file)) .map_err(|error| format!("failed to parse Ladybug phase request: {error}"))?; validate_phase_protocol(request.version)?; + let _database_use = + crate::storage::maintenance_ownership::DatabaseUseLease::acquire_shared(&request.db_path) + .map_err(|error| error.to_string())?; #[cfg(debug_assertions)] wait_for_test_phase_barrier(path)?; - execute_phase(&request).map_err(|error| error.to_string()) + execute_phase(&request).map_err(|error| error.to_string())?; + #[cfg(debug_assertions)] + record_test_phase_result(&request)?; + Ok(()) +} + +#[cfg(debug_assertions)] +fn copy_source_metadata(statement: &str) -> Option<(String, u64)> { + let source = statement.split_once(" FROM ")?.1.trim_start(); + let quote = source.chars().next()?; + if quote != '\"' && quote != '\'' { + return None; + } + let mut value = String::new(); + let mut escaped = false; + for character in source[quote.len_utf8()..].chars() { + if escaped { + value.push(match character { + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + other => other, + }); + escaped = false; + } else if character == '\\' { + escaped = true; + } else if character == quote { + let bytes = fs::metadata(&value).ok()?.len(); + return Some((value, bytes)); + } else { + value.push(character); + } + } + None +} + +#[cfg(debug_assertions)] +fn record_test_phase_result(request: &LadybugWritePhaseRequest) -> Result<(), String> { + let Some(directory) = std::env::var_os("CODEBASE_GRAPH_TEST_COPY_RESULT_DIR") else { + return Ok(()); + }; + let directory = PathBuf::from(directory); + fs::create_dir_all(&directory) + .map_err(|error| format!("failed to create test phase result directory: {error}"))?; + let (phase, copy_index, copy_total, target) = match &request.phase { + LadybugWritePhase::Schema { .. } => ("schema", None, None, None), + LadybugWritePhase::Copy { + index, + total, + statement, + .. + } => ( + "copy", + Some(*index), + Some(*total), + Some(copy_target(statement).to_string()), + ), + LadybugWritePhase::Index { table } => ("index", None, None, Some(table.clone())), + LadybugWritePhase::PostCopy { .. } => ("postcopy", None, None, None), + }; + let mut trace = serde_json::to_value(request) + .map_err(|error| format!("failed to encode test phase request: {error}"))?; + let object = trace + .as_object_mut() + .ok_or_else(|| "test phase request did not encode as an object".to_string())?; + object.insert("phase_kind".into(), serde_json::json!(phase)); + object.insert("copy_index".into(), serde_json::json!(copy_index)); + object.insert("copy_total".into(), serde_json::json!(copy_total)); + object.insert("target".into(), serde_json::json!(target)); + let staged = match &request.phase { + LadybugWritePhase::Copy { statement, .. } => copy_source_metadata(statement), + _ => None, + }; + object.insert( + "staged_file_path".into(), + serde_json::json!(staged.as_ref().map(|(path, _)| path)), + ); + object.insert( + "staged_file_bytes".into(), + serde_json::json!(staged.as_ref().map(|(_, bytes)| bytes)), + ); + object.insert("pid".into(), serde_json::json!(std::process::id())); + object.insert("success".into(), serde_json::json!(true)); + let trace_path = directory.join("phases.jsonl"); + let mut trace_file = OpenOptions::new() + .create(true) + .append(true) + .open(&trace_path) + .map_err(|error| format!("failed to open test phase results: {error}"))?; + writeln!(trace_file, "{trace}") + .map_err(|error| format!("failed to write test phase result: {error}"))?; + if matches!(&request.phase, LadybugWritePhase::Copy { .. }) + && directory.join("FAIL_AFTER_COPY").exists() + { + return Err( + "COPY result acknowledgement failed: Buffer manager exception: the buffer pool is full" + .to_string(), + ); + } + Ok(()) } fn validate_phase_protocol(version: u64) -> Result<(), String> { @@ -385,7 +504,7 @@ fn wait_for_test_phase_barrier(request_path: &Path) -> Result<(), String> { directory.join("CONTINUE").display() )); } - std::thread::sleep(RSS_SAMPLE_INTERVAL); + std::thread::sleep(std::time::Duration::from_millis(25)); } Ok(()) } @@ -432,12 +551,8 @@ fn allocate_test_phase_memory(directory: &Path, pid: u32) -> Result Result<(), NativeError> { - let database = open_ladybug_database_with_limits( - &request.db_path, - false, - request.buffer_pool_bytes, - request.max_num_threads, - )?; + let database = + open_ladybug_database_with_threads(&request.db_path, false, request.max_num_threads)?; let connection = connect_ladybug_database(&database)?; match &request.phase { LadybugWritePhase::Schema { @@ -454,7 +569,7 @@ fn execute_phase(request: &LadybugWritePhaseRequest) -> Result<(), NativeError> })?; } for statement in statements { - query_ignoring_existing(&connection, statement)?; + run_schema_query(&connection, statement)?; } checkpoint(&connection, "schema creation") } @@ -465,14 +580,9 @@ fn execute_phase(request: &LadybugWritePhaseRequest) -> Result<(), NativeError> runtime_loads, } => { load_runtime_extensions(&connection, runtime_loads)?; - connection.query(statement).map_err(|error| { - NativeError::Database(format!( - "COPY statement {}/{} ({}) failed: {error}", - index + 1, - total, - copy_target(statement) - )) - })?; + connection + .query(statement) + .map_err(|error| copy_query_failure(*index, *total, statement, error))?; checkpoint( &connection, &format!( @@ -499,16 +609,23 @@ fn execute_phase(request: &LadybugWritePhaseRequest) -> Result<(), NativeError> runtime_loads, } => { load_runtime_extensions(&connection, runtime_loads)?; - query_ignoring_existing(&connection, statement)?; + run_schema_query(&connection, statement)?; checkpoint(&connection, "post-COPY schema creation") } } } +#[cfg(test)] fn write_phase_request( request: &LadybugWritePhaseRequest, registry_root: Option<&Path>, ) -> Result { + write_phase_request_owned(request, registry_root).map(|(path, _)| path) +} +fn write_phase_request_owned( + request: &LadybugWritePhaseRequest, + registry_root: Option<&Path>, +) -> Result<(PathBuf, Option), NativeError> { let parent = match registry_root { Some(root) => root, None => request.db_path.parent().ok_or_else(|| { @@ -521,25 +638,113 @@ fn write_phase_request( fs::create_dir_all(parent)?; for _ in 0..16 { let sequence = PHASE_SEQUENCE.fetch_add(1, Ordering::Relaxed); + let mut phase_owner = None; let path = if registry_root.is_some() { let workspace = parent.join(format!("phase-{}-{sequence}", std::process::id())); - match fs::create_dir(&workspace) { - Ok(()) => workspace.join("request.json"), - Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue, - Err(error) => return Err(NativeError::Io(error)), + let parent_scope = + PHASE_PARENT_RUN.with(|slot| slot.borrow().as_ref().map(|parent| parent.retain())); + let mutation = parent_scope + .as_ref() + .map(|parent| { + crate::storage::maintenance_ownership::ReferenceMutation::begin_in_domain( + parent.domain(), + ) + }) + .transpose()?; + let _startup = parent_scope + .as_ref() + .map(|parent| { + crate::storage::locks::open_shared_coordination( + &parent.domain().workspace_startup_path(), + ) + }) + .transpose()?; + let Some(fresh) = crate::storage::layout::create_fresh_reference_root(&workspace)? + else { + continue; + }; + if let (Some(parent), Some(mutation)) = (parent_scope, mutation) { + let directory = + crate::storage::locks::RootedDirectory::open(&workspace, fresh.identity())?; + let owner = crate::storage::locks::create_new_coordination_in( + &directory, + b"lease.lock", + &workspace.join("lease.lock"), + )?; + if let Some(registration) = + crate::storage::maintenance_controls::WorkspaceRegistration::create_with_parent( + &fresh, + parent.domain(), + &mutation, + &owner, + 3, + Some(&parent), + ) + .ok() + .flatten() + { + phase_owner = Some(PhaseWorkspaceOwner { + registration, + owner, + mutation, + _parent: parent, + }); + } } + workspace.join("request.json") } else { parent.join(format!( ".ladybug-phase-{}-{sequence}.json", std::process::id() )) }; + if phase_owner.is_some() { + match crate::storage::maintenance_controls::WorkspaceOutputFile::create_new(&path) { + Ok(Some(output)) => { + let _ = crate::storage::maintenance_origin::record_created_workspace_output( + &output, + ); + let mut writer = BufWriter::new(output); + serde_json::to_writer(&mut writer, request)?; + writer.flush()?; + let output = writer + .into_inner() + .map_err(|error| NativeError::Io(error.into_error()))?; + if let Ok(completed) = output.finish() { + let _ = + crate::storage::maintenance_origin::record_completed_workspace_output( + &completed, + ); + } + return Ok((path, phase_owner)); + } + Ok(None) => { + // Optional activation may have created an unattested empty + // inode. Primary request construction remains available. + let file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(&path)?; + let mut writer = BufWriter::new(file); + serde_json::to_writer(&mut writer, request)?; + writer.flush()?; + return Ok((path, phase_owner)); + } + Err(NativeError::Io(error)) + if error.kind() == std::io::ErrorKind::AlreadyExists => + { + continue + } + Err(error) => return Err(error), + } + } match OpenOptions::new().write(true).create_new(true).open(&path) { Ok(file) => { let mut writer = BufWriter::new(file); serde_json::to_writer(&mut writer, request)?; writer.flush()?; - return Ok(path); + return Ok((path, phase_owner)); } Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue, Err(error) => return Err(NativeError::Io(error)), @@ -555,35 +760,42 @@ fn load_runtime_extensions( statements: &[String], ) -> Result<(), NativeError> { for statement in statements { - query_ignoring_existing(connection, statement)?; + run_schema_query(connection, statement)?; } Ok(()) } -fn query_ignoring_existing( - connection: &Connection<'_>, +fn run_schema_query(connection: &Connection<'_>, statement: &str) -> Result<(), NativeError> { + connection + .query(statement) + .map(|_| ()) + .map_err(|error| NativeError::Database(error.to_string())) +} + +fn copy_query_failure( + index: usize, + total: usize, statement: &str, -) -> Result<(), NativeError> { - match connection.query(statement) { - Ok(_) => Ok(()), - Err(error) => { - let message = error.to_string().to_lowercase(); - if message.contains("already exists") - || message.contains("exists already") - || message.contains("already installed") - { - Ok(()) - } else { - Err(NativeError::Database(error.to_string())) - } - } - } + error: impl std::fmt::Display, +) -> NativeError { + NativeError::Database(format!( + "COPY statement {}/{} ({}) failed: {error}; operation=COPY-query", + index.saturating_add(1), + total, + copy_target(statement) + )) +} + +fn checkpoint_failure(after: &str, error: impl std::fmt::Display) -> NativeError { + NativeError::Database(format!( + "checkpoint after {after} failed: {error}; operation=explicit-CHECKPOINT" + )) } fn checkpoint(connection: &Connection<'_>, after: &str) -> Result<(), NativeError> { - connection.query("CHECKPOINT").map_err(|error| { - NativeError::Database(format!("checkpoint after {after} failed: {error}")) - })?; + connection + .query("CHECKPOINT") + .map_err(|error| checkpoint_failure(after, error))?; Ok(()) } @@ -595,11 +807,98 @@ fn copy_target(statement: &str) -> &str { .unwrap_or("unknown target") } -fn bounded_child_error(bytes: &[u8]) -> String { - let start = bytes.len().saturating_sub(MAX_CHILD_ERROR_BYTES); - String::from_utf8_lossy(&bytes[start..]).trim().to_string() +fn read_error_tail(path: &Path) -> Result { + read_error_tail_from(&mut fs::File::open(path)?) +} + +fn read_error_tail_from(file: &mut (impl Read + Seek)) -> Result { + let len = file.seek(SeekFrom::End(0))?; + let start = len.saturating_sub(MAX_CHILD_ERROR_BYTES as u64); + file.seek(SeekFrom::Start(start))?; + let mut bytes = Vec::with_capacity((len - start) as usize); + file.take(MAX_CHILD_ERROR_BYTES as u64) + .read_to_end(&mut bytes)?; + let decoded = String::from_utf8_lossy(&bytes); + let marker = "[stderr truncated] "; + let truncated = start > 0 || decoded.len() > MAX_CHILD_ERROR_BYTES; + let budget = MAX_CHILD_ERROR_BYTES - if truncated { marker.len() } else { 0 }; + let tail = truncate_utf8_tail(decoded.trim(), budget); + Ok(if truncated { + format!("{marker}{tail}") + } else { + tail.to_string() + }) +} + +fn isolated_phase_error( + request: &LadybugWritePhaseRequest, + status: &str, + child_error: &str, +) -> String { + let db_path = request.db_path.display().to_string(); + let db_path = truncate_utf8_head(&db_path, 1024); + let status = truncate_utf8_head(status, 1024); + let (phase, target) = match &request.phase { + LadybugWritePhase::Schema { .. } => ("schema", String::new()), + LadybugWritePhase::Copy { + index, + total, + statement, + .. + } => ( + "copy", + format!( + " copy={}/{} target={}", + index.saturating_add(1), + total, + truncate_utf8_head(copy_target(statement), 1024) + ), + ), + LadybugWritePhase::Index { table } => ( + "index", + format!(" target={}", truncate_utf8_head(table, 1024)), + ), + LadybugWritePhase::PostCopy { .. } => ("post-copy", String::new()), + }; + let context = format!( + "isolated Ladybug write phase exited with {status}: phase={phase} db={db_path} max_num_threads={}{target}; ", + request.max_num_threads, + ); + let marker = "[stderr truncated] "; + let payload = child_error.strip_prefix(marker).unwrap_or(child_error); + let truncated = child_error.starts_with(marker) + || payload.len() > MAX_CHILD_ERROR_BYTES.saturating_sub(context.len()); + let budget = MAX_CHILD_ERROR_BYTES + .saturating_sub(context.len()) + .saturating_sub(if truncated { marker.len() } else { 0 }); + let tail = truncate_utf8_tail(payload, budget); + if truncated { + format!("{context}{marker}{tail}") + } else { + format!("{context}{tail}") + } } +fn truncate_utf8_head(text: &str, limit: usize) -> &str { + let mut end = text.len().min(limit); + while !text.is_char_boundary(end) { + end -= 1; + } + &text[..end] +} + +fn truncate_utf8_tail(text: &str, limit: usize) -> &str { + if text.len() <= limit { + return text; + } + let mut start = text.len() - limit; + while !text.is_char_boundary(start) { + start += 1; + } + &text[start..] +} + +#[cfg(target_os = "macos")] impl LadybugWritePhase { fn trace_label(&self) -> String { let label = match self { @@ -625,6 +924,39 @@ impl LadybugWritePhase { mod tests { use super::*; + #[test] + fn phase_query_keeps_successful_if_not_exists_idempotence_but_surfaces_native_error() { + let root = std::env::temp_dir().join(format!( + "phase-query-test-{}-{}", + std::process::id(), + PHASE_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir_all(&root).unwrap(); + let path = root.join("db.lbug"); + let database = open_ladybug_database_with_threads(&path, false, 1).unwrap(); + let connection = connect_ladybug_database(&database).unwrap(); + run_schema_query( + &connection, + "CREATE NODE TABLE IF NOT EXISTS `Thing`(`id` STRING PRIMARY KEY)", + ) + .unwrap(); + run_schema_query( + &connection, + "CREATE NODE TABLE IF NOT EXISTS `Thing`(`id` STRING PRIMARY KEY)", + ) + .unwrap(); + let error = run_schema_query( + &connection, + "CREATE NODE TABLE `Thing`(`id` STRING PRIMARY KEY)", + ) + .unwrap_err(); + let message = error.to_string().to_lowercase(); + assert!(message.contains("already exists") || message.contains("exists already")); + drop(connection); + drop(database); + fs::remove_dir_all(root).unwrap(); + } + #[test] fn phase_registry_scope_restores_nested_and_unwound_contexts() { assert!(PHASE_WORKSPACE_ROOT.with(|slot| slot.borrow().is_none())); @@ -661,8 +993,6 @@ mod tests { let registry = root.join("stable").join("phases"); let request = LadybugWritePhaseRequest::new( root.join("run/candidate/db"), - 1024, - 128, 2, LadybugWritePhase::Index { table: "Function".into(), @@ -732,11 +1062,140 @@ mod tests { } #[test] - fn phase_request_v2_preserves_native_resource_settings() { + fn error_tail_reads_at_most_the_limit_and_decodes_utf8_lossily() { + let root = std::env::temp_dir().join(format!( + "phase-error-tail-{}-{}", + std::process::id(), + PHASE_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir_all(&root).unwrap(); + let path = root.join("stderr"); + let mut contents = vec![b'x'; MAX_CHILD_ERROR_BYTES + 4096]; + contents[MAX_CHILD_ERROR_BYTES + 3] = 0xff; + contents[MAX_CHILD_ERROR_BYTES + 4094] = 0xc3; + contents[MAX_CHILD_ERROR_BYTES + 4095] = 0xa9; + fs::write(&path, contents).unwrap(); + let tail = read_error_tail(&path).unwrap(); + assert!(tail.len() <= MAX_CHILD_ERROR_BYTES + "[stderr truncated]\n".len()); + assert!(tail.starts_with("[stderr truncated]")); + assert!(tail.contains('\u{fffd}')); + assert!(tail.ends_with('é')); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn error_tail_reader_requests_no_more_than_the_limit() { + struct CountingCursor { + cursor: std::io::Cursor>, + requested: usize, + } + impl Read for CountingCursor { + fn read(&mut self, buffer: &mut [u8]) -> std::io::Result { + self.requested = self.requested.saturating_add(buffer.len()); + self.cursor.read(buffer) + } + } + impl Seek for CountingCursor { + fn seek(&mut self, position: SeekFrom) -> std::io::Result { + self.cursor.seek(position) + } + } + let mut reader = CountingCursor { + cursor: std::io::Cursor::new(vec![b'x'; MAX_CHILD_ERROR_BYTES * 5]), + requested: 0, + }; + let tail = read_error_tail_from(&mut reader).unwrap(); + assert_eq!(reader.requested, MAX_CHILD_ERROR_BYTES); + assert!(tail.starts_with("[stderr truncated]")); + assert!(tail.len() <= MAX_CHILD_ERROR_BYTES + "[stderr truncated]\n".len()); + } + + #[test] + fn oversized_native_error_preserves_query_vs_explicit_checkpoint_boundary() { + let statement = "COPY `Thing` FROM 'rows.json'"; + let request = LadybugWritePhaseRequest::new( + "candidate/db", + 1, + LadybugWritePhase::Copy { + index: 0, + total: 1, + statement: statement.into(), + runtime_loads: vec![], + }, + ); + let native = "é".repeat(MAX_CHILD_ERROR_BYTES); + let failures = [ + ( + copy_query_failure(0, 1, statement, &native), + "operation=COPY-query", + ), + ( + checkpoint_failure("COPY statement 1/1", &native), + "operation=explicit-CHECKPOINT", + ), + ]; + for (failure, boundary) in failures { + let mut stderr = + std::io::Cursor::new(format!("earlier native output\n{failure}\n").into_bytes()); + let tail = read_error_tail_from(&mut stderr).unwrap(); + let diagnostic = isolated_phase_error(&request, "exit status: 1", &tail); + assert!(diagnostic.len() <= MAX_CHILD_ERROR_BYTES); + assert!(diagnostic.contains("[stderr truncated]")); + assert!( + diagnostic.ends_with(boundary), + "operation boundary was lost" + ); + assert!(diagnostic.contains("copy=1/1 target=`Thing`")); + } + } + + #[test] + fn isolated_error_keeps_context_inside_the_cap() { + let request = LadybugWritePhaseRequest::new( + "candidate/db", + 2, + LadybugWritePhase::Copy { + index: 7, + total: 10, + statement: "COPY `DocumentationSource` FROM 'rows.csv'".into(), + runtime_loads: vec![], + }, + ); + let message = isolated_phase_error(&request, "exit status: 1", &"é".repeat(40_000)); + assert!(message.len() <= MAX_CHILD_ERROR_BYTES); + assert!(message.contains("copy=8/10 target=`DocumentationSource`")); + assert!(message.contains("max_num_threads=2")); + assert!(std::str::from_utf8(message.as_bytes()).is_ok()); + } + + #[cfg(debug_assertions)] + #[test] + fn copy_trace_metadata_reports_generated_file_bytes() { + let root = std::env::temp_dir().join(format!( + "phase-copy-source-{}-{}", + std::process::id(), + PHASE_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir_all(&root).unwrap(); + let source = root.join("rows with spaces.json"); + fs::write(&source, b"[{}]").unwrap(); + let statement = format!( + "COPY `Thing` FROM \"{}\"", + crate::staging_writer::copy_path(&source) + ); + let (observed_path, observed_bytes) = copy_source_metadata(&statement).unwrap(); + assert_eq!( + fs::canonicalize(observed_path).unwrap(), + fs::canonicalize(&source).unwrap() + ); + assert_eq!(observed_bytes, 4); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn phase_request_v2_preserves_thread_setting() { let request = LadybugWritePhaseRequest::new( "candidate/db", - 1024 * 1024 * 1024, - 128 * 1024 * 1024, 3, LadybugWritePhase::Copy { index: 2, @@ -748,13 +1207,7 @@ mod tests { let roundtrip: LadybugWritePhaseRequest = serde_json::from_slice(&serde_json::to_vec(&request).unwrap()).unwrap(); assert_eq!(roundtrip.version, 2); - assert_eq!(roundtrip.worker_memory_bytes, request.worker_memory_bytes); - assert_eq!(roundtrip.buffer_pool_bytes, request.buffer_pool_bytes); assert_eq!(roundtrip.max_num_threads, 3); - assert_eq!( - phase_memory_label(&roundtrip), - "ladybug__Function__pool_128_mib" - ); assert!(matches!( roundtrip.phase, LadybugWritePhase::Copy { diff --git a/src/db_writer/request.rs b/src/db_writer/request.rs index 5bb9d895..924720d7 100644 --- a/src/db_writer/request.rs +++ b/src/db_writer/request.rs @@ -1,8 +1,6 @@ #[derive(Debug, Clone)] pub struct LadybugWriteRequest { pub db_path: String, - pub worker_memory_bytes: u64, - pub buffer_pool_bytes: u64, pub max_num_threads: u64, pub defer_hash_indexes: bool, pub include_fts: bool, diff --git a/src/db_writer/tests.rs b/src/db_writer/tests.rs index cf25dc43..3b9c9520 100644 --- a/src/db_writer/tests.rs +++ b/src/db_writer/tests.rs @@ -24,8 +24,6 @@ fn native_writer_loads_json_staging_through_ladybug_copy() { let result = write_database(LadybugWriteRequest { db_path: db_path.to_string_lossy().to_string(), - worker_memory_bytes: 768 * 1024 * 1024, - buffer_pool_bytes: 128 * 1024 * 1024, max_num_threads: 1, defer_hash_indexes: true, include_fts: false, @@ -65,8 +63,6 @@ fn native_writer_loads_multiple_chunks_into_one_primary_key_table() { let result = write_database(LadybugWriteRequest { db_path: db_path.to_string_lossy().to_string(), - worker_memory_bytes: 768 * 1024 * 1024, - buffer_pool_bytes: 128 * 1024 * 1024, max_num_threads: 1, defer_hash_indexes: true, include_fts: false, @@ -132,3 +128,408 @@ fn copy_path(path: &Path) -> String { .replace('\\', "/") .replace('"', "\\\"") } + +#[test] +fn guarded_database_move_and_connection_retain_physical_family() { + use crate::storage::maintenance_ownership::DatabaseUseLease; + let root = unique_temp_dir("guarded-database-lifetime"); + fs::create_dir_all(&root).unwrap(); + let path = root.join("graph.lbug"); + let database = super::open_ladybug_database(&path, false).unwrap(); + let moved = database; + let connection = super::connect_ladybug_database(&moved).unwrap(); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_none()); + // Family exclusion also protects another spelling/resource in this parent. + assert!(DatabaseUseLease::try_exclusive(&root.join("other.lbug")) + .unwrap() + .is_none()); + connection.query("RETURN 1").unwrap(); + drop(connection); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_none()); + drop(moved); + let exclusive = DatabaseUseLease::try_exclusive(&path).unwrap().unwrap(); + // A native reopen succeeds once the wrapper released its exclusion. This + // would fail if the native handle were still alive after guard release. + let native = lbug::Database::new( + &path, + lbug::SystemConfig::default().buffer_pool_size(128 * 1024 * 1024), + ) + .unwrap(); + drop(native); + drop(exclusive); + fs::remove_dir_all(root).unwrap(); +} + +#[test] +fn guarded_database_failed_native_open_releases_exclusion() { + use crate::storage::maintenance_ownership::DatabaseUseLease; + let root = unique_temp_dir("guarded-database-open-error"); + fs::create_dir_all(&root).unwrap(); + let path = root.join("missing.lbug"); + assert!(super::open_ladybug_database(&path, true).is_err()); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_some()); + fs::remove_dir_all(root).unwrap(); +} + +#[test] +fn guarded_database_unwind_closes_native_handle_and_releases_exclusion() { + use crate::storage::maintenance_ownership::DatabaseUseLease; + let root = unique_temp_dir("guarded-database-unwind"); + fs::create_dir_all(&root).unwrap(); + let path = root.join("graph.lbug"); + assert!(std::panic::catch_unwind(|| { + let database = super::open_ladybug_database(&path, false).unwrap(); + let _connection = super::connect_ladybug_database(&database).unwrap(); + panic!("fixture unwind"); + }) + .is_err()); + let exclusive = DatabaseUseLease::try_exclusive(&path).unwrap().unwrap(); + let native = lbug::Database::new( + &path, + lbug::SystemConfig::default().buffer_pool_size(128 * 1024 * 1024), + ) + .unwrap(); + drop(native); + drop(exclusive); + fs::remove_dir_all(root).unwrap(); +} + +#[cfg(unix)] +#[test] +fn guarded_database_rejects_symlink_and_hardlink_resources_before_open() { + let root = unique_temp_dir("guarded-database-alias"); + fs::create_dir_all(&root).unwrap(); + let path = root.join("graph.lbug"); + drop(super::open_ladybug_database(&path, false).unwrap()); + let alias = root.join("alias.lbug"); + std::os::unix::fs::symlink(&path, &alias).unwrap(); + assert!(super::open_ladybug_database(&alias, true).is_err()); + fs::remove_file(&alias).unwrap(); + fs::hard_link(&path, &alias).unwrap(); + assert!(super::open_ladybug_database(&alias, true).is_err()); + assert!(super::open_ladybug_database(&path, true).is_err()); + fs::remove_dir_all(root).unwrap(); +} + +#[cfg(target_os = "macos")] +#[test] +fn guarded_database_unicode_alias_retains_physical_parent_exclusion() { + use crate::storage::maintenance_ownership::DatabaseUseLease; + let root = unique_temp_dir("guarded-database-unicode"); + fs::create_dir_all(&root).unwrap(); + let path = root.join("caf\u{e9}.lbug"); + let alias = root.join("cafe\u{301}.lbug"); + drop(super::open_ladybug_database(&path, false).unwrap()); + let database = super::open_ladybug_database(&alias, true).unwrap(); + let connection = super::connect_ladybug_database(&database).unwrap(); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_none()); + connection.query("RETURN 1").unwrap(); + drop(connection); + drop(database); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_some()); + fs::remove_dir_all(root).unwrap(); +} + +#[cfg(unix)] +#[test] +fn guarded_database_readonly_rejects_parent_without_coordination_write_access() { + use std::os::unix::fs::PermissionsExt; + let root = unique_temp_dir("guarded-database-readonly-parent"); + fs::create_dir_all(&root).unwrap(); + let path = root.join("graph.lbug"); + // This fixture intentionally starts with a native-created file and no use + // locks, modeling a database exposed by an older/uncoordinated producer. + drop( + lbug::Database::new( + &path, + lbug::SystemConfig::default().buffer_pool_size(128 * 1024 * 1024), + ) + .unwrap(), + ); + fs::set_permissions(&root, fs::Permissions::from_mode(0o555)).unwrap(); + let result = super::open_ladybug_database(&path, true); + fs::set_permissions(&root, fs::Permissions::from_mode(0o755)).unwrap(); + assert!( + result.is_err(), + "readonly native access must still establish exclusion" + ); + assert!(!root.join(".database-use-family.lock").exists()); + fs::remove_dir_all(root).unwrap(); +} + +#[test] +fn guarded_database_native_path_rejects_transforms_before_filesystem_effects() { + let root = unique_temp_dir("guarded-native-path-rejection"); + fs::create_dir_all(&root).unwrap(); + fs::write(root.join("sentinel"), b"unchanged").unwrap(); + let traversal = root.join("absent").join("..").join("graph.lbug"); + let tilde = PathBuf::from(format!( + "~/../../{}", + root.join("tilde-native") + .join("graph.lbug") + .strip_prefix("/") + .unwrap_or(&root) + .display() + )); + for invalid in [ + PathBuf::new(), + PathBuf::from(":memory:"), + PathBuf::from("~"), + PathBuf::from("~other/graph.lbug"), + PathBuf::from("graph\0.lbug"), + traversal, + tilde, + PathBuf::from(format!("{}/absent/", root.display())), + PathBuf::from(format!("{}/absent/.", root.display())), + ] { + assert!( + matches!( + super::open_ladybug_database(&invalid, false), + Err(NativeError::InvalidInput(_)) + ), + "unsupported native spelling must be rejected: {invalid:?}" + ); + } + assert_eq!(fs::read(root.join("sentinel")).unwrap(), b"unchanged"); + assert_eq!(fs::read_dir(&root).unwrap().count(), 1); + fs::remove_dir_all(root).unwrap(); +} + +#[cfg(unix)] +#[test] +fn guarded_database_native_path_rejects_full_non_utf8_before_effects() { + use std::ffi::OsString; + use std::os::unix::ffi::OsStringExt; + let root = unique_temp_dir("guarded-native-nonutf8"); + fs::create_dir_all(&root).unwrap(); + // No invalid-byte directory is created: Darwin rejects such a fixture at + // the filesystem boundary. This exercises Rust-side rejection on Darwin + // and Linux without pretending to establish a Linux native runtime result. + let invalid = root + .join(OsString::from_vec(b"invalid-\xff-parent".to_vec())) + .join("graph.lbug"); + assert!(matches!( + super::open_ladybug_database(&invalid, false), + Err(NativeError::InvalidInput(_)) + )); + assert_eq!(fs::read_dir(&root).unwrap().count(), 0); + fs::remove_dir_all(root).unwrap(); +} + +#[test] +fn guarded_database_relative_native_open_uses_one_absolute_guarded_resource() { + use crate::storage::maintenance_ownership::DatabaseUseLease; + let cwd = std::env::current_dir().unwrap(); + let name = unique_temp_dir("guarded-native-relative") + .file_name() + .unwrap() + .to_owned(); + let root = cwd.join(&name); + fs::create_dir(&root).unwrap(); + let relative = PathBuf::from(".").join(&name).join("graph.lbug"); + let bound = super::access::guarded_native_path(&relative).unwrap(); + assert!(bound.is_absolute()); + assert_eq!(bound, root.join("graph.lbug")); + let database = super::open_ladybug_database(&relative, false).unwrap(); + assert!(bound.is_file()); + assert!(DatabaseUseLease::try_exclusive(&bound).unwrap().is_none()); + let connection = super::connect_ladybug_database(&database).unwrap(); + connection.query("RETURN 1").unwrap(); + drop(connection); + drop(database); + assert!(DatabaseUseLease::try_exclusive(&bound).unwrap().is_some()); + fs::remove_dir_all(root).unwrap(); +} + +#[cfg(unix)] +#[test] +fn guarded_database_symlink_parent_traversal_cannot_bind_another_native_resource() { + let root = unique_temp_dir("guarded-native-symlink-traversal"); + fs::create_dir_all(root.join("actual").join("nested")).unwrap(); + std::os::unix::fs::symlink(root.join("actual").join("nested"), root.join("alias")).unwrap(); + // Kernel resolution would address actual/graph.lbug; Ladybug's lexical + // normalization would address root/graph.lbug. Neither may be opened. + let addressed = root.join("alias").join("..").join("graph.lbug"); + assert!(matches!( + super::open_ladybug_database(&addressed, false), + Err(NativeError::InvalidInput(_)) + )); + assert!(!root.join("graph.lbug").exists()); + assert!(!root.join("actual").join("graph.lbug").exists()); + assert!(!root.join(".database-use-family.lock").exists()); + assert!(!root + .join("actual") + .join(".database-use-family.lock") + .exists()); + fs::remove_dir_all(root).unwrap(); +} + +#[cfg(windows)] +#[test] +fn guarded_database_native_path_rejects_unsupported_windows_prefixes() { + for spelling in [ + r"C:graph.lbug", + r"\graph.lbug", + r"\\?\GLOBALROOT\Device\graph.lbug", + r"\\.\pipe\graph.lbug", + r"C:\absent\..\graph.lbug", + ] { + assert!(matches!( + super::open_ladybug_database(Path::new(spelling), false), + Err(NativeError::InvalidInput(_)) + )); + } +} + +#[test] +fn guarded_database_windows_encoding_policy_checks_the_complete_native_path() { + use super::access::windows_native_encoding_matches; + for code_page in [0, 437, 932, 1252, 65001] { + for ascii in [ + r"C:\fixture\graph.lbug", + r"\\?\C:\fixture\graph.lbug", + r"\\server\share\fixture\graph.lbug", + r"\\?\UNC\server\share\fixture\graph.lbug", + ] { + assert!(windows_native_encoding_matches(ascii, code_page)); + } + for unicode in [ + r"C:\café\graph.lbug", + r"\\?\C:\café\graph.lbug", + r"\\server\share\café\graph.lbug", + r"\\?\UNC\server\share\café\graph.lbug", + ] { + // Put Unicode in the parent, retaining an ASCII database basename. + assert_eq!( + windows_native_encoding_matches(unicode, code_page), + code_page == 65001 + ); + } + } +} + +#[cfg(windows)] +#[test] +fn guarded_database_windows_preserves_disk_unc_and_canonical_namespace_spelling() { + for spelling in [ + r"C:\fixture\graph.lbug", + r"\\server\share\fixture\graph.lbug", + r"\\?\C:\fixture\graph.lbug", + r"\\?\UNC\server\share\fixture\graph.lbug", + ] { + let path = Path::new(spelling); + assert_eq!( + super::access::guarded_native_path(path) + .unwrap() + .as_os_str(), + path.as_os_str() + ); + } + for spelling in [ + r"\\?\C:\fixture\.\graph.lbug", + r"\\?\C:\fixture\..\graph.lbug", + r"\\?\C:\fixture\\graph.lbug", + r"\\?\C:\fixture/graph.lbug", + r"C:\fixture.\graph.lbug", + r"C:\fixture \graph.lbug", + ] { + assert!(super::access::guarded_native_path(Path::new(spelling)).is_err()); + } +} + +#[cfg(windows)] +#[test] +fn guarded_database_windows_canonicalized_parent_native_use_retains_exclusion() { + use crate::storage::maintenance_ownership::DatabaseUseLease; + use std::path::{Component, Prefix}; + let root = unique_temp_dir("guarded-native-windows-canonical"); + fs::create_dir_all(&root).unwrap(); + let canonical = fs::canonicalize(&root).unwrap(); + assert!( + matches!(canonical.components().next(), Some(Component::Prefix(prefix)) + if matches!(prefix.kind(), Prefix::VerbatimDisk(_) | Prefix::VerbatimUNC(_, _))) + ); + let path = canonical.join("graph.lbug"); + if !super::access::windows_native_encoding_matches( + path.to_str().unwrap(), + super::access::windows_effective_file_api_code_page(), + ) { + assert!(matches!( + super::open_ladybug_database(&path, false), + Err(NativeError::InvalidInput(_)) + )); + assert_eq!(fs::read_dir(&root).unwrap().count(), 0); + fs::remove_dir_all(root).unwrap(); + return; + } + assert_eq!(super::access::guarded_native_path(&path).unwrap(), path); + let database = super::open_ladybug_database(&path, false).unwrap(); + let connection = super::connect_ladybug_database(&database).unwrap(); + connection.query("RETURN 1").unwrap(); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_none()); + drop(connection); + drop(database); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_some()); + fs::remove_dir_all(root).unwrap(); +} + +#[cfg(windows)] +#[test] +fn guarded_database_windows_extended_long_native_path_retains_prefix_and_exclusion() { + use crate::storage::maintenance_ownership::DatabaseUseLease; + let root = unique_temp_dir("guarded-native-windows-long"); + fs::create_dir_all(&root).unwrap(); + let mut parent = fs::canonicalize(&root).unwrap(); + for _ in 0..8 { + parent.push("a".repeat(40)); + } + fs::create_dir_all(&parent).unwrap(); + let path = parent.join("graph.lbug"); + assert!(path.to_str().unwrap().len() > 260); + if !super::access::windows_native_encoding_matches( + path.to_str().unwrap(), + super::access::windows_effective_file_api_code_page(), + ) { + assert!(matches!( + super::open_ladybug_database(&path, false), + Err(NativeError::InvalidInput(_)) + )); + fs::remove_dir_all(root).unwrap(); + return; + } + assert_eq!(super::access::guarded_native_path(&path).unwrap(), path); + let database = super::open_ladybug_database(&path, false).unwrap(); + let connection = super::connect_ladybug_database(&database).unwrap(); + connection.query("RETURN 1").unwrap(); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_none()); + drop(connection); + drop(database); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_some()); + fs::remove_dir_all(root).unwrap(); +} + +#[cfg(windows)] +#[test] +fn guarded_database_windows_non_ascii_parent_follows_effective_file_api_codepage() { + use crate::storage::maintenance_ownership::DatabaseUseLease; + let root = unique_temp_dir("guarded-native-windows-encoding"); + fs::create_dir_all(&root).unwrap(); + let path = fs::canonicalize(&root) + .unwrap() + .join("café") + .join("graph.lbug"); + let result = super::open_ladybug_database(&path, false); + if super::access::windows_effective_file_api_code_page() == 65001 { + let database = result.unwrap(); + let connection = super::connect_ladybug_database(&database).unwrap(); + connection.query("RETURN 1").unwrap(); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_none()); + drop(connection); + drop(database); + assert!(DatabaseUseLease::try_exclusive(&path).unwrap().is_some()); + } else { + assert!(matches!(result, Err(NativeError::InvalidInput(_)))); + assert_eq!(fs::read_dir(&root).unwrap().count(), 0); + } + fs::remove_dir_all(root).unwrap(); +} diff --git a/src/db_writer/write.rs b/src/db_writer/write.rs index 14adae56..f887a97b 100644 --- a/src/db_writer/write.rs +++ b/src/db_writer/write.rs @@ -5,19 +5,13 @@ use super::phase::{ use super::request::{LadybugWriteMetrics, LadybugWriteRequest}; use super::rss::release_unused_allocator_pages; use super::schema::statement_phases; -use super::{ - connect_ladybug_database, open_ladybug_database_with_limits, retry_transient_database, - WRITE_RETRY_POLICY, -}; +use super::{connect_ladybug_database, open_ladybug_database_with_threads}; use crate::error::NativeError; use lbug::Connection; use std::collections::BTreeSet; use std::fs; use std::path::Path; -const INITIAL_DATABASE_BUFFER_POOL_BYTES: u64 = 256 * 1024 * 1024; -const DATABASE_BUFFER_POOL_STEP_BYTES: u64 = 64 * 1024 * 1024; - pub fn write_database(request: LadybugWriteRequest) -> Result<(), NativeError> { write_database_with_metrics(request).map(|_| ()) } @@ -25,20 +19,22 @@ pub fn write_database(request: LadybugWriteRequest) -> Result<(), NativeError> { pub fn write_database_with_metrics( request: LadybugWriteRequest, ) -> Result { + let _database_use = crate::storage::maintenance_ownership::DatabaseUseLease::acquire_shared( + Path::new(&request.db_path), + )?; if phase_worker_available() { write_database_isolated(&request) } else { - retry_transient_database(WRITE_RETRY_POLICY, || write_database_once(&request))?; + write_database_once(&request)?; Ok(LadybugWriteMetrics::default()) } } fn write_database_once(request: &LadybugWriteRequest) -> Result<(), NativeError> { prepare_candidate(request)?; - let database = open_ladybug_database_with_limits( + let database = open_ladybug_database_with_threads( Path::new(&request.db_path), false, - request.buffer_pool_bytes, request.max_num_threads, )?; let connection = connect_ladybug_database(&database)?; @@ -53,7 +49,7 @@ fn write_database_once(request: &LadybugWriteRequest) -> Result<(), NativeError> } let phases = statement_phases(request.include_fts, &request.schema_statements); for statement in phases.pre_copy { - query_ignoring_existing(&connection, statement)?; + run_schema_query(&connection, statement)?; } if request.defer_hash_indexes { execute_deferred_index_copy(&connection, request)?; @@ -63,13 +59,20 @@ fn write_database_once(request: &LadybugWriteRequest) -> Result<(), NativeError> } } for statement in phases.post_copy { - query_ignoring_existing(&connection, statement)?; + run_schema_query(&connection, statement)?; } Ok(()) } fn write_database_isolated( request: &LadybugWriteRequest, +) -> Result { + write_database_isolated_with(request, run_phase) +} + +fn write_database_isolated_with( + request: &LadybugWriteRequest, + mut execute: impl FnMut(&LadybugWriteRequest, LadybugWritePhase) -> Result, ) -> Result { prepare_candidate(request)?; let phases = statement_phases(request.include_fts, &request.schema_statements); @@ -92,7 +95,7 @@ fn write_database_isolated( // resident in the parent allocator. Return them before the first Ladybug // child so the combined parent+child RSS supervisor measures live work. release_unused_allocator_pages(); - let mut high_water_bytes = run_phase( + let mut high_water_bytes = execute( request, LadybugWritePhase::Schema { defer_hash_indexes: request.defer_hash_indexes, @@ -117,8 +120,10 @@ fn write_database_isolated( .iter() .enumerate() { - high_water_bytes = - high_water_bytes.max(run_copy_phase(request, index, statement, &runtime_loads)?); + high_water_bytes = high_water_bytes.max(execute( + request, + copy_phase(request, index, statement, &runtime_loads), + )?); if request.defer_hash_indexes { let table = node_table_name(statement).ok_or_else(|| { NativeError::InvalidInput(format!( @@ -128,7 +133,7 @@ fn write_database_isolated( })?; if early_index_tables.contains(table) { if indexed_tables.insert(table.to_string()) { - high_water_bytes = high_water_bytes.max(run_phase( + high_water_bytes = high_water_bytes.max(execute( request, LadybugWritePhase::Index { table: table.to_string(), @@ -142,21 +147,19 @@ fn write_database_isolated( } for table in node_tables { high_water_bytes = - high_water_bytes.max(run_phase(request, LadybugWritePhase::Index { table })?); + high_water_bytes.max(execute(request, LadybugWritePhase::Index { table })?); } for (offset, statement) in request.copy_statements[connector_start..] .iter() .enumerate() { - high_water_bytes = high_water_bytes.max(run_copy_phase( + high_water_bytes = high_water_bytes.max(execute( request, - connector_start + offset, - statement, - &[], + copy_phase(request, connector_start + offset, statement, &[]), )?); } for statement in phases.post_copy { - high_water_bytes = high_water_bytes.max(run_phase( + high_water_bytes = high_water_bytes.max(execute( request, LadybugWritePhase::PostCopy { statement: statement.to_string(), @@ -183,66 +186,27 @@ fn prepare_candidate(request: &LadybugWriteRequest) -> Result<(), NativeError> { fn phase_request( request: &LadybugWriteRequest, - buffer_pool_bytes: u64, phase: LadybugWritePhase, ) -> LadybugWritePhaseRequest { - LadybugWritePhaseRequest::new( - &request.db_path, - request.worker_memory_bytes, - buffer_pool_bytes, - request.max_num_threads, - phase, - ) + LadybugWritePhaseRequest::new(&request.db_path, request.max_num_threads, phase) } -fn run_copy_phase( +fn copy_phase( request: &LadybugWriteRequest, index: usize, statement: &str, runtime_loads: &[String], -) -> Result { - run_phase( - request, - LadybugWritePhase::Copy { - index, - total: request.copy_statements.len(), - statement: statement.to_string(), - runtime_loads: runtime_loads.to_vec(), - }, - ) -} - -fn run_phase(request: &LadybugWriteRequest, phase: LadybugWritePhase) -> Result { - let mut buffer_pool_bytes = request - .buffer_pool_bytes - .min(INITIAL_DATABASE_BUFFER_POOL_BYTES); - loop { - match run_isolated_phase(&phase_request(request, buffer_pool_bytes, phase.clone())) { - Ok(high_water_bytes) => return Ok(high_water_bytes), - Err(error) - if is_retryable_buffer_pool_exhaustion(&error) - && buffer_pool_bytes < request.buffer_pool_bytes => - { - buffer_pool_bytes = - next_buffer_pool_bytes(buffer_pool_bytes, request.buffer_pool_bytes); - } - Err(error) => return Err(error), - } +) -> LadybugWritePhase { + LadybugWritePhase::Copy { + index, + total: request.copy_statements.len(), + statement: statement.to_string(), + runtime_loads: runtime_loads.to_vec(), } } -fn next_buffer_pool_bytes(current: u64, maximum: u64) -> u64 { - current - .saturating_add(DATABASE_BUFFER_POOL_STEP_BYTES) - .min(maximum) -} - -fn is_retryable_buffer_pool_exhaustion(error: &NativeError) -> bool { - let NativeError::Database(message) = error else { - return false; - }; - let message = message.to_ascii_lowercase(); - message.contains("buffer pool is full") && !message.contains("checkpoint after") +fn run_phase(request: &LadybugWriteRequest, phase: LadybugWritePhase) -> Result { + run_isolated_phase(&phase_request(request, phase)) } fn execute_deferred_index_copy( @@ -373,63 +337,116 @@ fn node_table_name(statement: &str) -> Option<&str> { .then_some(table) } -fn query_ignoring_existing( - connection: &Connection<'_>, - statement: &str, -) -> Result<(), NativeError> { - match connection.query(statement) { - Ok(_) => Ok(()), - Err(error) => { - let message = error.to_string().to_lowercase(); - if message.contains("already exists") - || message.contains("exists already") - || message.contains("already installed") - { - Ok(()) - } else { - Err(NativeError::Database(error.to_string())) - } - } - } +fn run_schema_query(connection: &Connection<'_>, statement: &str) -> Result<(), NativeError> { + connection + .query(statement) + .map(|_| ()) + .map_err(|error| NativeError::Database(error.to_string())) } #[cfg(test)] mod tests { - use super::{ - is_retryable_buffer_pool_exhaustion, next_buffer_pool_bytes, - DATABASE_BUFFER_POOL_STEP_BYTES, INITIAL_DATABASE_BUFFER_POOL_BYTES, - }; - use crate::error::{MemoryBudgetExceeded, NativeError}; + use super::*; + use std::cell::RefCell; + use std::path::PathBuf; - #[test] - fn database_pool_escalation_is_bounded_and_incremental() { - let maximum = 384 * 1024 * 1024; - let second = next_buffer_pool_bytes(INITIAL_DATABASE_BUFFER_POOL_BYTES, maximum); - let third = next_buffer_pool_bytes(second, maximum); - - assert_eq!(second, 320 * 1024 * 1024); - assert_eq!(third, maximum); - assert_eq!(next_buffer_pool_bytes(third, maximum), maximum); - assert_eq!(DATABASE_BUFFER_POOL_STEP_BYTES, 64 * 1024 * 1024); + fn request(copy_count: usize) -> (PathBuf, LadybugWriteRequest) { + let root = std::env::temp_dir().join(format!( + "writer-phase-order-{}-{}-{}", + std::process::id(), + copy_count, + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + let mut statements = (0..copy_count) + .map(|i| format!("COPY `Thing` FROM 'rows{i}.csv'")) + .collect::>(); + statements.push("COPY `FROM_Links` FROM 'links.csv'".into()); + let request = LadybugWriteRequest { + db_path: root.join("candidate.lbug").to_string_lossy().to_string(), + max_num_threads: 3, + defer_hash_indexes: true, + include_fts: true, + schema_statements: vec![ + "CREATE NODE TABLE IF NOT EXISTS `Thing`(`id` STRING PRIMARY KEY)".into(), + "CALL CREATE_FTS_INDEX('Thing', 'thing', ['id'])".into(), + ], + copy_statements: statements, + }; + (root, request) } #[test] - fn only_failed_database_operations_retry_pool_exhaustion() { - let copy_failure = NativeError::Database( - "COPY failed: Buffer manager exception: the buffer pool is full".to_string(), - ); - let checkpoint_failure = NativeError::Database( - "checkpoint after COPY failed: the buffer pool is full".to_string(), - ); - let budget_failure = NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new( - "ladybug_copy", - 768 * 1024 * 1024, - 760 * 1024 * 1024, - 760 * 1024 * 1024, - )); + fn isolated_phase_schedule_stops_after_first_error_without_replay() { + let successful_schedule = ["schema", "copy0", "index", "copy1", "copy2", "postcopy"]; + for failure_index in 0..successful_schedule.len() { + let (root, request) = request(2); + fs::create_dir_all(&root).unwrap(); + let calls = RefCell::new(Vec::new()); + let result = write_database_isolated_with(&request, |request, phase| { + let ordinal = calls.borrow().len(); + let phase_request = phase_request(request, phase.clone()); + let encoded = serde_json::to_value(&phase_request).unwrap(); + assert!(encoded.get("buffer_pool_bytes").is_none()); + assert!(encoded.get("worker_memory_bytes").is_none()); + calls.borrow_mut().push(phase); + if ordinal == failure_index { + Err(NativeError::Database( + "first failure: buffer pool is full".into(), + )) + } else { + Ok(1) + } + }); + assert_eq!( + result.unwrap_err().to_string(), + "first failure: buffer pool is full" + ); + let phases = calls.into_inner(); + assert_eq!(phases.len(), failure_index + 1); + for (ordinal, phase) in phases.iter().enumerate() { + match (successful_schedule[ordinal], phase) { + ("schema", LadybugWritePhase::Schema { .. }) => {} + ("copy0", LadybugWritePhase::Copy { index: 0, .. }) => {} + ("copy1", LadybugWritePhase::Copy { index: 1, .. }) => {} + ("copy2", LadybugWritePhase::Copy { index: 2, .. }) => {} + ("index", LadybugWritePhase::Index { .. }) => {} + ("postcopy", LadybugWritePhase::PostCopy { .. }) => {} + other => panic!("unexpected phase schedule row: {other:?}"), + } + } + let _ = fs::remove_dir_all(root); + } + } - assert!(is_retryable_buffer_pool_exhaustion(©_failure)); - assert!(!is_retryable_buffer_pool_exhaustion(&checkpoint_failure)); - assert!(!is_retryable_buffer_pool_exhaustion(&budget_failure)); + #[test] + fn query_errors_are_not_suppressed_by_duplicate_ddl_wording() { + let root = std::env::temp_dir().join(format!("writer-query-test-{}", std::process::id())); + fs::create_dir_all(&root).unwrap(); + let path = root.join("db.lbug"); + let database = open_ladybug_database_with_threads(&path, false, 1).unwrap(); + let connection = connect_ladybug_database(&database).unwrap(); + run_schema_query( + &connection, + "CREATE NODE TABLE IF NOT EXISTS `Thing`(`id` STRING PRIMARY KEY)", + ) + .unwrap(); + run_schema_query( + &connection, + "CREATE NODE TABLE IF NOT EXISTS `Thing`(`id` STRING PRIMARY KEY)", + ) + .unwrap(); + let error = run_schema_query( + &connection, + "CREATE NODE TABLE `Thing`(`id` STRING PRIMARY KEY)", + ) + .unwrap_err(); + let message = error.to_string().to_lowercase(); + assert!(message.contains("already exists") || message.contains("exists already")); + drop(connection); + drop(database); + fs::remove_dir_all(root).unwrap(); } } diff --git a/src/error.rs b/src/error.rs index 055814f0..39482602 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,42 +1,5 @@ -use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct MemoryBudgetExceeded { - pub phase: String, - pub limit_bytes: u64, - pub accounted_bytes: u64, - pub observed_rss_bytes: u64, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub parent_rss_bytes: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub child_rss_bytes: Option, -} - -impl MemoryBudgetExceeded { - pub(crate) fn new( - phase: impl Into, - limit_bytes: u64, - accounted_bytes: u64, - observed_rss_bytes: u64, - ) -> Self { - Self { - phase: phase.into(), - limit_bytes, - accounted_bytes, - observed_rss_bytes, - parent_rss_bytes: None, - child_rss_bytes: None, - } - } - - pub(crate) fn with_process_rss(mut self, parent_bytes: u64, child_bytes: u64) -> Self { - self.parent_rss_bytes = Some(parent_bytes); - self.child_rss_bytes = Some(child_bytes); - self - } -} - #[derive(Debug)] pub enum NativeError { Io(std::io::Error), @@ -44,7 +7,6 @@ pub enum NativeError { Database(String), InvalidInput(String), SourceChanged { path: String }, - MemoryBudgetExceeded(MemoryBudgetExceeded), Unsupported(String), } @@ -64,22 +26,6 @@ impl fmt::Display for NativeError { }); write!(formatter, "{payload}") } - NativeError::MemoryBudgetExceeded(error) => { - let mut payload = serde_json::json!({ - "error": "memory_budget_exceeded", - "phase": error.phase, - "limit_bytes": error.limit_bytes, - "accounted_bytes": error.accounted_bytes, - "observed_rss_bytes": error.observed_rss_bytes, - }); - if let Some(parent_bytes) = error.parent_rss_bytes { - payload["parent_rss_bytes"] = serde_json::json!(parent_bytes); - } - if let Some(child_bytes) = error.child_rss_bytes { - payload["child_rss_bytes"] = serde_json::json!(child_bytes); - } - write!(formatter, "{payload}") - } NativeError::Unsupported(message) => write!(formatter, "{message}"), } } @@ -103,32 +49,6 @@ impl From for NativeError { mod tests { use super::*; - #[test] - fn memory_budget_failure_has_a_structured_machine_readable_message() { - let error = - NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new("parse", 1024, 2048, 512)); - let value: serde_json::Value = serde_json::from_str(&error.to_string()).unwrap(); - - assert_eq!(value["error"], "memory_budget_exceeded"); - assert_eq!(value["phase"], "parse"); - assert_eq!(value["limit_bytes"], 1024); - assert_eq!(value["accounted_bytes"], 2048); - assert_eq!(value["observed_rss_bytes"], 512); - assert!(value.get("parent_rss_bytes").is_none()); - assert!(value.get("child_rss_bytes").is_none()); - } - - #[test] - fn memory_budget_failure_can_report_supervised_process_split() { - let error = NativeError::MemoryBudgetExceeded( - MemoryBudgetExceeded::new("ladybug_copy", 1024, 900, 900).with_process_rss(300, 600), - ); - let value: serde_json::Value = serde_json::from_str(&error.to_string()).unwrap(); - - assert_eq!(value["parent_rss_bytes"], 300); - assert_eq!(value["child_rss_bytes"], 600); - } - #[test] fn source_change_failure_has_a_stable_machine_readable_message() { let error = NativeError::SourceChanged { diff --git a/src/execution/parallel.rs b/src/execution/parallel.rs index f786a998..85ace916 100644 --- a/src/execution/parallel.rs +++ b/src/execution/parallel.rs @@ -1,6 +1,6 @@ use super::timing::elapsed_seconds; use crate::artifact_store::{ArtifactExpectations, ArtifactStore}; -use crate::error::{MemoryBudgetExceeded, NativeError}; +use crate::error::NativeError; use crate::hash; use crate::parser; use crate::partition_builder; @@ -8,6 +8,7 @@ use crate::protocol::{LanguageProfile, NativeSyntaxMaterializationRequest, Sourc use crate::scan; use std::fs::File; use std::io::Read; +use std::path::Path; use std::sync::{mpsc, Mutex}; use std::thread; use std::time::Instant; @@ -28,6 +29,7 @@ pub(super) struct PartitionBuildResult { pub(super) parse_seconds: f64, pub(super) graph_build_seconds: f64, pub(super) reused_artifact: bool, + pub(super) transient: Option, pub(super) accounted_bytes: u64, } @@ -38,16 +40,18 @@ pub(super) fn build_execution_plan( mut consume: impl FnMut(usize, PartitionBuildResult) -> Result<(), NativeError>, ) -> Result { let request = &scan.input; + let transient_plan_bytes = crate::artifact_store::TransientPartition::plan_bytes( + &request.staging_dir, + scan.supported.keys().map(String::as_str), + ); let worker_count = resolved_worker_count(request, rebuild_paths.len()); let mut stats = ExecutionPlanStats::default(); if worker_count > 1 { let mut result_slots = Vec::new(); - result_slots.try_reserve(worker_count).map_err(|_| { - memory_budget_error( - "execution_plan", - request.rust_memory_limit_bytes().unwrap_or(u64::MAX), - request.rust_memory_limit_bytes().unwrap_or(u64::MAX), - ) + result_slots.try_reserve(worker_count).map_err(|error| { + NativeError::Io(std::io::Error::other(format!( + "failed to reserve parallel result window: {error}" + ))) })?; result_slots.resize_with(worker_count, || None); let (job_tx, job_rx) = mpsc::sync_channel::(worker_count); @@ -80,7 +84,6 @@ pub(super) fn build_execution_plan( scan, &rebuild_paths[index], &artifact_store, - worker_count, ), ); if result_tx.send(result).is_err() { @@ -120,7 +123,7 @@ pub(super) fn build_execution_plan( result .accounted_bytes .saturating_mul(worker_count as u64) - .min(request.rust_memory_limit_bytes().unwrap_or(u64::MAX)), + .saturating_add(transient_plan_bytes), ); consume(next_consume, result)?; next_consume += 1; @@ -149,8 +152,10 @@ pub(super) fn build_execution_plan( } for (index, path) in rebuild_paths.iter().enumerate() { - let result = build_partition_for_path_with_workers(scan, path, artifact_store, 1)?; - stats.high_water_bytes = stats.high_water_bytes.max(result.accounted_bytes); + let result = build_partition_for_path_with_workers(scan, path, artifact_store)?; + stats.high_water_bytes = stats + .high_water_bytes + .max(result.accounted_bytes.saturating_add(transient_plan_bytes)); consume(index, result)?; } Ok(stats) @@ -168,7 +173,6 @@ fn build_partition_for_path_with_workers( scan: &scan::SourceScan, path: &str, artifact_store: &ArtifactStore, - worker_count: usize, ) -> Result { let Some(snapshot) = scan.supported.get(path) else { return Err(NativeError::InvalidInput(format!( @@ -206,7 +210,6 @@ fn build_partition_for_path_with_workers( profile, previous_entry, artifact_store, - worker_count, ) } @@ -216,32 +219,26 @@ fn build_partition_for_snapshot( profile: &LanguageProfile, previous_entry: Option<&crate::protocol::ManifestEntry>, artifact_store: &ArtifactStore, - worker_count: usize, ) -> Result { - let per_worker_limit = request - .rust_memory_limit_bytes()? - .checked_div(worker_count.max(1) as u64) - .unwrap_or(0); let artifact_key = ArtifactStore::key_for_request(request, snapshot, Some(profile))?; if previous_entry.and_then(|entry| entry.artifact_key.as_deref()) == Some(artifact_key.as_str()) { - if let Some(partition) = artifact_store.load_partition_with_budget( + if let Some(partition) = artifact_store.load_partition( &artifact_key, &ArtifactExpectations { path: &snapshot.path, content_hash: &snapshot.content_hash, language: snapshot.language.as_deref().unwrap_or_default(), }, - per_worker_limit, )? { - let accounted_bytes = - ensure_partition_fits(&partition, snapshot.byte_len, per_worker_limit)?; + let accounted_bytes = partition_accounted_bytes(&partition, snapshot.byte_len); return Ok(PartitionBuildResult { partition, diagnostics: Vec::new(), parse_seconds: 0.0, graph_build_seconds: 0.0, reused_artifact: true, + transient: None, accounted_bytes, }); } @@ -249,7 +246,7 @@ fn build_partition_for_snapshot( let snapshot = scan::spool_snapshot_for_build(request, snapshot)?; let parse_started = Instant::now(); let mut parse_snapshot = snapshot.clone(); - parse_snapshot.source = Some(read_snapshotted_source(&snapshot, per_worker_limit)?); + parse_snapshot.source = Some(read_snapshotted_source(&snapshot)?); let parse = parser::parse_file(&parse_snapshot, profile)?; drop(parse_snapshot); let parse_seconds = elapsed_seconds(parse_started); @@ -257,39 +254,47 @@ fn build_partition_for_snapshot( let graph_build_started = Instant::now(); let mut partition = partition_builder::build_partition(request, &snapshot, parse)?; partition.set_artifact_key(artifact_key.clone()); - let accounted_bytes = ensure_partition_fits(&partition, snapshot.byte_len, per_worker_limit)?; + let accounted_bytes = partition_accounted_bytes(&partition, snapshot.byte_len); let graph_build_seconds = elapsed_seconds(graph_build_started); - let _ = artifact_store.store_partition(&artifact_key, &partition)?; + let transient = match artifact_store.store_partition(&artifact_key, &partition) { + Ok(_) => None, + Err(error) if crate::artifact_store::is_canonical_payload_conflict(&error) => { + Some(crate::artifact_store::TransientPartition::create( + Path::new(&request.staging_dir), + &artifact_key, + &partition, + )?) + } + Err(error) => return Err(error), + }; + let mut diagnostics = diagnostics; + if transient.is_some() { + diagnostics.push(format!("artifact cache publication conflict for {}; rebuilt from scanned source using a per-run copy; canonical cache preserved", snapshot.path)); + } Ok(PartitionBuildResult { partition, diagnostics, parse_seconds, graph_build_seconds, reused_artifact: false, + transient, accounted_bytes, }) } -fn read_snapshotted_source( - snapshot: &SourceSnapshot, - per_worker_limit: u64, -) -> Result { - let accounted_bytes = snapshot - .byte_len - .saturating_mul(PARSE_WORKING_BYTES_PER_SOURCE_BYTE); - if accounted_bytes > per_worker_limit { - return Err(memory_budget_error( - "source_parse", - per_worker_limit, - accounted_bytes, - )); - } - let source_capacity = usize::try_from(snapshot.byte_len) - .map_err(|_| memory_budget_error("source_parse", per_worker_limit, snapshot.byte_len))?; +fn read_snapshotted_source(snapshot: &SourceSnapshot) -> Result { + let source_capacity = usize::try_from(snapshot.byte_len).map_err(|_| { + NativeError::InvalidInput(format!( + "source is too large to represent on this platform: {}", + snapshot.path + )) + })?; let mut source = String::new(); - source - .try_reserve_exact(source_capacity) - .map_err(|_| memory_budget_error("source_parse", per_worker_limit, snapshot.byte_len))?; + source.try_reserve_exact(source_capacity).map_err(|error| { + NativeError::Io(std::io::Error::other(format!( + "failed to reserve source buffer: {error}" + ))) + })?; File::open(&snapshot.absolute_path)?.read_to_string(&mut source)?; if source.len() as u64 != snapshot.byte_len || hash::sha256_file(std::path::Path::new(&snapshot.absolute_path))? @@ -303,22 +308,13 @@ fn read_snapshotted_source( Ok(source) } -fn ensure_partition_fits( +fn partition_accounted_bytes( partition: &partition_builder::GraphPartition, source_bytes: u64, - per_worker_limit: u64, -) -> Result { +) -> u64 { let source_working = source_bytes.saturating_mul(PARSE_WORKING_BYTES_PER_SOURCE_BYTE); let partition_bytes = estimate_partition_bytes(partition).unwrap_or(u64::MAX); - let accounted_bytes = partition_working_bytes(source_working, partition_bytes); - if accounted_bytes > per_worker_limit { - return Err(memory_budget_error( - "partition_build", - per_worker_limit, - accounted_bytes, - )); - } - Ok(accounted_bytes) + partition_working_bytes(source_working, partition_bytes) } fn partition_working_bytes(source_working: u64, partition_bytes: u64) -> u64 { @@ -377,15 +373,6 @@ fn checked_add_strings(mut bytes: u64, strings: [&String; N]) -> Some(bytes) } -fn memory_budget_error(phase: &str, limit_bytes: u64, accounted_bytes: u64) -> NativeError { - NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new( - phase, - limit_bytes, - accounted_bytes, - 0, - )) -} - #[cfg(test)] mod tests { use super::*; @@ -438,8 +425,6 @@ mod tests { atomic_rebuild: false, strict: true, parallel: false, - worker_memory_mib: 768, - rust_memory_mib: 384, spill_chunk_mib: 32, max_parallelism: 2, progress: false, @@ -533,7 +518,6 @@ mod tests { vec!["src/a.rs", "src/b.rs", "src/c.rs", "src/d.rs"] ); assert!(stats.high_water_bytes > 0); - assert!(stats.high_water_bytes <= request.rust_memory_limit_bytes().unwrap()); let _ = fs::remove_dir_all(root); } @@ -611,33 +595,4 @@ mod tests { assert_eq!(reused, Some(false)); let _ = fs::remove_dir_all(root); } - - #[test] - fn oversized_source_returns_structured_budget_failure() { - let snapshot = SourceSnapshot { - path: "src/huge.rs".to_string(), - absolute_path: "missing".to_string(), - content_hash: "hash".to_string(), - language: Some("rust".to_string()), - byte_len: 2 * 1024 * 1024, - source: None, - }; - - let error = read_snapshotted_source(&snapshot, 1024 * 1024).unwrap_err(); - let NativeError::MemoryBudgetExceeded(error) = error else { - panic!("oversized source should return a memory budget error"); - }; - assert_eq!(error.phase, "source_parse"); - assert_eq!(error.limit_bytes, 1024 * 1024); - assert!(error.accounted_bytes > error.limit_bytes); - assert_eq!(error.observed_rss_bytes, 0); - } - - #[test] - fn reference_sized_partition_fits_the_default_per_worker_budget() { - let reference_partition_bytes = 77 * 1024 * 1024; - let per_worker_budget_bytes = 192 * 1024 * 1024; - - assert!(partition_working_bytes(0, reference_partition_bytes) <= per_worker_budget_bytes); - } } diff --git a/src/execution/run.rs b/src/execution/run.rs index e6ac5700..428309c5 100644 --- a/src/execution/run.rs +++ b/src/execution/run.rs @@ -1,7 +1,7 @@ use super::parallel::build_execution_plan; use super::timing::elapsed_seconds; use crate::artifact_store::{ArtifactExpectations, ArtifactStore}; -use crate::error::{MemoryBudgetExceeded, NativeError}; +use crate::error::NativeError; use crate::partition_builder::GraphPartition; use crate::protocol::{ GraphSummary, ManifestEntry, NativeSyntaxMaterializationRequest, @@ -13,18 +13,34 @@ use std::fs; use std::path::Path; use std::time::Instant; -// Ladybug's bundled JSON reader does not consolidate a list or glob of JSON -// files, so production currently loads one complete table document at a time. -const COPY_FILE_CHUNK_BYTES: usize = usize::MAX; +#[cfg(test)] +thread_local! { + static OPERATION_FINISHED_HOOK: std::cell::RefCell>> = const { std::cell::RefCell::new(None) }; +} pub fn execute_materialization_pipeline( request: &NativeSyntaxMaterializationRequest, ) -> Result { + // Keep use ownership over scan, artifact persistence, sidecar construction, + // native helper lifetimes and final result assembly. + let _artifact_use = crate::storage::maintenance_ownership::ArtifactUseLease::acquire_shared( + &request.resolved_artifact_root(), + )?; + let _database_use = crate::storage::maintenance_ownership::DatabaseUseLease::acquire_shared( + Path::new(&request.db_path), + )?; let mut phase_timings = BTreeMap::new(); let scan_started = Instant::now(); let scan = scan::scan_sources(request)?; phase_timings.insert("scan_seconds".to_string(), elapsed_seconds(scan_started)); - execute_scanned_materialization(scan, phase_timings) + let result = execute_scanned_materialization(scan, phase_timings); + #[cfg(test)] + OPERATION_FINISHED_HOOK.with(|hook| { + if let Some(hook) = hook.borrow_mut().take() { + hook(); + } + }); + result } fn execute_scanned_materialization( @@ -41,17 +57,14 @@ fn execute_scanned_materialization( .ok() .and_then(|mib| mib.checked_mul(1024 * 1024)) .ok_or_else(|| { - NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new( - "staging_spill_configuration", - request.rust_memory_limit_bytes().unwrap_or(u64::MAX), - u64::MAX, - 0, - )) + NativeError::InvalidInput( + "spill chunk size cannot be represented on this platform".into(), + ) })?; let mut staging_accumulator = staging_writer::StagingAccumulator::with_limits( &request.staging_dir, spill_chunk_bytes, - COPY_FILE_CHUNK_BYTES, + spill_chunk_bytes, )?; let materialization_paths = scan.supported.keys().cloned().collect::>(); let materialization_total = materialization_paths.len(); @@ -65,6 +78,15 @@ fn execute_scanned_materialization( // proportional to the number of source files. let mut progress_events = BTreeMap::new(); let mut planned_entries = BTreeMap::new(); + let mut transient_partitions = Vec::new(); + transient_partitions + .try_reserve_exact(materialization_total) + .map_err(|error| { + NativeError::Io(std::io::Error::other(format!( + "failed to reserve transient artifact slots: {error}" + ))) + })?; + transient_partitions.resize_with(materialization_total, || None); let mut parsed_paths = BTreeSet::new(); let mut artifacts_reused = 0usize; let mut artifacts_rebuilt = 0usize; @@ -94,6 +116,9 @@ fn execute_scanned_materialization( }, ); } + if let Some(transient) = result.transient { + transient_partitions[index] = Some(transient); + } planned_entries.insert( result.partition.entry.path.clone(), result.partition.entry.compact(), @@ -114,17 +139,20 @@ fn execute_scanned_materialization( diff, } = scan; diagnostics.extend(parse_diagnostics); - let artifact_memory_limit = request.rust_memory_limit_bytes()?; let mut materialized_entries = BTreeMap::new(); let mut rebuilt_entries = BTreeMap::new(); for (index, path) in materialization_paths.iter().enumerate() { - let partition = load_planned_partition( - &supported, - path, - &planned_entries, - &artifact_store, - artifact_memory_limit, - )?; + let transient = transient_partitions[index].take(); + let partition = if let Some(transient) = transient.as_ref() { + let snapshot = &supported[path]; + transient.load(&ArtifactExpectations { + path, + content_hash: &snapshot.content_hash, + language: snapshot.language.as_deref().unwrap_or_default(), + })? + } else { + load_planned_partition(&supported, path, &planned_entries, &artifact_store)? + }; let compact_entry = partition.entry.clone().compact(); materialized_entries.insert(path.clone(), compact_entry.clone()); if parsed_paths.contains(path) { @@ -132,6 +160,7 @@ fn execute_scanned_materialization( } let staging_started = Instant::now(); staging_accumulator.add_partition(&partition); + drop(transient); staging_seconds += elapsed_seconds(staging_started); if request.progress { progress_events.insert( @@ -235,7 +264,6 @@ fn load_planned_partition( path: &str, planned_entries: &BTreeMap, artifact_store: &ArtifactStore, - memory_limit_bytes: u64, ) -> Result { let snapshot = supported.get(path).ok_or_else(|| { NativeError::InvalidInput(format!( @@ -253,14 +281,13 @@ fn load_planned_partition( )) })?; artifact_store - .load_partition_with_budget( + .load_partition( artifact_key, &ArtifactExpectations { path, content_hash: &snapshot.content_hash, language: snapshot.language.as_deref().unwrap_or_default(), }, - memory_limit_bytes, )? .ok_or_else(|| { NativeError::InvalidInput(format!( @@ -288,6 +315,108 @@ mod tests { use std::path::{Path, PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; + #[test] + fn dc_s2_public_pipeline_keeps_use_guards_through_final_result_assembly() { + use crate::storage::maintenance_ownership::{ArtifactUseLease, DatabaseUseLease}; + use std::sync::mpsc; + use std::time::Duration; + let root = unique_temp_dir("codebase-graph-s2-public-operation"); + let source = root.join("repo"); + let state = root.join("state"); + fs::create_dir_all(&source).unwrap(); + fs::write(source.join("lib.rs"), "pub fn guarded() {}\n").unwrap(); + let fresh = crate::storage::layout::create_fresh_reference_root(&state) + .unwrap() + .unwrap(); + let layout = crate::storage::layout::ManagedLayout::new(&state); + layout.ensure_roots().unwrap(); + let writer = crate::storage::locks::open_locked( + layout.writer_lock_path(), + crate::storage::locks::LockMode::Exclusive, + ) + .unwrap(); + let domain = + crate::storage::maintenance_ownership::ReferenceDomain::managed(&layout).unwrap(); + assert!( + crate::storage::maintenance_ownership::initialize_fresh_reference_domain( + &domain, &fresh, &writer + ) + .unwrap() + ); + drop(writer); + let before = match crate::storage::maintenance_ownership::observe_reference_epoch(&domain) + .unwrap() + { + crate::storage::maintenance_ownership::EpochObservation::Verified(epoch) => epoch, + other => panic!("{other:?}"), + }; + let mut request = request(&source, &state, None, Vec::new()); + request.artifact_root = layout.artifacts_root().to_string_lossy().into_owned(); + let artifact = request.resolved_artifact_root(); + let database = PathBuf::from(&request.db_path); + let (ready_tx, ready_rx) = mpsc::channel(); + let (resume_tx, resume_rx) = mpsc::channel(); + let worker = std::thread::spawn(move || { + OPERATION_FINISHED_HOOK.with(|hook| { + *hook.borrow_mut() = Some(Box::new(move || { + ready_tx.send(()).unwrap(); + resume_rx.recv_timeout(Duration::from_secs(30)).unwrap(); + })) + }); + execute_materialization_pipeline(&request) + }); + ready_rx.recv_timeout(Duration::from_secs(60)).unwrap(); + assert!(artifact.is_dir()); + assert!(matches!( + crate::storage::maintenance_ownership::observe_reference_epoch(&domain).unwrap(), + crate::storage::maintenance_ownership::EpochObservation::Pending(_) + )); + assert!(ArtifactUseLease::try_exclusive(&artifact) + .unwrap() + .is_none()); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_none()); + resume_tx.send(()).unwrap(); + assert!(worker.join().unwrap().unwrap().database_written); + assert!(ArtifactUseLease::try_exclusive(&artifact) + .unwrap() + .is_some()); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_some()); + let after = match crate::storage::maintenance_ownership::observe_reference_epoch(&domain) + .unwrap() + { + crate::storage::maintenance_ownership::EpochObservation::Verified(epoch) => epoch, + other => panic!("{other:?}"), + }; + assert!(after.counter() > before.counter()); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn dc_s2_pure_planning_does_not_create_use_coordination_files() { + let root = unique_temp_dir("codebase-graph-s2-pure-plan"); + let source = root.join("repo"); + let state = root.join("state"); + fs::create_dir_all(&source).unwrap(); + fs::write(source.join("lib.rs"), "pub fn planned() {}\n").unwrap(); + let request = request(&source, &state, None, Vec::new()); + crate::execution::plan_materialization(&request).unwrap(); + assert!(!request.resolved_artifact_root().exists()); + // Scan keeps its established snapshot/staging behavior; pure planning + // adds neither artifact-use nor database-use coordination files. + if state.exists() { + assert!(!fs::read_dir(&state).unwrap().any(|entry| entry + .unwrap() + .file_name() + .to_string_lossy() + .starts_with(".database-use-"))); + } + fs::remove_dir_all(root).unwrap(); + } + #[test] fn scanned_payload_completes_materialization_after_source_removal() { let root = unique_temp_dir("codebase-graph-scanned-pipeline"); @@ -426,6 +555,11 @@ mod tests { .join(&corrupt_key) .join("partition.json"); fs::write(&corrupt_path, "{not-json").unwrap(); + let corrupt_identity = crate::storage::locks::path_identity(&corrupt_path).unwrap(); + let foreign_neighbor = corrupt_path.parent().unwrap().join("foreign-neighbor"); + fs::write(&foreign_neighbor, b"preserve foreign cache sibling").unwrap(); + let foreign_identity = crate::storage::locks::path_identity(&foreign_neighbor).unwrap(); + let corrupt_previous = manifest_from_response(&response); let corrupt_state = root.join("state-corrupt"); @@ -438,6 +572,30 @@ mod tests { )) .unwrap(); assert!(corrupt_response.artifacts_rebuilt >= 1); + assert_eq!(fs::read(&corrupt_path).unwrap(), b"{not-json"); + assert_eq!( + crate::storage::locks::path_identity(&corrupt_path).unwrap(), + corrupt_identity + ); + assert_eq!( + fs::read(&foreign_neighbor).unwrap(), + b"preserve foreign cache sibling" + ); + assert_eq!( + crate::storage::locks::path_identity(&foreign_neighbor).unwrap(), + foreign_identity + ); + assert!(corrupt_response + .diagnostics + .iter() + .any(|message| message.contains("artifact cache publication conflict"))); + assert!(!fs::read_dir(corrupt_state.join("staging")) + .unwrap() + .filter_map(Result::ok) + .any(|entry| entry + .file_name() + .to_string_lossy() + .starts_with(".partition-fallback-"))); let _ = fs::remove_dir_all(root); } @@ -721,8 +879,6 @@ mod tests { atomic_rebuild: false, strict: true, parallel: false, - worker_memory_mib: 768, - rust_memory_mib: 384, spill_chunk_mib: 32, max_parallelism: 2, progress: false, diff --git a/src/lib.rs b/src/lib.rs index 8d156649..c0325c2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,12 @@ +#[cfg(not(test))] #[global_allocator] static GLOBAL_ALLOCATOR: mimalloc::MiMalloc = mimalloc::MiMalloc; +#[cfg(test)] +#[global_allocator] +static GLOBAL_ALLOCATOR: storage::maintenance_scheduler::allocation_boundary_test::Allocator = + storage::maintenance_scheduler::allocation_boundary_test::Allocator; + pub mod adapters; pub mod agent_hooks; pub mod api; @@ -27,7 +33,7 @@ mod staging_writer; mod storage; mod syntax_materializer; -pub use error::{MaterializationError, MemoryBudgetExceeded}; +pub use error::MaterializationError; pub use execution::{execute_materialization_pipeline, plan_materialization}; pub use protocol::{MaterializationInput, MaterializationResult}; diff --git a/src/materialization_worker.rs b/src/materialization_worker.rs index 5d27904d..1fd60afa 100644 --- a/src/materialization_worker.rs +++ b/src/materialization_worker.rs @@ -1,4 +1,4 @@ -use crate::api::context::{resolve_runtime, RepoRuntime, DEFAULT_WORKER_MEMORY_MIB}; +use crate::api::context::{resolve_runtime, RepoRuntime}; use crate::api::{ execute_candidate_materialization, execute_materialization, read_manifest, MaterializeOptions, RepoSelector, @@ -15,8 +15,10 @@ use crate::storage::layout::{managed_generation_id, DirectLayout, ManagedLayout} use crate::storage::locks::{try_open_locked, LockMode, WorkerLease}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; -use std::fs::{self, File, OpenOptions}; -use std::io::{BufRead, BufReader, BufWriter, Write}; +#[cfg(test)] +use std::fs::OpenOptions; +use std::fs::{self, File}; +use std::io::{BufRead, BufReader, Write}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::OnceLock; @@ -28,7 +30,6 @@ const WORKER_STATE_VERSION: u64 = 2; const MAX_PROGRESS_FRAME_BYTES: usize = 64 * 1024; const MAX_CHILD_ERROR_BYTES: usize = 64 * 1024; const SUPERVISOR_CHECK_INTERVAL: Duration = Duration::from_millis(50); -const MIB: u64 = 1024 * 1024; const MATERIALIZATION_CANCELLED_ERROR: &str = r#"{"error":"materialization_cancelled"}"#; static WORKER_EXECUTABLE: OnceLock = OnceLock::new(); static WORKER_SEQUENCE: AtomicU64 = AtomicU64::new(0); @@ -94,7 +95,7 @@ pub(crate) fn execute_explicit_worker( fn execute_worker( options: &MaterializeOptions, candidate_paths: Option>, - mut worker_pid: impl FnMut(Option), + worker_pid: impl FnMut(Option), should_cancel: impl Fn() -> bool, wait_for_lock: bool, ) -> Result { @@ -112,6 +113,25 @@ fn execute_worker( } .map(|(_, response)| response); }; + execute_isolated_worker( + options, + candidate_paths, + worker_pid, + should_cancel, + wait_for_lock, + executable, + ) +} + +fn execute_isolated_worker( + options: &MaterializeOptions, + candidate_paths: Option>, + mut worker_pid: impl FnMut(Option), + should_cancel: impl Fn() -> bool, + wait_for_lock: bool, + executable: &Path, +) -> Result { + initialize_fresh_worker_domain(options)?; let _lease = if wait_for_lock { acquire_worker_lease_cancellable(options, &should_cancel)? } else { @@ -125,16 +145,28 @@ fn execute_worker( return Err(materialization_cancelled_error()); } let build_id = managed_generation_id(); - let workspace = create_worker_workspace(options, &build_id)?; + let (workspace, worker_owner) = create_worker_workspace(options, &build_id)?; if should_cancel() { - cleanup_worker_workspace(&workspace); + finish_empty_worker_workspace(worker_owner, &workspace); return Err(materialization_cancelled_error()); } let request_path = workspace.join("request.json"); let result_path = workspace.join("result.json"); let stderr_path = workspace.join("stderr.log"); - let state_path = worker_state_path(options)?; - let before_publication = publication_marker(options)?; + // No containment exists before prepare; every error here can close the + // actual registered owner without changing the primary error. + let setup = (|| { + let state_path = worker_state_path(options)?; + let before_publication = publication_marker(options)?; + Ok::<_, String>((state_path, before_publication)) + })(); + let (state_path, before_publication) = match setup { + Ok(setup) => setup, + Err(error) => { + finish_empty_worker_workspace(worker_owner, &workspace); + return Err(error); + } + }; if let Err(error) = write_worker_request( &request_path, &MaterializationWorkerRequest { @@ -144,21 +176,13 @@ fn execute_worker( candidate_paths, }, ) { - cleanup_worker_workspace(&workspace); + finish_empty_worker_workspace(worker_owner, &workspace); return Err(error); } - let memory_limit = options - .worker_memory_mib - .unwrap_or(DEFAULT_WORKER_MEMORY_MIB) - .checked_mul(MIB) - .ok_or_else(|| { - cleanup_worker_workspace(&workspace); - "materialization worker memory limit overflowed".to_string() - })?; let mut child = match PreparedChild::prepare(SpawnSpec { - program: executable.clone(), - guardian_executable: Some(executable.clone()), + program: executable.to_path_buf(), + guardian_executable: Some(executable.to_path_buf()), args: vec![ "__codebase_graph_internal".into(), "materialization-worker-v2".into(), @@ -168,15 +192,13 @@ fn execute_worker( workspace: workspace.clone(), stderr_path: stderr_path.clone(), stdout: OutputMode::Pipe, - memory_limit_bytes: Some(memory_limit), - memory_phase: Some("materialization_worker".into()), parent_rss_pid: None, }) { Ok(child) => child, Err(error) => { // A failed READY may still have a guardian or contained descendants. if process_lifecycle::cleanup_if_inactive(&workspace).unwrap_or(false) { - cleanup_worker_workspace(&workspace); + finish_empty_worker_workspace(worker_owner, &workspace); return Err(error); } return Err(containment_unknown_error(error)); @@ -193,7 +215,7 @@ fn execute_worker( match child.terminate("state persistence failed") { Ok(outcome) if outcome.containment_empty => { join_progress(progress.take()); - cleanup_worker_workspace(&workspace); + finish_empty_worker_workspace(worker_owner, &workspace); return Err(format!("failed to record materialization worker state: {error}")); } _ => return Err(containment_unknown_error(format!( @@ -219,6 +241,7 @@ fn execute_worker( match child.terminate("supervision failed") { Ok(outcome) if outcome.containment_empty => { join_progress(progress.take()); + finish_empty_worker_workspace(worker_owner, &workspace); clear_worker_execution_state( &state_path, &descriptor.build_id, @@ -242,6 +265,7 @@ fn execute_worker( } join_progress(progress); if supervision.cancelled { + finish_empty_worker_workspace(worker_owner, &workspace); clear_worker_execution_state( &state_path, &descriptor.build_id, @@ -252,9 +276,6 @@ fn execute_worker( } let outcome = if result_path.exists() { read_worker_result(&result_path, &build_id).and_then(|result| result.outcome) - } else if let Some(error) = &supervision.budget_reason { - reconcile_completed_publication(options, before_publication, &build_id) - .or(Err(error.clone())) } else if supervision.success { reconcile_completed_publication(options, before_publication, &build_id) } else { @@ -271,7 +292,7 @@ fn execute_worker( response }); remove_worker_state(&state_path, &descriptor.build_id); - cleanup_worker_workspace(&workspace); + finish_empty_worker_workspace(worker_owner, &workspace); outcome } @@ -382,7 +403,7 @@ pub(crate) fn execute_worker_file(request_path: &Path, result_path: &Path) -> Re compact_refresh_worker_result(response); } } - write_json_atomically( + process_lifecycle::write_private_json( result_path, &MaterializationWorkerResult { version: WORKER_PROTOCOL_VERSION, @@ -505,13 +526,76 @@ fn recover_orphan_worker( if should_cancel() { return Err(materialization_cancelled_error()); } - cleanup_abandoned_workspaces(options) + Ok(()) +} + +fn initialize_fresh_worker_domain(options: &MaterializeOptions) -> Result<(), String> { + let Some(storage) = options.storage_root.as_ref() else { + if let (Some(database), Some(manifest)) = (&options.db, &options.manifest) { + crate::storage::maintenance_ownership::initialize_fresh_direct_domain( + &crate::storage::layout::DirectLayout::new(database, manifest), + ) + .map_err(|error| error.to_string())?; + } + return Ok(()); + }; + crate::storage::maintenance_ownership::initialize_fresh_managed_domain(&ManagedLayout::new( + storage, + )) + .map_err(|error| error.to_string())?; + Ok(()) +} + +struct WorkerWorkspaceOwner { + registration: crate::storage::maintenance_controls::WorkspaceRegistration, + owner: crate::storage::locks::LockedFile, + mutation: crate::storage::maintenance_ownership::ReferenceMutation<'static>, +} +impl WorkerWorkspaceOwner { + fn finish_if_empty(self) { + let _ = self.registration.terminal( + &self.owner, + &self.mutation, + crate::storage::maintenance_controls::WorkspaceDisposition::PublishedConsumed, + ); + } +} + +/// Call only before child preparation or after authenticated containment-empty +/// proof. Unknown teardown paths deliberately retain their Building control. +fn finish_empty_worker_workspace(owner: Option, workspace: &Path) { + if let Some(owner) = owner { + owner.finish_if_empty(); + } + cleanup_worker_workspace(workspace); } fn create_worker_workspace( options: &MaterializeOptions, build_id: &str, -) -> Result { +) -> Result<(PathBuf, Option), String> { + let domain = match options.storage_root.as_ref() { + Some(storage) => crate::storage::maintenance_ownership::ReferenceDomain::managed( + &ManagedLayout::new(storage), + ) + .ok(), + None => options + .db + .as_ref() + .zip(options.manifest.as_ref()) + .and_then(|(db, manifest)| { + crate::storage::maintenance_ownership::ReferenceDomain::direct(&DirectLayout::new( + db, manifest, + )) + .ok() + }), + }; + let mutation = domain.as_ref().and_then(|domain| { + crate::storage::maintenance_ownership::ReferenceMutation::begin_in_domain(domain).ok() + }); + let _startup = domain.as_ref().and_then(|domain| { + crate::storage::locks::open_shared_coordination(&domain.workspace_startup_path()).ok() + }); let root = worker_workspace_root(options)?; fs::create_dir_all(&root) .map_err(|error| format!("failed to create materialization worker root: {error}"))?; @@ -528,9 +612,33 @@ fn create_worker_workspace( "worker-{build_id}-{}-{sequence}", std::process::id() )); - fs::create_dir(&workspace) - .map_err(|error| format!("failed to create materialization worker workspace: {error}"))?; - Ok(workspace) + let fresh = crate::storage::layout::create_fresh_reference_root(&workspace) + .map_err(|error| error.to_string())? + .ok_or_else(|| "worker workspace already exists".to_string())?; + let worker_owner = match (domain.as_ref(), mutation, _startup.as_ref()) { + (Some(domain), Some(mutation), Some(_)) => { + let anchor = crate::storage::locks::RootedDirectory::open(&workspace, fresh.identity()) + .map_err(|error| error.to_string())?; + let owner = crate::storage::locks::create_new_coordination_in( + &anchor, + b"lease.lock", + &workspace.join("lease.lock"), + ) + .map_err(|error| error.to_string())?; + crate::storage::maintenance_controls::WorkspaceRegistration::create( + &fresh, domain, &mutation, &owner, 2, + ) + .ok() + .flatten() + .map(|registration| WorkerWorkspaceOwner { + registration, + owner, + mutation, + }) + } + _ => None, + }; + Ok((workspace, worker_owner)) } fn worker_workspace_root(options: &MaterializeOptions) -> Result { @@ -548,71 +656,9 @@ fn worker_workspace_root(options: &MaterializeOptions) -> Result Result<(), String> { - let root = worker_workspace_root(options)?; - let entries = match fs::read_dir(&root) { - Ok(entries) => entries, - Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()), - Err(error) => { - return Err(format!( - "failed to inspect materialization worker workspaces: {error}" - )) - } - }; - let mut unknown = None; - for entry in entries { - let entry = entry.map_err(containment_unknown_error)?; - let path = entry.path(); - if !path - .file_name() - .and_then(|name| name.to_str()) - .is_some_and(|name| name.starts_with("worker-")) - { - continue; - } - let metadata = fs::symlink_metadata(&path).map_err(containment_unknown_error)?; - if metadata.file_type().is_symlink() || !metadata.is_dir() { - unknown.get_or_insert_with(|| { - format!( - "worker workspace must be a real directory: {}", - path.display() - ) - }); - continue; - } - match process_lifecycle::cleanup_if_inactive(&path) { - Ok(true) => cleanup_worker_workspace(&path), - Ok(false) => { - unknown.get_or_insert_with(|| { - format!( - "abandoned worker containment is still active or unproven: {}", - path.display() - ) - }); - } - Err(error) => { - unknown.get_or_insert(error); - } - } - } - match unknown { - Some(reason) => Err(containment_unknown_error(reason)), - None => Ok(()), - } -} - fn write_worker_request(path: &Path, request: &MaterializationWorkerRequest) -> Result<(), String> { - let file = OpenOptions::new() - .write(true) - .create_new(true) - .open(path) - .map_err(|error| format!("failed to create materialization worker request: {error}"))?; - let mut writer = BufWriter::new(file); - serde_json::to_writer(&mut writer, request) - .map_err(|error| format!("failed to encode materialization worker request: {error}"))?; - writer - .flush() - .map_err(|error| format!("failed to flush materialization worker request: {error}")) + process_lifecycle::write_private_json_new(path, request) + .map_err(|error| format!("failed to write materialization worker request: {error}")) } fn remove_worker_state(path: &Path, build_id: &str) { @@ -819,6 +865,10 @@ fn bounded_child_error(bytes: &[u8]) -> String { } fn cleanup_worker_workspace(path: &Path) { + if path.join(".workspace.binding").exists() { + return; + } + let is_owned_workspace = path .file_name() .and_then(|name| name.to_str()) @@ -836,7 +886,56 @@ fn cleanup_worker_workspace(path: &Path) { #[cfg(test)] mod tests { use super::*; - use crate::error::{MemoryBudgetExceeded, NativeError}; + + #[test] + fn empty_worker_cancellation_and_setup_failure_publish_terminal_owner() { + { + let root = std::env::temp_dir().canonicalize().unwrap().join(format!( + "cgraph-worker-terminal-{}-{}", + std::process::id(), + WORKER_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir_all(&root).unwrap(); + let storage = root.join("storage"); + let options = MaterializeOptions { + storage_root: Some(storage.clone()), + ..MaterializeOptions::default() + }; + let polls = AtomicU64::new(0); + let result = execute_isolated_worker( + &options, + None, + |_| {}, + || polls.fetch_add(1, Ordering::SeqCst) >= 3, + false, + &root.join("never-spawned"), + ); + let error = result.unwrap_err(); + assert_eq!(error, materialization_cancelled_error()); + let workspaces = fs::read_dir(storage.join("workers")) + .unwrap() + .map(|entry| entry.unwrap().path()) + .collect::>(); + assert_eq!(workspaces.len(), 1); + assert!( + workspaces[0].join(".workspace.binding").is_file(), + "test must exercise an actual registered workspace" + ); + let domain = crate::storage::maintenance_ownership::ReferenceDomain::managed( + &ManagedLayout::new(&storage), + ) + .unwrap(); + let memory = + crate::storage::maintenance_budget::CleanupMemory::new(8 * 1024 * 1024).unwrap(); + crate::storage::maintenance_controls::prepare_terminal_workspace_boxed( + &workspaces[0], + &domain, + &memory, + ) + .unwrap(); + fs::remove_dir_all(root).unwrap(); + } + } #[test] fn worker_request_ignores_retired_semantic_keys() { @@ -928,19 +1027,6 @@ mod tests { let _ = fs::remove_dir_all(root); } - #[test] - fn memory_failure_is_structured() { - let error = NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new( - "materialization_worker", - 768 * MIB, - 769 * MIB, - 769 * MIB, - )); - let value: serde_json::Value = serde_json::from_str(&error.to_string()).unwrap(); - assert_eq!(value["error"], serde_json::json!("memory_budget_exceeded")); - assert_eq!(value["phase"], serde_json::json!("materialization_worker")); - } - #[test] fn progress_drain_discards_oversized_frames_and_continues_bounded() { let valid = serde_json::to_vec(&WorkerProgressFrame { @@ -994,7 +1080,7 @@ mod tests { } #[test] - fn abandoned_workspace_without_global_state_retains_held_lifecycle() { + fn unrelated_workspace_without_global_state_is_preserved_without_blocking_current_request() { use fs2::FileExt; let root = std::env::temp_dir().join(format!( "codebase-graph-held-lifecycle-{}-{}", @@ -1017,15 +1103,22 @@ mod tests { .open(lifecycle.join("lease")) .unwrap(); lease.lock_exclusive().unwrap(); - let error = recover_orphan_worker(&options, &|| false).unwrap_err(); - assert_eq!( - serde_json::from_str::(&error).unwrap()["error"], - "containment_unknown" - ); + recover_orphan_worker(&options, &|| false).unwrap(); assert!(workspace.exists()); + assert!(fs2::FileExt::try_lock_exclusive( + &OpenOptions::new() + .read(true) + .write(true) + .open(lifecycle.join("lease")) + .unwrap() + ) + .is_err()); drop(lease); recover_orphan_worker(&options, &|| false).unwrap(); - assert!(!workspace.exists()); + assert!( + workspace.exists(), + "unrelated orphan collection is exclusively a bounded-controller duty" + ); fs::remove_dir_all(root).unwrap(); } diff --git a/src/process_lifecycle/mod.rs b/src/process_lifecycle/mod.rs index 57a762b1..7763c0b8 100644 --- a/src/process_lifecycle/mod.rs +++ b/src/process_lifecycle/mod.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use std::{ fmt, fs::{self, File, OpenOptions}, - io::{Read, Write}, + io::{BufWriter, Read, Write}, net::{TcpListener, TcpStream}, path::{Path, PathBuf}, process::{Child, ChildStdin, ChildStdout, Command, Stdio}, @@ -25,7 +25,6 @@ mod windows; use windows as platform; const TICK: Duration = Duration::from_millis(25); const DEADLINE: Duration = Duration::from_secs(5); -const HEADROOM: u64 = 16 * 1024 * 1024; const MAX_FRAME: usize = 4096; #[derive(Clone, Debug, Serialize, Deserialize)] @@ -42,9 +41,6 @@ pub(crate) struct SpawnSpec { pub workspace: PathBuf, pub stderr_path: PathBuf, pub stdout: OutputMode, - pub memory_limit_bytes: Option, - #[serde(default)] - pub memory_phase: Option, pub parent_rss_pid: Option, } pub(crate) use crate::storage::containment::{BuildDescriptorV2, ContainmentProof, ExitOutcome}; @@ -58,6 +54,8 @@ pub(crate) enum RecoveryOutcome { struct Launch { spec: SpawnSpec, descriptor: BuildDescriptorV2, + #[serde(default)] + workspace_startup: Option, } #[derive(Serialize, Deserialize)] struct Control { @@ -91,6 +89,52 @@ impl Drop for GuardianChild { } } +/// Minted only by actual owner-private directory creation, retaining its +/// physical directory through the optional origin binding operation. +pub(crate) struct FreshLifecycleDirectory { + path: PathBuf, + directory: crate::storage::locks::RootedDirectory, +} +impl FreshLifecycleDirectory { + fn create(path: &Path) -> Result { + platform::create_private_dir(path)?; + let identity = crate::storage::locks::path_identity(path).map_err(err)?; + let directory = + crate::storage::locks::RootedDirectory::open(path, identity).map_err(err)?; + Ok(Self { + path: path.to_owned(), + directory, + }) + } + pub(crate) fn path(&self) -> &Path { + &self.path + } + pub(crate) fn directory(&self) -> &crate::storage::locks::RootedDirectory { + &self.directory + } +} + +/// O_EXCL owner-private file birth; only this module's actual platform +/// creation branch can construct it. No File/bool conversion is exposed. +pub(crate) struct FreshLifecycleFile { + path: PathBuf, + file: File, +} +impl FreshLifecycleFile { + pub(crate) fn create(path: &Path) -> Result { + Ok(Self { + path: path.to_owned(), + file: platform::create_private_file(path)?, + }) + } + pub(crate) fn path(&self) -> &Path { + &self.path + } + pub(crate) fn file(&self) -> &File { + &self.file + } +} + pub(crate) struct PreparedChild { guardian: GuardianChild, owner: Option, @@ -110,21 +154,36 @@ impl PreparedChild { spec.stderr_path = cwd.join(&spec.stderr_path); } } + let workspace_startup = + crate::storage::maintenance_controls::WorkspaceStartupBinding::discover( + &spec.workspace, + ) + .map_err(err)?; + let _parent_startup = workspace_startup + .as_ref() + .map(|binding| binding.acquire()) + .transpose() + .map_err(err)?; fs::create_dir_all(&spec.workspace).map_err(err)?; let build_id = random_id()?; let dir = spec.workspace.join(format!(".lifecycle-{build_id}")); - platform::create_private_dir(&dir)?; + let fresh_lifecycle = FreshLifecycleDirectory::create(&dir)?; + if let Ok(Some(created)) = + crate::storage::maintenance_controls::WorkspaceCreatedDirectory::from_lifecycle( + &fresh_lifecycle, + ) + { + let _ = + crate::storage::maintenance_origin::record_created_workspace_directory(&created); + } + drop(fresh_lifecycle); #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; fs::set_permissions(&dir, fs::Permissions::from_mode(0o700)).map_err(err)?; } - OpenOptions::new() - .create_new(true) - .read(true) - .write(true) - .open(dir.join("lease")) - .map_err(err)?; + let lease_path = dir.join("lease"); + create_bound_private_control(&lease_path)?; let descriptor = BuildDescriptorV2 { version: 2, build_id, @@ -140,6 +199,7 @@ impl PreparedChild { &Launch { spec: spec.clone(), descriptor, + workspace_startup, }, )?; let guardian_executable = match &spec.guardian_executable { @@ -155,13 +215,7 @@ impl PreparedChild { OutputMode::Pipe => Stdio::piped(), OutputMode::Null => Stdio::null(), }) - .stderr(Stdio::from( - OpenOptions::new() - .create(true) - .append(true) - .open(&spec.stderr_path) - .map_err(err)?, - )); + .stderr(Stdio::from(open_private_append(&spec.stderr_path)?)); #[cfg(unix)] { use std::os::unix::process::CommandExt; @@ -345,6 +399,14 @@ pub(crate) fn run_internal(args: &[String]) -> Result, String> { } fn run_guardian(path: &Path) -> Result<(), String> { let launch: Launch = serde_json::from_slice(&fs::read(path).map_err(err)?).map_err(err)?; + // The child opens its OWN shared gate before acquiring lifecycle ownership. + // Inherited descriptors are not portable evidence of parent liveness. + let startup = launch + .workspace_startup + .as_ref() + .map(|binding| binding.acquire()) + .transpose() + .map_err(err)?; let mut d = launch.descriptor; let lease = OpenOptions::new() .read(true) @@ -352,6 +414,9 @@ fn run_guardian(path: &Path) -> Result<(), String> { .open(d.lifecycle_dir.join("lease")) .map_err(err)?; lease.lock_exclusive().map_err(err)?; + if let Some(startup) = &startup { + startup.recheck_building().map_err(err)?; + } if d.lifecycle_dir.join("retired").exists() || d.lifecycle_dir.join("receipt.json").exists() || !path.is_file() @@ -380,7 +445,6 @@ fn run_guardian(path: &Path) -> Result<(), String> { d.worker_pid = child.id(); let mut started = false; let mut high_water = 0; - let mut budget_reason = None; let mut supervision_error = before_ready_barrier(&d, &owner_gone).err(); let mut cancelled = owner_gone.load(Ordering::Acquire); if !cancelled && supervision_error.is_none() { @@ -388,6 +452,7 @@ fn run_guardian(path: &Path) -> Result<(), String> { supervision_error = Some(e); } } + drop(startup); loop { if supervision_error.is_some() || cancelled { break; @@ -448,28 +513,6 @@ fn run_guardian(path: &Path) -> Result<(), String> { .unwrap_or(0); let observed = rss.saturating_add(parent_rss); high_water = high_water.max(observed); - if launch - .spec - .memory_limit_bytes - .is_some_and(|limit| observed > limit.saturating_sub(HEADROOM)) - { - let mut failure = crate::error::MemoryBudgetExceeded::new( - launch - .spec - .memory_phase - .as_deref() - .unwrap_or("materialization_worker"), - launch.spec.memory_limit_bytes.unwrap(), - observed, - observed, - ); - if launch.spec.parent_rss_pid.is_some() { - failure = failure.with_process_rss(parent_rss, rss); - } - budget_reason = - Some(crate::error::NativeError::MemoryBudgetExceeded(failure).to_string()); - break; - } } } thread::sleep(TICK); @@ -480,7 +523,6 @@ fn run_guardian(path: &Path) -> Result<(), String> { code: None, signal: None, high_water_bytes: high_water, - budget_reason, cancelled, containment_empty: result.is_ok(), supervision_error, @@ -493,10 +535,7 @@ fn run_guardian(path: &Path) -> Result<(), String> { use std::os::unix::process::ExitStatusExt; outcome.signal = status.signal(); } - outcome.success = status.success() - && !cancelled - && outcome.budget_reason.is_none() - && outcome.supervision_error.is_none(); + outcome.success = status.success() && !cancelled && outcome.supervision_error.is_none(); } Err(e) => { outcome.supervision_error = Some(format!("containment_unknown: {e}")); @@ -614,25 +653,90 @@ fn random_id() -> Result { fn err(e: impl fmt::Display) -> String { e.to_string() } +fn create_bound_private_control(path: &Path) -> Result<(), String> { + let fresh = FreshLifecycleFile::create(path)?; + let file = + crate::storage::maintenance_controls::WorkspaceControlFile::from_created(fresh, path); + if let Some(tracked) = file.tracking() { + let _ = crate::storage::maintenance_origin::record_created_workspace_control(tracked); + } + file.finish_created().map_err(err) +} +/// A fresh stderr leaf is private and creation-bound before any inherited +/// writer can append. Existing caller-owned leaves are never origin-attested. +fn open_private_append(path: &Path) -> Result { + if !path.try_exists().map_err(err)? { + if let Err(error) = create_bound_private_control(path) { + if !path.try_exists().map_err(err)? { + return Err(error); + } + } + } + OpenOptions::new().append(true).open(path).map_err(err) +} fn write_json(path: &Path, value: &impl Serialize) -> Result<(), String> { write_private_json(path, value) } /// Capability-bearing JSON must be private from the instant its file exists. pub(crate) fn write_private_json(path: &Path, value: &impl Serialize) -> Result<(), String> { + write_private_json_recorded(path, value).map(|_| ()) +} +/// Actual creator/accepted-byte/rename evidence, suitable for a caller's +/// prebound known-metadata record. This never attests an observed old file. +pub(crate) fn write_private_json_recorded( + path: &Path, + value: &impl Serialize, +) -> Result, String> { let temp = path.with_extension(format!("{}.tmp", random_id()?)); - let mut file = platform::create_private_file(&temp)?; + let fresh = FreshLifecycleFile::create(&temp)?; + let cleanup = (|| { + let identity = crate::storage::locks::file_identity(fresh.file()).ok()?; + let parent = temp.parent()?; + let directory = crate::storage::locks::RootedDirectory::open( + parent, + crate::storage::locks::path_identity(parent).ok()?, + ) + .ok()?; + Some((directory, identity)) + })(); + let mut file = + crate::storage::maintenance_controls::WorkspaceControlFile::from_created(fresh, path); + if let Some(tracked) = file.tracking() { + let _ = crate::storage::maintenance_origin::record_created_workspace_control(tracked); + } let result = (|| { - file.write_all(&serde_json::to_vec(value).map_err(err)?) - .map_err(err)?; - file.sync_all().map_err(err)?; - drop(file); - fs::rename(&temp, path).map_err(err) + { + let mut writer = BufWriter::new(&mut file); + serde_json::to_writer(&mut writer, value).map_err(err)?; + writer.flush().map_err(err)?; + } + file.publish_recorded(path).map_err(err) })(); if result.is_err() { - let _ = fs::remove_file(&temp); + if let Some((directory, identity)) = cleanup { + if let Some(name) = temp.file_name() { + let _ = directory.unlink_file(name.as_encoded_bytes(), identity); + } + } } result } +/// Exclusive direct output for a fresh worker request. Serialization remains +/// streamed through a bounded buffer; the canonical collision is preserved. +pub(crate) fn write_private_json_new(path: &Path, value: &impl Serialize) -> Result<(), String> { + let fresh = FreshLifecycleFile::create(path)?; + let mut file = + crate::storage::maintenance_controls::WorkspaceControlFile::from_created(fresh, path); + if let Some(tracked) = file.tracking() { + let _ = crate::storage::maintenance_origin::record_created_workspace_control(tracked); + } + { + let mut writer = BufWriter::new(&mut file); + serde_json::to_writer(&mut writer, value).map_err(err)?; + writer.flush().map_err(err)?; + } + file.finish_created().map_err(err) +} #[cfg(test)] mod tests { @@ -732,6 +836,7 @@ mod tests { proof: ContainmentProof::Pending, }; let launch = Launch { + workspace_startup: None, descriptor, spec: SpawnSpec { program: std::env::current_exe().unwrap(), @@ -740,8 +845,6 @@ mod tests { workspace: dir.clone(), stderr_path: dir.join("stderr"), stdout: OutputMode::Null, - memory_limit_bytes: None, - memory_phase: None, parent_rss_pid: None, }, }; diff --git a/src/process_lifecycle/unix.rs b/src/process_lifecycle/unix.rs index dec3df9e..5d794be8 100644 --- a/src/process_lifecycle/unix.rs +++ b/src/process_lifecycle/unix.rs @@ -28,15 +28,25 @@ pub(super) struct Contained { impl Contained { pub fn spawn(spec: &SpawnSpec, d: &mut BuildDescriptorV2) -> Result { let path = d.lifecycle_dir.join("writers.fifo"); - let cpath = CString::new(path.as_os_str().as_encoded_bytes()).map_err(err)?; - if unsafe { libc::mkfifo(cpath.as_ptr(), 0o600) } != 0 { - return Err(err(io::Error::last_os_error())); - } - let fifo = OpenOptions::new() - .read(true) - .custom_flags(libc::O_NONBLOCK | libc::O_NOFOLLOW) - .open(&path) + // The covered creator pins the genuinely new FIFO before any child + // can inherit it. Linux does not support user xattrs on FIFOs; the + // private hardlink preserves its identity until terminal disposal. + let created = crate::storage::maintenance_controls::WorkspaceCreatedFifo::create_new(&path) .map_err(err)?; + let fifo = if let Some(created) = created { + let _ = crate::storage::maintenance_origin::record_created_workspace_fifo(&created); + created.file().try_clone().map_err(err)? + } else { + let cpath = CString::new(path.as_os_str().as_encoded_bytes()).map_err(err)?; + if unsafe { libc::mkfifo(cpath.as_ptr(), 0o600) } != 0 { + return Err(err(io::Error::last_os_error())); + } + OpenOptions::new() + .read(true) + .custom_flags(libc::O_NONBLOCK | libc::O_NOFOLLOW) + .open(&path) + .map_err(err)? + }; let metadata = fifo.metadata().map_err(err)?; d.proof = ContainmentProof::UnixFifo { path: path.clone(), @@ -66,6 +76,7 @@ impl Contained { &Launch { spec: spec.clone(), descriptor: d.clone(), + workspace_startup: None, }, )?; let mut command = Command::new(std::env::current_exe().map_err(err)?); @@ -150,7 +161,14 @@ impl Contained { let pid = unsafe { info.si_pid() }; #[cfg(not(target_os = "linux"))] let pid = info.si_pid; - Ok(pid != 0) + // Darwin can return CLD_STOPPED despite WEXITED. A nonzero pid is + // therefore an observation, not exit proof; keep stopped/traced or + // continued children alive until an actual exit class is reported. + Ok(pid != 0 + && matches!( + info.si_code, + libc::CLD_EXITED | libc::CLD_KILLED | libc::CLD_DUMPED + )) } pub fn teardown(&mut self, deadline: Instant) -> Result { self.teardown_attempted = true; @@ -331,6 +349,120 @@ pub(super) fn create_private_file(path: &Path) -> Result { #[cfg(test)] mod tests { use super::*; + fn contained_child_for_exit_probe() -> (PathBuf, Contained) { + let dir = std::env::temp_dir().join(format!("graph-native-exit-{}", random_id().unwrap())); + fs::create_dir(&dir).unwrap(); + let path = dir.join("writers.fifo"); + let encoded = CString::new(path.as_os_str().as_encoded_bytes()).unwrap(); + assert_eq!(unsafe { libc::mkfifo(encoded.as_ptr(), 0o600) }, 0); + let fifo = OpenOptions::new() + .read(true) + .custom_flags(libc::O_NONBLOCK | libc::O_NOFOLLOW) + .open(&path) + .unwrap(); + let writer = OpenOptions::new() + .write(true) + .custom_flags(libc::O_NONBLOCK | libc::O_NOFOLLOW) + .open(&path) + .unwrap(); + let metadata = fifo.metadata().unwrap(); + // Isolate the real native peek/teardown boundary using an actual owned + // process group and FIFO; no serialized PID is adopted as authority. + let mut child = Command::new("/bin/sh") + .args(["-c", "read command; exit 7"]) + .stdin(Stdio::piped()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .process_group(0) + .spawn() + .unwrap(); + let gate = child.stdin.take(); + let proof = BuildDescriptorV2 { + version: 2, + build_id: "native-exit-probe".into(), + lifecycle_dir: dir.clone(), + endpoint: String::new(), + nonce: String::new(), + worker_pid: child.id(), + guardian_pid: 0, + proof: ContainmentProof::UnixFifo { + path, + device: metadata.dev(), + inode: metadata.ino(), + }, + }; + let mut lifetime_pipe = [-1; 2]; + assert_eq!(unsafe { libc::pipe(lifetime_pipe.as_mut_ptr()) }, 0); + let lifetime_reader = unsafe { File::from_raw_fd(lifetime_pipe[0]) }; + let lifetime = unsafe { File::from_raw_fd(lifetime_pipe[1]) }; + drop(lifetime_reader); + ( + dir, + Contained { + child, + gate, + fifo, + writer: Some(writer), + proof, + _lifetime_writer: lifetime, + teardown_attempted: false, + }, + ) + } + + #[test] + fn stopped_owned_child_is_not_an_exit_and_resume_or_cancellation_still_completes() { + use std::os::unix::process::ExitStatusExt; + for cancel in [false, true] { + let (dir, mut child) = contained_child_for_exit_probe(); + assert_eq!(unsafe { libc::kill(child.id() as i32, libc::SIGSTOP) }, 0); + let deadline = Instant::now() + Duration::from_secs(5); + loop { + let mut info = std::mem::MaybeUninit::::zeroed(); + assert_eq!( + unsafe { + libc::waitid( + libc::P_PID, + child.id() as libc::id_t, + info.as_mut_ptr(), + libc::WSTOPPED | libc::WNOHANG | libc::WNOWAIT, + ) + }, + 0 + ); + let info = unsafe { info.assume_init() }; + if info.si_code == libc::CLD_STOPPED { + break; + } + assert!(Instant::now() < deadline, "owned child did not stop"); + thread::sleep(Duration::from_millis(5)); + } + for _ in 0..3 { + assert!(!child.exited().unwrap(), "stopped child reported exited"); + } + if cancel { + let status = child.teardown(Instant::now() + DEADLINE).unwrap(); + assert_eq!(status.signal(), Some(libc::SIGKILL)); + } else { + assert_eq!(unsafe { libc::kill(child.id() as i32, libc::SIGCONT) }, 0); + child.gate.as_mut().unwrap().write_all(b"finish\n").unwrap(); + let deadline = Instant::now() + Duration::from_secs(5); + while !child.exited().unwrap() { + assert!(Instant::now() < deadline, "resumed child did not exit"); + thread::sleep(Duration::from_millis(5)); + } + // Repeated peeks must not consume the owned child's status. + for _ in 0..3 { + assert!(child.exited().unwrap()); + } + let status = child.teardown(Instant::now() + DEADLINE).unwrap(); + assert_eq!(status.code(), Some(7)); + } + drop(child); + fs::remove_dir_all(dir).unwrap(); + } + } + #[test] fn absent_proof_never_establishes_emptiness() { let d = BuildDescriptorV2 { diff --git a/src/process_lifecycle/windows.rs b/src/process_lifecycle/windows.rs index e682402b..e608da27 100644 --- a/src/process_lifecycle/windows.rs +++ b/src/process_lifecycle/windows.rs @@ -318,11 +318,7 @@ impl Contained { // Recovery must know the job identity before any contained child exists. write_json(&descriptor.lifecycle_dir.join("proof.json"), descriptor)?; let stdin = File::open("NUL").map_err(err)?; - let stderr = OpenOptions::new() - .create(true) - .append(true) - .open(&spec.stderr_path) - .map_err(err)?; + let stderr = open_private_append(&spec.stderr_path)?; let stdin = duplicate(stdin.as_raw_handle())?; let stdout = duplicate(std::io::stdout().as_raw_handle())?; let stderr = duplicate(stderr.as_raw_handle())?; @@ -566,8 +562,6 @@ mod tests { workspace: directory.clone(), stderr_path: directory.join("stderr.log"), stdout: OutputMode::Null, - memory_limit_bytes: None, - memory_phase: None, parent_rss_pid: None, }; // Creation establishes real kernel membership while the START gate keeps diff --git a/src/protocol.rs b/src/protocol.rs index 6c0cd535..bbf055cf 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -10,17 +10,6 @@ pub(crate) const GRAPH_BUILD_DIGEST_FORMAT_VERSION: u64 = 5; pub(crate) const PROFILE_COMPATIBILITY_VERSION: u64 = 2; pub(crate) const MATERIALIZATION_MANIFEST_SCHEMA_VERSION: u64 = 5; pub(crate) const SEARCH_BACKEND_FORMAT_VERSION: u64 = 1; -const MAX_DATABASE_BUFFER_POOL_BYTES: u64 = 384 * 1024 * 1024; -const MIN_DATABASE_BUFFER_POOL_BYTES: u64 = 128 * 1024 * 1024; - -const fn default_worker_memory_mib() -> u64 { - crate::api::context::DEFAULT_WORKER_MEMORY_MIB -} - -const fn default_rust_memory_mib() -> u64 { - crate::api::context::DEFAULT_RUST_MEMORY_MIB -} - const fn default_spill_chunk_mib() -> u64 { crate::api::context::DEFAULT_SPILL_CHUNK_MIB } @@ -63,10 +52,6 @@ pub struct NativeSyntaxMaterializationRequest { pub strict: bool, #[serde(default = "default_parallel")] pub parallel: bool, - #[serde(default = "default_worker_memory_mib")] - pub worker_memory_mib: u64, - #[serde(default = "default_rust_memory_mib")] - pub rust_memory_mib: u64, #[serde(default = "default_spill_chunk_mib")] pub spill_chunk_mib: u64, #[serde(default = "default_max_parallelism")] @@ -156,62 +141,12 @@ impl NativeSyntaxMaterializationRequest { Ok(hex_lower(Sha256::digest(payload).as_ref())) } - pub(crate) fn rust_memory_limit_bytes(&self) -> Result { - self.rust_memory_mib - .checked_mul(1024 * 1024) - .ok_or_else(|| { - NativeError::InvalidInput( - "rust_memory_mib is too large to convert to bytes".to_string(), - ) - }) - } - - pub(crate) fn database_buffer_pool_bytes(&self) -> Result { - let worker_bytes = self - .worker_memory_mib - .checked_mul(1024 * 1024) - .ok_or_else(|| { - NativeError::InvalidInput( - "worker_memory_mib is too large to convert to bytes".to_string(), - ) - })?; - let rust_bytes = self.rust_memory_limit_bytes()?; - let available = worker_bytes.checked_sub(rust_bytes).ok_or_else(|| { - NativeError::InvalidInput( - "worker_memory_mib must exceed rust_memory_mib for database writes".to_string(), - ) - })?; - if available < MIN_DATABASE_BUFFER_POOL_BYTES { - return Err(NativeError::InvalidInput( - "worker_memory_mib must reserve at least 128 MiB beyond rust_memory_mib for database writes" - .to_string(), - )); - } - Ok(available.min(MAX_DATABASE_BUFFER_POOL_BYTES)) - } - - pub(crate) fn validate_resource_limits(&self) -> Result<(), NativeError> { - if self.worker_memory_mib == 0 - || self.rust_memory_mib == 0 - || self.spill_chunk_mib == 0 - || self.max_parallelism == 0 - { - return Err(NativeError::InvalidInput( - "materialization memory limits and max_parallelism must be positive".to_string(), - )); - } - if self.rust_memory_mib > self.worker_memory_mib { - return Err(NativeError::InvalidInput( - "rust_memory_mib must not exceed worker_memory_mib".to_string(), - )); - } - self.database_buffer_pool_bytes()?; - if self.spill_chunk_mib > self.rust_memory_mib { + pub(crate) fn validate_execution_options(&self) -> Result<(), NativeError> { + if self.spill_chunk_mib == 0 || self.max_parallelism == 0 { return Err(NativeError::InvalidInput( - "spill_chunk_mib must not exceed rust_memory_mib".to_string(), + "spill_chunk_mib and max_parallelism must be positive".to_string(), )); } - self.rust_memory_limit_bytes()?; Ok(()) } } @@ -380,6 +315,108 @@ impl ManifestDiff { } } +/// Compact cached maintenance measurements. Unknown bytes remain JSON null. +#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)] +pub struct MaintenanceByteBucket { + pub logical_bytes: Option, + pub allocated_bytes: Option, +} + +/// Fixed categories make absence of an observation explicit for every category. +#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)] +pub struct MaintenanceCategoryBytes { + pub active_graph: MaintenanceByteBucket, + pub active_cache: MaintenanceByteBucket, + pub leased_generation: MaintenanceByteBucket, + pub live_run: MaintenanceByteBucket, + pub recovery_evidence: MaintenanceByteBucket, + pub artifact_temporary: MaintenanceByteBucket, + pub legacy_staging: MaintenanceByteBucket, + pub retired_direct: MaintenanceByteBucket, + pub coordination: MaintenanceByteBucket, + pub unknown: MaintenanceByteBucket, +} + +#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)] +pub struct MaintenanceBytes { + pub total: MaintenanceByteBucket, + pub retained: MaintenanceByteBucket, + pub potential_reclamation: MaintenanceByteBucket, + pub deferred: MaintenanceByteBucket, + pub unknown: MaintenanceByteBucket, + pub by_category: MaintenanceCategoryBytes, +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum MaintenanceInventoryStatus { + Complete, + Partial, + Busy, + Missing, + Invalid, +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum MaintenanceAttemptStatus { + Complete, + Partial, + Deferred, + Missing, + Invalid, +} + +/// Metrics are unknown until this attempt completes. A running attempt must not +/// reuse metrics from a prior attempt with its newer start time. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +pub struct MaintenanceAttempt { + pub started_at_unix_ms: u64, + pub finished_at_unix_ms: Option, + pub status: Option, + pub attempted_entries: Option, + pub deleted_entries: Option, + pub reclaimed_allocated_bytes: Option, + pub continuation_phase: Option, +} + +/// Additive health/materialization presentation, independent of legacy +/// `cleanup_pending`. This contains no inventory entries or storage authority. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +pub struct MaintenanceSummary { + pub requested: bool, + pub running: bool, + pub pending: bool, + pub observed_at_unix_ms: Option, + pub observation_age_ms: Option, + pub inventory_status: Option, + pub measurement_complete: bool, + pub observation_complete: bool, + pub bytes: MaintenanceBytes, + pub reasons: Vec, + pub errors: Vec, + pub last_attempt: Option, +} + +impl Default for MaintenanceSummary { + fn default() -> Self { + Self { + requested: false, + running: false, + pending: true, + observed_at_unix_ms: None, + observation_age_ms: None, + inventory_status: None, + measurement_complete: false, + observation_complete: false, + bytes: MaintenanceBytes::default(), + reasons: vec!["maintenance_not_observed".to_string()], + errors: Vec::new(), + last_attempt: None, + } + } +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct NativeSyntaxMaterializationResponse { pub snapshots: BTreeMap, @@ -405,6 +442,8 @@ pub struct NativeSyntaxMaterializationResponse { #[serde(default)] pub cleanup_pending: bool, #[serde(default)] + pub maintenance: Option, + #[serde(default)] pub pending_runs: usize, #[serde(default)] pub artifacts_reused: usize, @@ -463,6 +502,7 @@ impl NativeSyntaxMaterializationResponse { storage_format: String::new(), active_generation: None, cleanup_pending: false, + maintenance: None, pending_runs: 0, artifacts_reused: 0, artifacts_rebuilt: 0, @@ -508,6 +548,7 @@ impl NativeSyntaxMaterializationResponse { storage_format: String::new(), active_generation: None, cleanup_pending: false, + maintenance: None, pending_runs: 0, artifacts_reused: 0, artifacts_rebuilt: 0, @@ -585,14 +626,8 @@ mod tests { serde_json::from_str(&request_json("")).unwrap(); assert!(request.parallel); - assert_eq!(request.worker_memory_mib, 768); - assert_eq!(request.rust_memory_mib, 384); assert_eq!(request.spill_chunk_mib, 32); assert_eq!(request.max_parallelism, 2); - assert_eq!( - request.database_buffer_pool_bytes().unwrap(), - 384 * 1024 * 1024 - ); assert_eq!( request.resolved_artifact_root(), PathBuf::from("/repo/.codebaseGraph/artifacts") @@ -608,27 +643,11 @@ mod tests { } #[test] - fn native_materialization_request_rejects_inconsistent_resource_limits() { - let mut request: NativeSyntaxMaterializationRequest = - serde_json::from_str(&request_json("")).unwrap(); - request.rust_memory_mib = request.worker_memory_mib + 1; - - let error = request.validate_resource_limits().unwrap_err(); - - assert!(error - .to_string() - .contains("rust_memory_mib must not exceed worker_memory_mib")); - } - - #[test] - fn native_materialization_request_reserves_worker_memory_for_database_writes() { + fn native_materialization_request_validates_positive_execution_options() { let mut request: NativeSyntaxMaterializationRequest = serde_json::from_str(&request_json("")).unwrap(); - request.worker_memory_mib = request.rust_memory_mib; - - let error = request.validate_resource_limits().unwrap_err(); - - assert!(error.to_string().contains("must reserve at least 128 MiB")); + request.spill_chunk_mib = 0; + assert!(request.validate_execution_options().is_err()); } #[test] @@ -919,3 +938,111 @@ mod tests { ); } } + +#[cfg(test)] +mod maintenance_summary_tests { + use super::*; + + #[test] + fn unobserved_maintenance_keeps_all_measurements_unknown() { + let value = serde_json::to_value(MaintenanceSummary::default()).unwrap(); + assert_eq!( + value["reasons"], + serde_json::json!(["maintenance_not_observed"]) + ); + assert!(value["observed_at_unix_ms"].is_null()); + assert!(value["inventory_status"].is_null()); + assert!(value["last_attempt"].is_null()); + assert_eq!(value["measurement_complete"], false); + assert_eq!(value["observation_complete"], false); + let bytes = value["bytes"].as_object().unwrap(); + for (name, bucket) in bytes { + if name == "by_category" { + assert_eq!(bucket.as_object().unwrap().len(), 10); + for bucket in bucket.as_object().unwrap().values() { + assert!(bucket["logical_bytes"].is_null()); + assert!(bucket["allocated_bytes"].is_null()); + } + } else { + assert!(bucket["logical_bytes"].is_null()); + assert!(bucket["allocated_bytes"].is_null()); + } + } + } + + #[test] + fn running_attempt_does_not_require_fabricated_metrics() { + let summary = MaintenanceSummary { + running: true, + observed_at_unix_ms: Some(100), + observation_age_ms: Some(200), + last_attempt: Some(MaintenanceAttempt { + started_at_unix_ms: 300, + finished_at_unix_ms: None, + status: None, + attempted_entries: None, + deleted_entries: None, + reclaimed_allocated_bytes: None, + continuation_phase: None, + }), + ..MaintenanceSummary::default() + }; + let value = serde_json::to_value(&summary).unwrap(); + for field in [ + "finished_at_unix_ms", + "status", + "attempted_entries", + "deleted_entries", + "reclaimed_allocated_bytes", + ] { + assert!(value["last_attempt"][field].is_null(), "{field}"); + } + assert_eq!(value["observed_at_unix_ms"], 100); + assert_eq!(value["last_attempt"]["started_at_unix_ms"], 300); + assert_eq!( + serde_json::from_value::(value).unwrap(), + summary + ); + } + + #[test] + fn partial_measurements_and_deferred_outcomes_round_trip_without_zero_fill() { + let summary = MaintenanceSummary { + inventory_status: Some(MaintenanceInventoryStatus::Partial), + bytes: MaintenanceBytes { + by_category: MaintenanceCategoryBytes { + active_cache: MaintenanceByteBucket { + logical_bytes: Some(4096), + ..Default::default() + }, + ..Default::default() + }, + deferred: MaintenanceByteBucket { + logical_bytes: Some(8192), + ..Default::default() + }, + ..Default::default() + }, + reasons: vec!["writer_in_use".into()], + errors: vec!["fixture permission failure".into()], + last_attempt: Some(MaintenanceAttempt { + started_at_unix_ms: 100, + finished_at_unix_ms: Some(200), + status: Some(MaintenanceAttemptStatus::Deferred), + attempted_entries: Some(1), + deleted_entries: Some(0), + reclaimed_allocated_bytes: None, + continuation_phase: Some("validation_pending".into()), + }), + ..MaintenanceSummary::default() + }; + let value = serde_json::to_value(&summary).unwrap(); + assert_eq!(value["inventory_status"], "partial"); + assert_eq!(value["last_attempt"]["status"], "deferred"); + assert!(value["bytes"]["deferred"]["allocated_bytes"].is_null()); + assert_eq!( + serde_json::from_value::(value).unwrap(), + summary + ); + } +} diff --git a/src/scan.rs b/src/scan.rs index 6dd7c557..d1e89f48 100644 --- a/src/scan.rs +++ b/src/scan.rs @@ -5,6 +5,7 @@ use crate::protocol::{ LanguageProfile, ManifestDiff, NativeSyntaxMaterializationRequest, SourceSnapshot, }; use crate::source_selection::SourceSelection; +use sha2::{Digest, Sha256}; use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::path::{Path, PathBuf}; @@ -25,7 +26,7 @@ pub(crate) struct SourceScan { pub(crate) fn scan_sources( request: &NativeSyntaxMaterializationRequest, ) -> Result { - request.validate_resource_limits()?; + request.validate_execution_options()?; validate_profile_grammar_versions(&request.profiles)?; let source_root = PathBuf::from(&request.source_root); let profiles = ProfileSet::new(&request.profiles); @@ -144,7 +145,7 @@ fn spool_supported_snapshots( } let snapshot_root = Path::new(&request.staging_dir).join(SOURCE_SNAPSHOT_DIRECTORY); - fs::create_dir_all(&snapshot_root)?; + create_snapshot_directory(&snapshot_root)?; for snapshot in supported.values_mut() { if !required_paths.contains(&snapshot.path) { continue; @@ -163,6 +164,60 @@ fn spool_supported_snapshots( Ok(()) } +fn create_snapshot_directory(path: &Path) -> Result<(), NativeError> { + if let Ok(Some(created)) = + crate::storage::maintenance_controls::WorkspaceCreatedDirectory::create_new(path) + { + let _ = crate::storage::maintenance_origin::record_created_workspace_directory(&created); + return Ok(()); + } + Ok(fs::create_dir_all(path)?) +} + +fn copy_snapshot( + source: &Path, + temporary: &Path, + final_path: &Path, +) -> std::io::Result<( + u64, + Option, + String, +)> { + use crate::process_lifecycle::FreshLifecycleFile; + use crate::storage::maintenance_controls::WorkspaceControlFile; + // Only the actual exclusive creator may bind this temporary inode and its + // intended publication name. Existing caller-owned paths are not adopted. + if let Ok(fresh) = FreshLifecycleFile::create(temporary) { + let mut output = WorkspaceControlFile::from_created(fresh, final_path); + if let Some(tracked) = output.tracking() { + let _ = crate::storage::maintenance_origin::record_created_workspace_control(tracked); + } + struct HashingWriter<'a> { + output: &'a mut WorkspaceControlFile, + hash: Sha256, + } + impl std::io::Write for HashingWriter<'_> { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + let count = std::io::Write::write(self.output, bytes)?; + self.hash.update(&bytes[..count]); + Ok(count) + } + fn flush(&mut self) -> std::io::Result<()> { + std::io::Write::flush(self.output) + } + } + let mut writer = HashingWriter { + output: &mut output, + hash: Sha256::new(), + }; + let length = std::io::copy(&mut fs::File::open(source)?, &mut writer)?; + let copied_hash = format!("{:x}", writer.hash.finalize()); + return Ok((length, Some(output), copied_hash)); + } + let length = fs::copy(source, temporary)?; + Ok((length, None, hash::sha256_file(temporary)?)) +} + fn spool_stable_source( source_path: &Path, snapshot_root: &Path, @@ -171,7 +226,7 @@ fn spool_stable_source( ) -> Result<(PathBuf, u64), NativeError> { let partition_id = hash::partition_id(relative_path); let shard = snapshot_root.join(&partition_id[..2]); - fs::create_dir_all(&shard)?; + create_snapshot_directory(&shard)?; let mut last_error = None; let mut source_changed = false; @@ -181,19 +236,9 @@ fn spool_stable_source( std::process::id(), attempt )); - match fs::copy(source_path, &temporary_path) { - Ok(byte_len) => { - let copied_hash = match hash::sha256_file(&temporary_path) { - Ok(value) => value, - Err(error) => { - last_error = Some(error.to_string()); - if error.kind() == std::io::ErrorKind::NotFound { - source_changed = true; - } - let _ = fs::remove_file(&temporary_path); - continue; - } - }; + let final_path = shard.join(format!("{partition_id}-{expected_hash}.snapshot")); + match copy_snapshot(source_path, &temporary_path, &final_path) { + Ok((byte_len, output, copied_hash)) => { let current_hash = match hash::sha256_file(source_path) { Ok(value) => value, Err(error) => { @@ -201,6 +246,7 @@ fn spool_stable_source( if error.kind() == std::io::ErrorKind::NotFound { source_changed = true; } + drop(output); let _ = fs::remove_file(&temporary_path); continue; } @@ -209,14 +255,15 @@ fn spool_stable_source( source_changed = true; last_error = Some("source changed after its scan metadata was captured".to_string()); + drop(output); let _ = fs::remove_file(&temporary_path); continue; } - let final_path = shard.join(format!("{partition_id}-{copied_hash}.snapshot")); if final_path.exists() { let existing_hash = hash::sha256_file(&final_path)?; if existing_hash == copied_hash { + drop(output); let _ = fs::remove_file(&temporary_path); return Ok((final_path, byte_len)); } @@ -224,7 +271,11 @@ fn spool_stable_source( "source snapshot collision for {relative_path}" ))); } - fs::rename(&temporary_path, &final_path)?; + if let Some(output) = output { + output.publish_recorded(&final_path)?; + } else { + fs::rename(&temporary_path, &final_path)?; + } return Ok((final_path, byte_len)); } Err(error) => { @@ -257,7 +308,7 @@ pub(crate) fn spool_snapshot_for_build( if current_path.starts_with(&snapshot_root) { return Ok(snapshot.clone()); } - fs::create_dir_all(&snapshot_root)?; + create_snapshot_directory(&snapshot_root)?; let (snapshot_path, byte_len) = spool_stable_source( ¤t_path, &snapshot_root, @@ -368,6 +419,31 @@ fn requires_full_source_rebuild(request: &NativeSyntaxMaterializationRequest) -> #[cfg(test)] mod tests { use super::*; + + #[test] + fn snapshot_hashing_keeps_the_exclusive_creator_open_until_publication() { + let root = std::env::temp_dir().join(format!( + "snapshot-private-{}", + crate::storage::layout::managed_generation_id() + )); + fs::create_dir(&root).unwrap(); + let source = root.join("source.rs"); + let temporary = root.join("temporary"); + let destination = root.join("published"); + fs::write(&source, b"fn preserved() {}\n").unwrap(); + let (length, output, digest) = copy_snapshot(&source, &temporary, &destination).unwrap(); + assert_eq!(length, fs::metadata(&source).unwrap().len()); + assert_eq!(digest, hash::sha256_file(&source).unwrap()); + #[cfg(windows)] + assert_eq!( + fs::File::open(&temporary).unwrap_err().raw_os_error(), + Some(32) + ); + output.unwrap().publish_recorded(&destination).unwrap(); + assert!(!temporary.exists()); + assert_eq!(fs::read(&destination).unwrap(), fs::read(&source).unwrap()); + fs::remove_dir_all(root).unwrap(); + } use crate::protocol::{ LanguageProfile, ManifestEntry, NativeManifest, OntologySchema, MATERIALIZATION_MANIFEST_SCHEMA_VERSION, @@ -423,8 +499,6 @@ mod tests { atomic_rebuild: false, strict: true, parallel: false, - worker_memory_mib: 768, - rust_memory_mib: 384, spill_chunk_mib: 32, max_parallelism: 2, progress: false, diff --git a/src/search_index/build.rs b/src/search_index/build.rs index 454c0cbb..9a314949 100644 --- a/src/search_index/build.rs +++ b/src/search_index/build.rs @@ -9,7 +9,7 @@ use super::model::{ LEXICON_MAGIC, LEXICON_SUFFIX, METADATA_SUFFIX, OCCURRENCES_MAGIC, POSTINGS_MAGIC, POSTINGS_SUFFIX, SIDECAR_SUFFIXES, }; -use super::{memory_budget_error, sidecar_path}; +use super::sidecar_path; use crate::error::NativeError; use crate::protocol::SearchBackendMetadata; use serde::de::{DeserializeSeed, Error as DeError, SeqAccess, Visitor}; @@ -23,7 +23,6 @@ use std::path::{Path, PathBuf}; const MAX_TERM_BYTES: usize = 256; const MAX_MERGE_FAN_IN: usize = 32; -const MIN_CHUNK_BYTES: usize = 4096; const MERGE_READER_ACCOUNTED_BYTES: usize = 768; const MERGE_READER_BUFFER_BYTES: usize = 256; @@ -44,11 +43,9 @@ pub(crate) struct SearchIndexBuildResult { pub(crate) fn build( request: SearchIndexBuildRequest<'_>, ) -> Result { - if request.chunk_bytes < MIN_CHUNK_BYTES { - return Err(memory_budget_error( - "search_index_build", - request.chunk_bytes, - MIN_CHUNK_BYTES, + if request.chunk_bytes == 0 { + return Err(NativeError::InvalidInput( + "search chunk target must be positive".into(), )); } ensure_targets_absent(request.db_path)?; @@ -120,7 +117,6 @@ fn build_inner( index_order, layer, }, - request.chunk_bytes, )?; write_u64(&mut document_offsets, offset)?; write_document_stat( @@ -519,7 +515,9 @@ struct OccurrenceSorter { impl OccurrenceSorter { fn new(root: &Path, chunk_limit: usize) -> Result { if chunk_limit == 0 { - return Err(memory_budget_error("search_occurrences", 0, 1)); + return Err(NativeError::InvalidInput( + "search chunk target must be positive".into(), + )); } Ok(Self { root: root.to_path_buf(), @@ -536,60 +534,33 @@ impl OccurrenceSorter { fn push(&mut self, occurrence: Occurrence) -> Result<(), NativeError> { let term_bytes = occurrence.term.capacity(); - let record_bytes = term_bytes + let projected_bytes = self + .buffered_bytes .checked_add(std::mem::size_of::()) - .ok_or_else(|| { - memory_budget_error("search_occurrences", self.chunk_limit, usize::MAX) - })?; - if record_bytes > self.chunk_limit { - return Err(memory_budget_error( - "search_occurrences", - self.chunk_limit, - record_bytes, - )); - } - let required_len = self.buffered.len().checked_add(1).ok_or_else(|| { - memory_budget_error("search_occurrences", self.chunk_limit, usize::MAX) - })?; - let projected_capacity = self.buffered.capacity().max(required_len); - let projected_bytes = projected_capacity - .checked_mul(std::mem::size_of::()) - .and_then(|bytes| bytes.checked_add(self.buffered_term_bytes)) .and_then(|bytes| bytes.checked_add(term_bytes)) - .ok_or_else(|| { - memory_budget_error("search_occurrences", self.chunk_limit, usize::MAX) - })?; + .ok_or_else(|| NativeError::InvalidInput("search occurrence size overflow".into()))?; if !self.buffered.is_empty() && projected_bytes > self.chunk_limit { self.flush()?; } - self.buffered.try_reserve_exact(1).map_err(|_| { - memory_budget_error("search_occurrences", self.chunk_limit, projected_bytes) + self.buffered.try_reserve_exact(1).map_err(|error| { + NativeError::Io(std::io::Error::other(format!( + "search occurrence allocation failed: {error}" + ))) })?; - let accounted_bytes = self - .buffered - .capacity() - .checked_mul(std::mem::size_of::()) - .and_then(|bytes| bytes.checked_add(self.buffered_term_bytes)) - .and_then(|bytes| bytes.checked_add(term_bytes)) - .ok_or_else(|| { - memory_budget_error("search_occurrences", self.chunk_limit, usize::MAX) - })?; - if accounted_bytes > self.chunk_limit { - return Err(memory_budget_error( - "search_occurrences", - self.chunk_limit, - accounted_bytes, - )); - } - self.buffered.push(occurrence); self.buffered_term_bytes = self .buffered_term_bytes .checked_add(term_bytes) - .ok_or_else(|| { - memory_budget_error("search_occurrences", self.chunk_limit, usize::MAX) - })?; - self.buffered_bytes = accounted_bytes; + .ok_or_else(|| NativeError::InvalidInput("search occurrence size overflow".into()))?; + self.buffered.push(occurrence); + self.buffered_bytes = self + .buffered + .capacity() + .saturating_mul(std::mem::size_of::()) + .saturating_add(self.buffered_term_bytes); self.high_water_bytes = self.high_water_bytes.max(self.buffered_bytes); + if self.buffered_bytes >= self.chunk_limit { + self.flush()?; + } Ok(()) } @@ -723,9 +694,11 @@ impl OccurrenceReader { ))); } let mut bytes = Vec::new(); - bytes - .try_reserve_exact(length) - .map_err(|_| memory_budget_error("search_occurrence_read", MAX_TERM_BYTES, length))?; + bytes.try_reserve_exact(length).map_err(|error| { + NativeError::Io(std::io::Error::other(format!( + "search occurrence allocation failed: {error}" + ))) + })?; bytes.resize(length, 0); self.reader.read_exact(&mut bytes)?; let term = String::from_utf8(bytes).map_err(|error| { diff --git a/src/search_index/io.rs b/src/search_index/io.rs index 7adcd1a7..a4572199 100644 --- a/src/search_index/io.rs +++ b/src/search_index/io.rs @@ -57,14 +57,11 @@ pub(super) fn read_u64(reader: &mut impl Read) -> Result { pub(super) fn write_document( writer: &mut (impl Write + Seek), document: &SearchDocument, - chunk_limit: usize, ) -> Result { let encoded = serde_json::to_vec(document)?; - if encoded.capacity() > chunk_limit || encoded.len() > u32::MAX as usize { - return Err(super::memory_budget_error( - "search_documents", - chunk_limit, - encoded.capacity(), + if encoded.len() > super::model::MAX_DOCUMENT_RECORD_BYTES { + return Err(NativeError::InvalidInput( + "search document exceeds sidecar read-format length".into(), )); } let offset = writer.stream_position()?; @@ -86,8 +83,10 @@ pub(super) fn read_document_at( ))); } let mut encoded = Vec::new(); - encoded.try_reserve_exact(length).map_err(|_| { - super::memory_budget_error("search_read_document", max_record_bytes, length) + encoded.try_reserve_exact(length).map_err(|error| { + NativeError::Io(io::Error::other(format!( + "search document allocation failed: {error}" + ))) })?; encoded.resize(length, 0); file.read_exact(&mut encoded)?; @@ -158,9 +157,11 @@ pub(super) fn read_lexicon_entry( ))); } let mut bytes = Vec::new(); - bytes - .try_reserve_exact(length) - .map_err(|_| super::memory_budget_error("search_lexicon_read", max_term_bytes, length))?; + bytes.try_reserve_exact(length).map_err(|error| { + NativeError::Io(io::Error::other(format!( + "search lexicon allocation failed: {error}" + ))) + })?; bytes.resize(length, 0); reader.read_exact(&mut bytes)?; let term = String::from_utf8(bytes).map_err(|error| { diff --git a/src/search_index/mod.rs b/src/search_index/mod.rs index 591d913e..fd679fd5 100644 --- a/src/search_index/mod.rs +++ b/src/search_index/mod.rs @@ -3,7 +3,6 @@ mod io; mod model; mod read; -use crate::error::{MemoryBudgetExceeded, NativeError}; use std::path::{Path, PathBuf}; pub(crate) use build::{build, SearchIndexBuildRequest}; @@ -14,14 +13,5 @@ pub(crate) fn sidecar_path(database_path: &Path, suffix: &str) -> PathBuf { PathBuf::from(format!("{}.{}", database_path.display(), suffix)) } -fn memory_budget_error(phase: &str, limit: usize, accounted: usize) -> NativeError { - NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new( - phase, - limit as u64, - accounted as u64, - 0, - )) -} - #[cfg(test)] mod tests; diff --git a/src/search_index/model.rs b/src/search_index/model.rs index d014e4dd..87ef787c 100644 --- a/src/search_index/model.rs +++ b/src/search_index/model.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; pub(super) const FORMAT_VERSION: u32 = 1; +pub(super) const MAX_DOCUMENT_RECORD_BYTES: usize = 1024 * 1024; pub(super) const BACKEND_NAME: &str = "disk_bm25_v1"; pub(super) const METADATA_SUFFIX: &str = "search.meta.json"; pub(super) const LEXICON_SUFFIX: &str = "search.lexicon.bin"; diff --git a/src/search_index/read.rs b/src/search_index/read.rs index c4e21658..53b229db 100644 --- a/src/search_index/read.rs +++ b/src/search_index/read.rs @@ -21,7 +21,7 @@ use std::path::Path; const MAX_TERM_BYTES: usize = 256; const MAX_QUERY_TERMS: usize = 32; -const MAX_DOCUMENT_RECORD_BYTES: usize = 1024 * 1024; +use super::model::MAX_DOCUMENT_RECORD_BYTES; const MAX_METADATA_BYTES: u64 = 64 * 1024; pub(crate) fn validate( @@ -190,13 +190,10 @@ pub(crate) fn search( read_magic(&mut offsets, DOCUMENT_OFFSETS_MAGIC)?; read_magic(&mut documents, DOCUMENTS_MAGIC)?; let mut results = Vec::new(); - results.try_reserve_exact(best.len()).map_err(|_| { - super::memory_budget_error( - "search_results", - limit.saturating_mul(std::mem::size_of::()), - best.len() - .saturating_mul(std::mem::size_of::()), - ) + results.try_reserve_exact(best.len()).map_err(|error| { + NativeError::Io(std::io::Error::other(format!( + "search result allocation failed: {error}" + ))) })?; for candidate in best { let offset_position = (DOCUMENT_OFFSETS_MAGIC.len() as u64) diff --git a/src/search_index/tests.rs b/src/search_index/tests.rs index 1689d65a..be32cb65 100644 --- a/src/search_index/tests.rs +++ b/src/search_index/tests.rs @@ -79,6 +79,51 @@ fn builder_consumes_all_staging_chunks_in_sequence() { ); } +#[test] +fn document_larger_than_batch_target_roundtrips_within_format_limit() { + let fixture = Fixture::new("large-document"); + let identifier = format!("fn:large:{}", "padding".repeat(2_000)); + fixture.write_nodes( + "function", + &[node(&identifier, "large_document_marker", "src/large.rs")], + ); + let built = build(fixture.request(128)).unwrap(); + validate(&fixture.db_path, &built.metadata).unwrap(); + let hits = search( + &fixture.db_path, + &built.metadata, + "large_document_marker", + "semantic", + 10, + ) + .unwrap(); + assert_eq!(hits[0].id, identifier); + let mut file = fs::File::open(sidecar_path( + &fixture.db_path, + super::model::DOCUMENTS_SUFFIX, + )) + .unwrap(); + let decoded = super::io::read_document_at( + &mut file, + super::model::DOCUMENTS_MAGIC.len() as u64, + super::model::MAX_DOCUMENT_RECORD_BYTES, + ) + .unwrap(); + assert_eq!(decoded.id, identifier); +} + +#[test] +fn document_format_bound_still_rejects_an_unreadable_record() { + let fixture = Fixture::new("invalid-large-document"); + let identifier = "x".repeat(super::model::MAX_DOCUMENT_RECORD_BYTES); + fixture.write_nodes( + "function", + &[node(&identifier, "format_limit_marker", "src/large.rs")], + ); + let error = build(fixture.request(128)).unwrap_err(); + assert!(error.to_string().contains("sidecar read-format length")); +} + #[test] fn tokenizer_indexes_unicode_identifiers_case_insensitively() { let fixture = Fixture::new("unicode"); diff --git a/src/staging_writer/accumulator.rs b/src/staging_writer/accumulator.rs index 6edc9dde..293c53cb 100644 --- a/src/staging_writer/accumulator.rs +++ b/src/staging_writer/accumulator.rs @@ -1,17 +1,20 @@ -use super::files::{copy_path, stage_file_stem, write_csv_field}; +use super::files::{ + copy_path, create_staging_directory, finish_output, stage_file_stem, write_csv_field, + StagingOutputFile, +}; use super::merge::{merge_edge_value, merge_node_value}; use super::result::StagingResult; use super::rows::{edge_fields, node_fields, EdgeStagedRow, NodeStagedRow}; use super::spill::{ - encode_bounded, encode_output_bounded, RecordFileReader, RecordFileWriter, SortedSpool, - SpillMetrics, SpillRecord, + encode_output, encode_record, RecordFileReader, RecordFileWriter, SortedSpool, SpillMetrics, + SpillRecord, }; -use crate::error::{MemoryBudgetExceeded, NativeError}; +use crate::error::NativeError; use crate::partition_builder::GraphPartition; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::{BTreeMap, BTreeSet}; -use std::fs::{self, File}; +use std::fs; use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicU64, Ordering}; @@ -30,6 +33,11 @@ pub(crate) struct StagingAccumulator { pending_error: Option, relation_constraints: RelationConstraints, metrics: SpillMetrics, + // Last owner: actual legacy source admission survives all spool/output + // owners and transfers to StagingResult through native COPY/loader close. + legacy_staging: Option< + std::sync::Arc, + >, } #[derive(Debug, Default)] @@ -60,13 +68,18 @@ impl StagingAccumulator { output_chunk_limit: usize, ) -> Result { let staging_dir = PathBuf::from(staging_dir); + let legacy_staging = + crate::storage::maintenance_legacy_guard::before_staging_producer(&staging_dir)? + .map(std::sync::Arc::new); let sequence = RUN_SEQUENCE.fetch_add(1, Ordering::Relaxed); let run_root = staging_dir.join(format!(".runs-{}-{sequence}", std::process::id())); let metrics = SpillMetrics::default(); let raw_records = SortedSpool::new(&run_root, "staged", chunk_limit, metrics.clone())?; + let mut run_workspace = RunWorkspace::new(run_root); + run_workspace.defer_disposal |= legacy_staging.is_some(); Ok(Self { staging_dir, - run_workspace: RunWorkspace::new(run_root), + run_workspace, chunk_limit, output_chunk_limit, raw_records: Some(raw_records), @@ -74,6 +87,7 @@ impl StagingAccumulator { pending_error: None, relation_constraints: RelationConstraints::from_declared_schema(), metrics, + legacy_staging, }) } @@ -82,9 +96,11 @@ impl StagingAccumulator { let staging_dir = PathBuf::from(staging_dir); let sequence = RUN_SEQUENCE.fetch_add(1, Ordering::Relaxed); Self { - run_workspace: RunWorkspace::new( - staging_dir.join(format!(".runs-{}-{sequence}", std::process::id())), - ), + run_workspace: RunWorkspace { + path: staging_dir.join(format!(".runs-{}-{sequence}", std::process::id())), + // Failed initialization created no outputs to own or remove. + defer_disposal: true, + }, staging_dir, chunk_limit, output_chunk_limit: chunk_limit, @@ -93,6 +109,7 @@ impl StagingAccumulator { pending_error: Some(error), relation_constraints: RelationConstraints::from_declared_schema(), metrics: SpillMetrics::default(), + legacy_staging: None, } } @@ -169,7 +186,7 @@ impl StagingAccumulator { if let Some(error) = self.pending_error.take() { return Err(error); } - fs::create_dir_all(&self.staging_dir)?; + create_staging_directory(&self.staging_dir)?; let raw_records = self.raw_records.take().ok_or_else(|| { NativeError::InvalidInput("staging record spool was not initialized".to_string()) })?; @@ -177,18 +194,8 @@ impl StagingAccumulator { let run_root = self.run_workspace.path(); let merged_edges_path = run_root.join("merged-edges.bin"); let node_types_path = run_root.join("node-types.bin"); - let mut merged_edges = RecordFileWriter::create( - &merged_edges_path, - self.chunk_limit, - "staging_edges", - self.metrics.clone(), - )?; - let mut node_types = RecordFileWriter::create( - &node_types_path, - self.chunk_limit, - "staging_node_types", - self.metrics.clone(), - )?; + let mut merged_edges = RecordFileWriter::create(&merged_edges_path, self.metrics.clone())?; + let mut node_types = RecordFileWriter::create(&node_types_path, self.metrics.clone())?; let mut endpoints = SortedSpool::new( run_root, "endpoints", @@ -197,7 +204,6 @@ impl StagingAccumulator { )?; let mut nodes = JsonTableSink::new( &self.staging_dir, - self.chunk_limit, self.output_chunk_limit, self.metrics.clone(), ); @@ -305,7 +311,6 @@ impl StagingAccumulator { let mut merged_edge_reader = RecordFileReader::::open(&merged_edges_path)?; let mut edges = JsonTableSink::new( &self.staging_dir, - self.chunk_limit, self.output_chunk_limit, self.metrics.clone(), ); @@ -381,7 +386,6 @@ impl StagingAccumulator { let mut connector_sink = ConnectorSink::new( &self.staging_dir, - self.chunk_limit, self.output_chunk_limit, self.metrics.clone(), ); @@ -411,19 +415,16 @@ impl StagingAccumulator { unique_edge_count, spill_bytes, high_water_bytes, + _legacy_staging: self.legacy_staging.take(), }) } fn take_source_order(&mut self) -> Result { let order = self.next_source_order; - self.next_source_order = self.next_source_order.checked_add(1).ok_or_else(|| { - NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new( - "staging_order", - u64::MAX, - u64::MAX, - 0, - )) - })?; + self.next_source_order = self + .next_source_order + .checked_add(1) + .ok_or_else(|| NativeError::InvalidInput("staging source order overflow".into()))?; Ok(order) } @@ -684,12 +685,11 @@ impl SpillRecord for ConnectorRecord { struct JsonTableSink { staging_dir: PathBuf, - record_limit: usize, chunk_limit: usize, metrics: SpillMetrics, current_table: Option, chunk_index: usize, - writer: Option>, + writer: Option>, first_row: bool, current_bytes: usize, copy_statements: Vec, @@ -697,15 +697,9 @@ struct JsonTableSink { } impl JsonTableSink { - fn new( - staging_dir: &Path, - record_limit: usize, - chunk_limit: usize, - metrics: SpillMetrics, - ) -> Self { + fn new(staging_dir: &Path, chunk_limit: usize, metrics: SpillMetrics) -> Self { Self { staging_dir: staging_dir.to_path_buf(), - record_limit, chunk_limit, metrics, current_table: None, @@ -719,18 +713,8 @@ impl JsonTableSink { } fn push(&mut self, table: &str, row: &T) -> Result<(), NativeError> { - let encoded = encode_bounded(row, self.record_limit, "staging_json_output")?; + let encoded = encode_record(row)?; let _charge = self.metrics.charge(encoded.capacity()); - let single_row_bytes = encoded.len().checked_add(3).ok_or_else(|| { - output_budget_error("staging_json_output", self.record_limit, usize::MAX) - })?; - if single_row_bytes > self.record_limit { - return Err(output_budget_error( - "staging_json_output", - self.record_limit, - single_row_bytes, - )); - } if self.current_table.as_deref() != Some(table) { self.close_chunk()?; self.current_table = Some(table.to_string()); @@ -743,9 +727,7 @@ impl JsonTableSink { .checked_add(separator_bytes) .and_then(|bytes| bytes.checked_add(encoded.len())) .and_then(|bytes| bytes.checked_add(2)) - .ok_or_else(|| { - output_budget_error("staging_json_output", self.chunk_limit, usize::MAX) - })?; + .ok_or_else(|| NativeError::InvalidInput("staging JSON size overflow".into()))?; if !self.first_row && projected_bytes > self.chunk_limit { self.close_chunk()?; self.chunk_index = self.chunk_index.checked_add(1).ok_or_else(|| { @@ -792,7 +774,7 @@ impl JsonTableSink { "json", self.chunk_index, ); - let mut writer = BufWriter::new(File::create(&path)?); + let mut writer = BufWriter::new(StagingOutputFile::create(&path)?); writer.write_all(b"[")?; self.copy_statements .push(format!("COPY `{}` FROM \"{}\";", table, copy_path(&path))); @@ -805,7 +787,7 @@ impl JsonTableSink { fn close_chunk(&mut self) -> Result<(), NativeError> { if let Some(mut writer) = self.writer.take() { writer.write_all(b"]\n")?; - writer.flush()?; + finish_output(writer)?; } self.current_bytes = 0; Ok(()) @@ -814,12 +796,11 @@ impl JsonTableSink { struct ConnectorSink { staging_dir: PathBuf, - record_limit: usize, chunk_limit: usize, metrics: SpillMetrics, current_group: Option<(String, EndpointSide, String, String)>, chunk_index: usize, - writer: Option>, + writer: Option>, current_bytes: usize, rows_in_chunk: usize, copy_statements: Vec, @@ -827,15 +808,9 @@ struct ConnectorSink { } impl ConnectorSink { - fn new( - staging_dir: &Path, - record_limit: usize, - chunk_limit: usize, - metrics: SpillMetrics, - ) -> Self { + fn new(staging_dir: &Path, chunk_limit: usize, metrics: SpillMetrics) -> Self { Self { staging_dir: staging_dir.to_path_buf(), - record_limit, chunk_limit, metrics, current_group: None, @@ -861,35 +836,19 @@ impl ConnectorSink { self.chunk_index = 0; self.open_chunk()?; } - let encoded = - encode_output_bounded(self.record_limit, "staging_connector_output", |writer| { - write_csv_field(writer, &connector.from_id)?; - writer.write_all(b",")?; - write_csv_field(writer, &connector.to_id)?; - writer.write_all(b",")?; - write_csv_field(writer, &connector.role)?; - writer.write_all(b"\r\n") - })?; + let encoded = encode_output(|writer| { + write_csv_field(writer, &connector.from_id)?; + writer.write_all(b",")?; + write_csv_field(writer, &connector.to_id)?; + writer.write_all(b",")?; + write_csv_field(writer, &connector.role)?; + writer.write_all(b"\r\n") + })?; let _charge = self.metrics.charge(encoded.capacity()); - let single_row_bytes = CONNECTOR_HEADER - .len() - .checked_add(encoded.len()) - .ok_or_else(|| { - output_budget_error("staging_connector_output", self.record_limit, usize::MAX) - })?; - if single_row_bytes > self.record_limit { - return Err(output_budget_error( - "staging_connector_output", - self.record_limit, - single_row_bytes, - )); - } let projected_bytes = self .current_bytes .checked_add(encoded.len()) - .ok_or_else(|| { - output_budget_error("staging_connector_output", self.chunk_limit, usize::MAX) - })?; + .ok_or_else(|| NativeError::InvalidInput("staging connector size overflow".into()))?; if self.rows_in_chunk > 0 && projected_bytes > self.chunk_limit { self.close_chunk()?; self.chunk_index = self.chunk_index.checked_add(1).ok_or_else(|| { @@ -937,7 +896,7 @@ impl ConnectorSink { stage_file_stem(to_type) ); let path = chunked_output_path(&self.staging_dir, &stem, "csv", self.chunk_index); - let mut writer = BufWriter::new(File::create(&path)?); + let mut writer = BufWriter::new(StagingOutputFile::create(&path)?); writer.write_all(CONNECTOR_HEADER)?; self.copy_statements.push(format!( "COPY `{}` FROM \"{}\" (header=true, from=\"{}\", to=\"{}\");", @@ -953,8 +912,8 @@ impl ConnectorSink { } fn close_chunk(&mut self) -> Result<(), NativeError> { - if let Some(mut writer) = self.writer.take() { - writer.flush()?; + if let Some(writer) = self.writer.take() { + finish_output(writer)?; } self.current_bytes = 0; self.rows_in_chunk = 0; @@ -972,22 +931,21 @@ fn chunked_output_path(root: &Path, stem: &str, extension: &str, index: usize) - } } -fn output_budget_error(phase: &str, limit: usize, accounted: usize) -> NativeError { - NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new( - phase, - u64::try_from(limit).unwrap_or(u64::MAX), - u64::try_from(accounted).unwrap_or(u64::MAX), - 0, - )) -} - struct RunWorkspace { path: PathBuf, + defer_disposal: bool, } impl RunWorkspace { fn new(path: PathBuf) -> Self { - Self { path } + // This predicate only restricts cleanup; it grants no Origin or + // authority over this directory or any injected child. + let defer_disposal = crate::db_writer::phase_has_parent_workspace() + || crate::storage::maintenance_controls::workspace_retention_required(&path); + Self { + path, + defer_disposal, + } } fn path(&self) -> &Path { @@ -997,6 +955,11 @@ impl RunWorkspace { impl Drop for RunWorkspace { fn drop(&mut self) { + if self.defer_disposal { + // Genuine directory/leaf receipts let the bounded terminal lane + // dispose owned entries. Recursive Drop cannot infer child Origin. + return; + } if self.path.is_dir() { let _ = fs::remove_dir_all(&self.path); } @@ -1054,11 +1017,88 @@ mod output_tests { use serde::Serialize; use std::time::{SystemTime, UNIX_EPOCH}; - #[derive(Serialize)] + #[derive(Serialize, serde::Deserialize)] struct OutputRow { id: String, } + #[test] + fn unregistered_preexisting_output_preserves_plain_write_behavior_without_adoption() { + let root = temp_root("untracked-existing-output"); + let path = root.join("caller-owned.csv"); + fs::write(&path, b"previous caller bytes").unwrap(); + let output = StagingOutputFile::create(&path).unwrap(); + assert!(matches!(&output, StagingOutputFile::Plain(_))); + let mut buffered = BufWriter::new(output); + buffered.write_all(b"new accepted bytes").unwrap(); + finish_output(buffered).unwrap(); + assert_eq!(fs::read(&path).unwrap(), b"new accepted bytes"); + assert_eq!( + fs::read_dir(&root).unwrap().count(), + 1, + "unknown output gained origin metadata" + ); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn registered_spool_drop_preserves_unreceipted_injected_child_for_terminal_collection() { + let root = temp_root("registered-spool-retention"); + let store = crate::storage::managed::GraphStorage::managed(root.join("storage")); + let session = store.begin_write().unwrap(); + assert!( + session.phase_owner_scope().is_some(), + "real fresh run was not registered" + ); + let spool = session + .staging_root() + .unwrap() + .join(".runs-retention-probe"); + create_staging_directory(&spool).unwrap(); + let child = spool.join("injected-unknown"); + fs::write(&child, b"must remain untouched").unwrap(); + drop(RunWorkspace::new(spool)); + assert_eq!(fs::read(&child).unwrap(), b"must remain untouched"); + session.abort(None).unwrap(); + assert_eq!(fs::read(&child).unwrap(), b"must remain untouched"); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn legacy_staging_gate_survives_finish_and_result_clones_until_loader_owner_drops() { + use crate::storage::locks::{try_open_existing_locked, LockMode}; + let root = temp_root("legacy-staging-result-lifetime"); + let state = root.join(".codebaseGraph"); + fs::create_dir(&state).unwrap(); + let staging = state.join("native-staging"); + let accumulator = + StagingAccumulator::with_limits(staging.to_str().unwrap(), 4096, 4096).unwrap(); + let gate = state.join(".legacy-staging.use.lock"); + assert!(try_open_existing_locked(&gate, LockMode::Exclusive) + .unwrap() + .is_none()); + let result = accumulator.finish().unwrap(); + assert!( + try_open_existing_locked(&gate, LockMode::Exclusive) + .unwrap() + .is_none(), + "finish released source admission before the returned CSV paths were consumed" + ); + let clone = result.clone(); + drop(result); + assert!(try_open_existing_locked(&gate, LockMode::Exclusive) + .unwrap() + .is_none()); + drop(clone); + assert!( + try_open_existing_locked(&gate, LockMode::Exclusive) + .unwrap() + .is_some(), + "last internal loader owner failed to release the actual source gate" + ); + fs::remove_dir_all(root).unwrap(); + } + #[test] fn json_output_rotates_at_the_byte_limit() { let root = temp_root("json-chunks"); @@ -1067,7 +1107,7 @@ mod output_tests { }; let encoded_len = serde_json::to_vec(&row).unwrap().len(); let chunk_limit = encoded_len + 3; - let mut sink = JsonTableSink::new(&root, chunk_limit, chunk_limit, SpillMetrics::default()); + let mut sink = JsonTableSink::new(&root, chunk_limit, SpillMetrics::default()); sink.push("Symbol", &row).unwrap(); sink.push("Symbol", &row).unwrap(); @@ -1087,42 +1127,92 @@ mod output_tests { } #[test] - fn final_outputs_reject_single_rows_larger_than_the_chunk() { - let root = temp_root("oversized-output"); - let row = OutputRow { - id: "x".repeat(128), + fn final_output_limits_include_json_framing_and_csv_header() { + let root = temp_root("exact-output-boundaries"); + let json_row = OutputRow { + id: "naïve,\"line\n".to_string(), }; - let encoded_len = serde_json::to_vec(&row).unwrap().len(); - let mut json = JsonTableSink::new( - &root, - encoded_len + 2, - encoded_len + 2, - SpillMetrics::default(), + let json_row_bytes = serde_json::to_vec(&json_row).unwrap().len(); + let json_limit = json_row_bytes + 3; + let mut json = JsonTableSink::new(&root, json_limit, SpillMetrics::default()); + json.push("Symbol", &json_row).unwrap(); + let (statements, rows) = json.finish().unwrap(); + assert_eq!(rows, 1); + assert_eq!(statements.len(), 1); + assert_eq!( + fs::read(root.join("symbol.json")).unwrap().len(), + json_limit ); - let error = json.push("Symbol", &row).unwrap_err(); - assert_budget_error(error, "staging_json_output", encoded_len + 2); let connector = ConnectorRecord { relation: "Contains".to_string(), side: EndpointSide::Source, from_type: "File".to_string(), to_type: "Contains".to_string(), - from_id: "source".repeat(64), + from_id: "naïve,\"source\n".to_string(), to_id: "edge:one".to_string(), role: "source".to_string(), }; - let mut connector_sink = ConnectorSink::new(&root, 64, 64, SpillMetrics::default()); - let error = connector_sink.push(&connector).unwrap_err(); - assert_budget_error(error, "staging_connector_output", 64); + let encoded = encode_output(|writer| { + write_csv_field(writer, &connector.from_id)?; + writer.write_all(b",")?; + write_csv_field(writer, &connector.to_id)?; + writer.write_all(b",")?; + write_csv_field(writer, &connector.role)?; + writer.write_all(b"\r\n") + }) + .unwrap(); + let csv_limit = CONNECTOR_HEADER.len() + encoded.len(); + let mut csv = ConnectorSink::new(&root, csv_limit, SpillMetrics::default()); + csv.push(&connector).unwrap(); + let (statements, rows) = csv.finish().unwrap(); + assert_eq!(rows, 1); + assert_eq!(statements.len(), 1); + let csv_path = root.join("from_contains__file__contains.csv"); + assert_eq!(fs::read(csv_path).unwrap().len(), csv_limit); } - fn assert_budget_error(error: NativeError, phase: &str, limit: usize) { - let NativeError::MemoryBudgetExceeded(error) = error else { - panic!("expected structured memory budget error"); + #[test] + fn oversized_rows_use_individual_chunks_without_rejection() { + let root = temp_root("oversized-output"); + let row = OutputRow { + id: "x".repeat(512), }; - assert_eq!(error.phase, phase); - assert_eq!(error.limit_bytes, limit as u64); - assert!(error.accounted_bytes > error.limit_bytes); + let mut json = JsonTableSink::new(&root, 64, SpillMetrics::default()); + json.push("Symbol", &row).unwrap(); + json.push("Symbol", &row).unwrap(); + let (statements, rows) = json.finish().unwrap(); + assert_eq!(rows, 2); + assert_eq!(statements.len(), 2); + for file in ["symbol.json", "symbol__000001.json"] { + let decoded: Vec = + serde_json::from_slice(&fs::read(root.join(file)).unwrap()).unwrap(); + assert_eq!(decoded.len(), 1); + assert_eq!(decoded[0].id, row.id); + } + let connector = ConnectorRecord { + relation: "Contains".into(), + side: EndpointSide::Source, + from_type: "File".into(), + to_type: "Contains".into(), + from_id: "naïve,\"source\n".repeat(64), + to_id: "edge:one".into(), + role: "source".into(), + }; + let mut csv = ConnectorSink::new(&root, 64, SpillMetrics::default()); + csv.push(&connector).unwrap(); + csv.push(&connector).unwrap(); + let (statements, rows) = csv.finish().unwrap(); + assert_eq!(rows, 2); + assert_eq!(statements.len(), 2); + for file in [ + "from_contains__file__contains.csv", + "from_contains__file__contains__000001.csv", + ] { + let contents = fs::read(root.join(file)).unwrap(); + assert!(contents.starts_with(CONNECTOR_HEADER)); + assert!(contents.len() > 64); + } } fn temp_root(name: &str) -> PathBuf { @@ -1130,7 +1220,7 @@ mod output_tests { .duration_since(UNIX_EPOCH) .unwrap() .as_nanos(); - let root = std::env::temp_dir().join(format!( + let root = std::env::temp_dir().canonicalize().unwrap().join(format!( "codebase_graph_output_{name}_{}_{nanos}", std::process::id() )); diff --git a/src/staging_writer/files.rs b/src/staging_writer/files.rs index 72a95bb7..d0009cc6 100644 --- a/src/staging_writer/files.rs +++ b/src/staging_writer/files.rs @@ -1,4 +1,6 @@ -use std::io::{self, Write}; +use crate::error::NativeError; +use std::fs::File; +use std::io::{self, BufWriter, Write}; use std::path::Path; pub(super) fn write_csv_field(writer: &mut dyn Write, value: &str) -> io::Result<()> { @@ -42,7 +44,7 @@ pub(super) fn stage_file_stem(name: &str) -> String { } } -pub(super) fn copy_path(path: &Path) -> String { +pub(crate) fn copy_path(path: &Path) -> String { let mut copy_path = path.to_string_lossy().replace('\\', "/"); if let Some(stripped) = copy_path.strip_prefix("//?/UNC/") { copy_path = format!("//{stripped}"); @@ -51,3 +53,68 @@ pub(super) fn copy_path(path: &Path) -> String { } copy_path.replace('"', "\\\"") } + +/// Tracking is optional producer metadata. Only the factory's genuine new-file +/// token may produce Origin; ordinary caller-owned or preexisting paths retain +/// their historical File::create behavior without being adopted. +pub(super) enum StagingOutputFile { + Tracked(Box), + Plain(File), +} +impl StagingOutputFile { + pub(super) fn create(path: &Path) -> Result { + match crate::storage::maintenance_controls::WorkspaceOutputFile::create_new(path) { + Ok(Some(output)) => { + let _ = + crate::storage::maintenance_origin::record_created_workspace_output(&output); + Ok(Self::Tracked(Box::new(output))) + } + Ok(None) | Err(_) => Ok(Self::Plain(File::create(path)?)), + } + } + fn finish(self) { + if let Self::Tracked(output) = self { + if let Ok(completed) = (*output).finish() { + // Keep the actual completed-output guards through receipt + // publication. Failure preserves the primary staging result; + // the unproven output remains ineligible for collection. + let _ = crate::storage::maintenance_origin::record_completed_workspace_output( + &completed, + ); + } + } + } +} +impl Write for StagingOutputFile { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + match self { + Self::Tracked(output) => output.write(bytes), + Self::Plain(output) => output.write(bytes), + } + } + fn flush(&mut self) -> std::io::Result<()> { + match self { + Self::Tracked(output) => output.flush(), + Self::Plain(output) => output.flush(), + } + } +} +pub(super) fn finish_output(mut writer: BufWriter) -> Result<(), NativeError> { + writer.flush()?; + let output = writer.into_inner().map_err(|error| error.into_error())?; + output.finish(); + Ok(()) +} + +/// Only an actual exclusive directory-creation token may attest namespace +/// birth. Existing/unregistered paths preserve primary creation behavior. +pub(super) fn create_staging_directory(path: &Path) -> Result<(), NativeError> { + match crate::storage::maintenance_controls::WorkspaceCreatedDirectory::create_new(path) { + Ok(Some(created)) => { + let _ = + crate::storage::maintenance_origin::record_created_workspace_directory(&created); + Ok(()) + } + Ok(None) | Err(_) => Ok(std::fs::create_dir_all(path)?), + } +} diff --git a/src/staging_writer/mod.rs b/src/staging_writer/mod.rs index 1687da93..1039a732 100644 --- a/src/staging_writer/mod.rs +++ b/src/staging_writer/mod.rs @@ -10,5 +10,8 @@ pub(crate) use accumulator::StagingAccumulator; pub(crate) use result::StagingResult; pub(crate) use writer::write_graph_rows; +#[cfg(test)] +pub(crate) use files::copy_path; + #[cfg(test)] mod tests; diff --git a/src/staging_writer/result.rs b/src/staging_writer/result.rs index e1d0a5cc..13694106 100644 --- a/src/staging_writer/result.rs +++ b/src/staging_writer/result.rs @@ -9,4 +9,9 @@ pub(crate) struct StagingResult { pub(crate) unique_edge_count: usize, pub(crate) spill_bytes: u64, pub(crate) high_water_bytes: usize, + // Internal lifetime ownership only; this result is consumed after actual + // native COPY closes, before constructing the public operation response. + pub(super) _legacy_staging: Option< + std::sync::Arc, + >, } diff --git a/src/staging_writer/spill.rs b/src/staging_writer/spill.rs index 27d45190..a1afe995 100644 --- a/src/staging_writer/spill.rs +++ b/src/staging_writer/spill.rs @@ -1,4 +1,5 @@ -use crate::error::{MemoryBudgetExceeded, NativeError}; +use super::files::{create_staging_directory, finish_output, StagingOutputFile}; +use crate::error::NativeError; use serde::de::DeserializeOwned; use serde::Serialize; use std::cmp::Ordering; @@ -117,7 +118,6 @@ struct BufferedRecord { #[derive(Clone, Debug)] struct RunDescriptor { path: PathBuf, - max_heap_record_bytes: usize, } pub(super) struct SortedSpool { @@ -128,7 +128,6 @@ pub(super) struct SortedSpool { buffered_bytes: usize, runs: Vec, next_run: usize, - max_buffered_heap_record_bytes: usize, metrics: SpillMetrics, marker: PhantomData, } @@ -141,7 +140,9 @@ impl SortedSpool { metrics: SpillMetrics, ) -> Result { if chunk_limit == 0 { - return Err(memory_budget_error(prefix, 0, 1)); + return Err(NativeError::InvalidInput( + "spill chunk target must be positive".into(), + )); } Ok(Self { root: root.to_path_buf(), @@ -151,40 +152,24 @@ impl SortedSpool { buffered_bytes: 0, runs: Vec::new(), next_run: 0, - max_buffered_heap_record_bytes: 0, metrics, marker: PhantomData, }) } pub(super) fn push(&mut self, record: &T) -> Result<(), NativeError> { - let encoded = encode_bounded(record, self.chunk_limit, &self.prefix)?; + let encoded = encode_record(record)?; let key = record.sort_key(); let record_bytes = encoded .capacity() .checked_add(T::key_bytes(&key)) .and_then(|bytes| bytes.checked_add(LENGTH_PREFIX_BYTES)) .and_then(|bytes| bytes.checked_add(std::mem::size_of::>())) - .ok_or_else(|| memory_budget_error(&self.prefix, self.chunk_limit, usize::MAX))?; - if record_bytes > self.chunk_limit { - return Err(memory_budget_error( - &self.prefix, - self.chunk_limit, - record_bytes, - )); - } + .ok_or_else(|| NativeError::InvalidInput("spill record size overflow".into()))?; let next_bytes = self .buffered_bytes .checked_add(record_bytes) - .ok_or_else(|| memory_budget_error(&self.prefix, self.chunk_limit, usize::MAX))?; - let heap_record_bytes = heap_item_bytes::(&key, &encoded)?; - if heap_record_bytes > self.chunk_limit { - return Err(memory_budget_error( - &format!("{}_merge", self.prefix), - self.chunk_limit, - heap_record_bytes, - )); - } + .ok_or_else(|| NativeError::InvalidInput("spill record size overflow".into()))?; self.metrics.add_live(record_bytes); if !self.buffered.is_empty() && next_bytes > self.chunk_limit { if let Err(error) = self.flush() { @@ -194,120 +179,60 @@ impl SortedSpool { } if self.buffered.try_reserve_exact(1).is_err() { self.metrics.release_live(record_bytes); - return Err(memory_budget_error( - &self.prefix, - self.chunk_limit, - self.buffered_bytes, - )); + return Err(NativeError::Io(io::Error::other( + "spill buffer allocation failed", + ))); } self.buffered.push(BufferedRecord { key, encoded }); self.buffered_bytes = self .buffered_bytes .checked_add(record_bytes) - .ok_or_else(|| memory_budget_error(&self.prefix, self.chunk_limit, usize::MAX))?; - self.max_buffered_heap_record_bytes = - self.max_buffered_heap_record_bytes.max(heap_record_bytes); + .ok_or_else(|| NativeError::InvalidInput("spill record size overflow".into()))?; + if self.buffered_bytes >= self.chunk_limit { + self.flush()?; + } Ok(()) } pub(super) fn finish(mut self) -> Result, NativeError> { self.flush()?; let mut runs = std::mem::take(&mut self.runs); - while !runs_fit_budget(&runs, self.chunk_limit) { + while runs.len() > MAX_MERGE_FAN_IN { runs = self.merge_level(runs)?; } - SortedStream::open( - &runs, - self.chunk_limit, - self.metrics.clone(), - &format!("{}_merge", self.prefix), - true, - ) + SortedStream::open(&runs, self.metrics.clone(), true) } fn flush(&mut self) -> Result<(), NativeError> { if self.buffered.is_empty() { return Ok(()); } - fs::create_dir_all(&self.root)?; + create_staging_directory(&self.root)?; self.buffered .sort_by(|left, right| left.key.cmp(&right.key)); let path = self.next_path("run"); - let mut writer = BufWriter::new(File::create(&path)?); + let mut writer = BufWriter::new(StagingOutputFile::create(&path)?); for record in &self.buffered { write_encoded(&mut writer, &record.encoded, &self.metrics)?; } - writer.flush()?; - self.runs.push(RunDescriptor { - path, - max_heap_record_bytes: self.max_buffered_heap_record_bytes, - }); + finish_output(writer)?; + self.runs.push(RunDescriptor { path }); let released_bytes = self.buffered_bytes; self.buffered = Vec::new(); self.buffered_bytes = 0; - self.max_buffered_heap_record_bytes = 0; self.metrics.release_live(released_bytes); Ok(()) } - fn merge_level( - &mut self, - mut runs: Vec, - ) -> Result, NativeError> { - runs.sort_by(|left, right| { - right - .max_heap_record_bytes - .cmp(&left.max_heap_record_bytes) - .then_with(|| left.path.cmp(&right.path)) - }); - let original_count = runs.len(); - let mut groups: Vec> = Vec::new(); - for run in runs { - let mut selected = None; - for (index, group) in groups.iter().enumerate() { - let group_bytes = group.iter().fold(0_usize, |total, descriptor| { - total.saturating_add(descriptor.max_heap_record_bytes) - }); - if group.len() < MAX_MERGE_FAN_IN - && group_bytes.saturating_add(run.max_heap_record_bytes) <= self.chunk_limit - { - selected = Some(index); - break; - } - } - if let Some(index) = selected { - groups[index].push(run); - } else { - groups.push(vec![run]); - } - } - + fn merge_level(&mut self, runs: Vec) -> Result, NativeError> { let mut merged = Vec::new(); - merged - .try_reserve_exact(groups.len()) - .map_err(|_| memory_budget_error(&self.prefix, self.chunk_limit, groups.len()))?; - for group in groups { + for group in runs.chunks(MAX_MERGE_FAN_IN) { if group.len() == 1 { - merged.extend(group); - continue; + merged.push(group[0].clone()); + } else { + let output = self.next_path("merge"); + merged.push(merge_run_group::(group, &output, &self.metrics)?); } - let output = self.next_path("merge"); - let descriptor = merge_run_group::( - &group, - &output, - self.chunk_limit, - &format!("{}_merge", self.prefix), - &self.metrics, - )?; - merged.push(descriptor); - } - if merged.len() >= original_count { - let accounted = smallest_pair_bytes(&merged).unwrap_or(usize::MAX); - return Err(memory_budget_error( - &format!("{}_merge", self.prefix), - self.chunk_limit, - accounted, - )); } Ok(merged) } @@ -335,48 +260,36 @@ pub(super) struct SortedStream { heap_bytes: usize, outstanding_bytes: usize, pending_reader: Option, - heap_budget: usize, metrics: SpillMetrics, - phase: String, input_paths: Vec, cleanup_when_exhausted: bool, } pub(super) struct RecordFileWriter { - writer: BufWriter, - record_limit: usize, - phase: String, + writer: BufWriter, metrics: SpillMetrics, marker: PhantomData, } impl RecordFileWriter { - pub(super) fn create( - path: &Path, - record_limit: usize, - phase: &str, - metrics: SpillMetrics, - ) -> Result { + pub(super) fn create(path: &Path, metrics: SpillMetrics) -> Result { if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; + create_staging_directory(parent)?; } Ok(Self { - writer: BufWriter::new(File::create(path)?), - record_limit, - phase: phase.to_string(), + writer: BufWriter::new(StagingOutputFile::create(path)?), metrics, marker: PhantomData, }) } pub(super) fn push(&mut self, record: &T) -> Result<(), NativeError> { - let encoded = encode_bounded(record, self.record_limit, &self.phase)?; + let encoded = encode_record(record)?; write_encoded(&mut self.writer, &encoded, &self.metrics) } - pub(super) fn finish(mut self) -> Result<(), NativeError> { - self.writer.flush()?; - Ok(()) + pub(super) fn finish(self) -> Result<(), NativeError> { + finish_output(self.writer) } } @@ -404,16 +317,14 @@ impl RecordFileReader { impl SortedStream { fn open( descriptors: &[RunDescriptor], - heap_budget: usize, metrics: SpillMetrics, - phase: &str, cleanup_when_exhausted: bool, ) -> Result { let mut live_charge = LiveChargeGuard::new(metrics.clone()); let mut readers = Vec::new(); readers .try_reserve_exact(descriptors.len()) - .map_err(|_| memory_budget_error(phase, heap_budget, descriptors.len()))?; + .map_err(|_| NativeError::Io(io::Error::other("merge reader allocation failed")))?; let mut heap = BinaryHeap::new(); let mut heap_bytes = 0_usize; for (reader_index, descriptor) in descriptors.iter().enumerate() { @@ -424,12 +335,10 @@ impl SortedStream { let accounted_bytes = heap_item_bytes::(&key, &encoded)?; let next_heap_bytes = heap_bytes .checked_add(accounted_bytes) - .ok_or_else(|| memory_budget_error(phase, heap_budget, usize::MAX))?; - if next_heap_bytes > heap_budget { - return Err(memory_budget_error(phase, heap_budget, next_heap_bytes)); - } - heap.try_reserve(1) - .map_err(|_| memory_budget_error(phase, heap_budget, next_heap_bytes))?; + .ok_or_else(|| NativeError::InvalidInput("merge accounting overflow".into()))?; + heap.try_reserve(1).map_err(|_| { + NativeError::Io(io::Error::other("merge heap allocation failed")) + })?; metrics.add_live(accounted_bytes); live_charge.add(accounted_bytes); heap.push(HeapItem { @@ -450,9 +359,7 @@ impl SortedStream { heap_bytes, outstanding_bytes: 0, pending_reader: None, - heap_budget, metrics, - phase: phase.to_string(), input_paths: descriptors .iter() .map(|descriptor| descriptor.path.clone()) @@ -475,21 +382,12 @@ impl SortedStream { let record = serde_json::from_slice::(&encoded)?; let key = record.sort_key(); let accounted_bytes = heap_item_bytes::(&key, &encoded)?; - let next_heap_bytes = - self.heap_bytes - .checked_add(accounted_bytes) - .ok_or_else(|| { - memory_budget_error(&self.phase, self.heap_budget, usize::MAX) - })?; - if next_heap_bytes > self.heap_budget { - return Err(memory_budget_error( - &self.phase, - self.heap_budget, - next_heap_bytes, - )); - } + let next_heap_bytes = self + .heap_bytes + .checked_add(accounted_bytes) + .ok_or_else(|| NativeError::InvalidInput("merge accounting overflow".into()))?; self.heap.try_reserve(1).map_err(|_| { - memory_budget_error(&self.phase, self.heap_budget, next_heap_bytes) + NativeError::Io(io::Error::other("merge heap allocation failed")) })?; self.metrics.add_live(accounted_bytes); self.heap.push(HeapItem { @@ -627,7 +525,7 @@ impl RunReader { let mut encoded = Vec::new(); encoded .try_reserve_exact(length) - .map_err(|_| memory_budget_error("run_record", length, length))?; + .map_err(|_| NativeError::Io(io::Error::other("spill record allocation failed")))?; encoded.resize(length, 0); self.reader.read_exact(&mut encoded)?; Ok(Some(encoded)) @@ -637,61 +535,30 @@ impl RunReader { fn merge_run_group( inputs: &[RunDescriptor], output: &Path, - heap_budget: usize, - phase: &str, metrics: &SpillMetrics, ) -> Result { - let max_heap_record_bytes = inputs - .iter() - .map(|input| input.max_heap_record_bytes) - .max() - .unwrap_or_default(); - let mut stream = SortedStream::::open(inputs, heap_budget, metrics.clone(), phase, false)?; - let mut writer = BufWriter::new(File::create(output)?); + let mut stream = SortedStream::::open(inputs, metrics.clone(), false)?; + let mut writer = BufWriter::new(StagingOutputFile::create(output)?); while let Some(item) = stream.next_encoded()? { write_encoded(&mut writer, &item.encoded, metrics)?; } - writer.flush()?; - drop(writer); + finish_output(writer)?; drop(stream); for input in inputs { fs::remove_file(&input.path)?; } Ok(RunDescriptor { path: output.to_path_buf(), - max_heap_record_bytes, }) } -fn runs_fit_budget(runs: &[RunDescriptor], heap_budget: usize) -> bool { - runs.len() <= MAX_MERGE_FAN_IN - && runs - .iter() - .try_fold(0_usize, |total, run| { - total.checked_add(run.max_heap_record_bytes) - }) - .is_some_and(|bytes| bytes <= heap_budget) -} - -fn smallest_pair_bytes(runs: &[RunDescriptor]) -> Option { - let mut sizes = runs - .iter() - .map(|run| run.max_heap_record_bytes) - .collect::>(); - sizes.sort_unstable(); - sizes - .first() - .zip(sizes.get(1)) - .map(|(left, right)| left.saturating_add(*right)) -} - fn heap_item_bytes(key: &T::Key, encoded: &[u8]) -> Result { encoded .len() .checked_mul(2) .and_then(|bytes| bytes.checked_add(T::key_bytes(key))) .and_then(|bytes| bytes.checked_add(std::mem::size_of::>())) - .ok_or_else(|| memory_budget_error("merge_heap", usize::MAX, usize::MAX)) + .ok_or_else(|| NativeError::InvalidInput("merge record size overflow".into())) } fn write_encoded( @@ -699,75 +566,39 @@ fn write_encoded( encoded: &[u8], metrics: &SpillMetrics, ) -> Result<(), NativeError> { - let length = u32::try_from(encoded.len()) - .map_err(|_| memory_budget_error("spill_record", u32::MAX as usize, encoded.len()))?; + let length = u32::try_from(encoded.len()).map_err(|_| { + NativeError::InvalidInput("spill record exceeds u32 encoding length".into()) + })?; writer.write_all(&length.to_le_bytes())?; writer.write_all(encoded)?; metrics.add_written((LENGTH_PREFIX_BYTES + encoded.len()) as u64); Ok(()) } -pub(super) fn encode_bounded( - value: &T, - limit: usize, - phase: &str, -) -> Result, NativeError> { - let mut writer = FallibleBuffer::new(limit); - let result = serde_json::to_writer(&mut writer, value); - if writer.exceeded { - return Err(memory_budget_error(phase, limit, writer.attempted)); - } - result?; +pub(super) fn encode_record(value: &T) -> Result, NativeError> { + let mut writer = FallibleBuffer::default(); + serde_json::to_writer(&mut writer, value)?; Ok(writer.bytes) } -pub(super) fn encode_output_bounded( - limit: usize, - phase: &str, +pub(super) fn encode_output( encode: impl FnOnce(&mut dyn Write) -> io::Result<()>, ) -> Result, NativeError> { - let mut writer = FallibleBuffer::new(limit); - let result = encode(&mut writer); - if writer.exceeded { - return Err(memory_budget_error(phase, limit, writer.attempted)); - } - result?; + let mut writer = FallibleBuffer::default(); + encode(&mut writer)?; Ok(writer.bytes) } +#[derive(Default)] struct FallibleBuffer { bytes: Vec, - limit: usize, - attempted: usize, - exceeded: bool, -} - -impl FallibleBuffer { - fn new(limit: usize) -> Self { - Self { - bytes: Vec::new(), - limit, - attempted: 0, - exceeded: false, - } - } } impl Write for FallibleBuffer { fn write(&mut self, source: &[u8]) -> io::Result { - let Some(next) = self.bytes.len().checked_add(source.len()) else { - self.exceeded = true; - self.attempted = usize::MAX; - return Err(io::Error::other("staging record size overflow")); - }; - self.attempted = next; - if next > self.limit { - self.exceeded = true; - return Err(io::Error::other("staging record exceeds memory budget")); - } self.bytes - .try_reserve_exact(source.len()) - .map_err(|_| io::Error::other("staging record allocation failed"))?; + .try_reserve(source.len()) + .map_err(|_| io::Error::other("record allocation failed"))?; self.bytes.extend_from_slice(source); Ok(source.len()) } @@ -777,15 +608,6 @@ impl Write for FallibleBuffer { } } -fn memory_budget_error(phase: &str, limit: usize, accounted: usize) -> NativeError { - NativeError::MemoryBudgetExceeded(MemoryBudgetExceeded::new( - phase, - u64::try_from(limit).unwrap_or(u64::MAX), - u64::try_from(accounted).unwrap_or(u64::MAX), - 0, - )) -} - #[cfg(test)] mod tests { use super::*; @@ -918,7 +740,72 @@ mod tests { assert_eq!(run_file_count(&root), 0); assert_eq!(metrics.current_bytes(), 0); let (_, high_water) = metrics.snapshot(); - assert!(high_water <= chunk_limit.saturating_mul(2)); + assert!(high_water > 0); + assert!(high_water <= chunk_limit.saturating_mul(MAX_MERGE_FAN_IN)); + } + + #[test] + fn jumbo_records_merge_across_multiple_levels_without_a_budget() { + let root = temp_root("jumbo-merge"); + let metrics = SpillMetrics::default(); + let mut spool = + SortedSpool::::new(&root, "jumbo", 64, metrics.clone()).unwrap(); + let count = (MAX_MERGE_FAN_IN * MAX_MERGE_FAN_IN + 3) as u32; + for key in (0..count).rev() { + spool + .push(&TestRecord { + key, + payload: "jumbo".repeat(80), + }) + .unwrap(); + } + assert_eq!(run_file_count(&root), count as usize); + let mut stream = spool.finish().unwrap(); + assert!(run_file_count(&root) <= MAX_MERGE_FAN_IN); + for key in 0..count { + let record = stream.next().unwrap().unwrap(); + assert_eq!(record.key, key); + assert_eq!(record.payload, "jumbo".repeat(80)); + } + assert!(stream.next().unwrap().is_none()); + assert_eq!(run_file_count(&root), 0); + assert_eq!(metrics.current_bytes(), 0); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn mixed_small_and_jumbo_records_keep_sorted_values() { + let root = temp_root("mixed-jumbo"); + let mut spool = + SortedSpool::::new(&root, "mixed", 256, SpillMetrics::default()).unwrap(); + for key in (0..75).rev() { + spool + .push(&TestRecord { + key, + payload: if key % 3 == 0 { + "large".repeat(400) + } else { + "small".into() + }, + }) + .unwrap(); + } + let mut stream = spool.finish().unwrap(); + for key in 0..75 { + let row = stream.next().unwrap().unwrap(); + assert_eq!(row.key, key); + assert_eq!( + row.payload, + if key % 3 == 0 { + "large".repeat(400) + } else { + "small".into() + } + ); + } + assert!(stream.next().unwrap().is_none()); + assert_eq!(run_file_count(&root), 0); + fs::remove_dir_all(root).unwrap(); } #[test] diff --git a/src/staging_writer/tests.rs b/src/staging_writer/tests.rs index f1be2058..6d6e9493 100644 --- a/src/staging_writer/tests.rs +++ b/src/staging_writer/tests.rs @@ -309,6 +309,11 @@ fn small_budget_forces_spill_without_changing_deterministic_output() { assert_eq!(bounded_result.connector_rows, 800); assert!(bounded_result.spill_bytes > 0); assert!(bounded_result.high_water_bytes > 0); + for entry in fs::read_dir(&bounded_dir).unwrap().filter_map(Result::ok) { + if entry.file_type().is_ok_and(|kind| kind.is_file()) { + assert!(entry.metadata().unwrap().len() <= 8_192); + } + } assert_eq!( unique_statement_tables(&bounded_result.copy_statements), unique_statement_tables(&default_result.copy_statements) @@ -335,8 +340,6 @@ fn small_budget_forces_spill_without_changing_deterministic_output() { let database_path = bounded_dir.join("chunked-copy.lbdb"); write_database(LadybugWriteRequest { db_path: database_path.to_string_lossy().into_owned(), - worker_memory_bytes: 768 * 1024 * 1024, - buffer_pool_bytes: 384 * 1024 * 1024, max_num_threads: 1, defer_hash_indexes: false, include_fts: false, @@ -350,25 +353,21 @@ fn small_budget_forces_spill_without_changing_deterministic_output() { } #[test] -fn single_record_larger_than_budget_fails_structurally() { - let staging_dir = temp_staging_dir("record_budget"); +fn single_record_larger_than_chunk_target_roundtrips() { + let staging_dir = temp_staging_dir("record_target"); let graph = partition( "hash-1", - vec![node("file:one", "File", "file.py")], + vec![node("file:one", "File", &"label".repeat(128))], Vec::new(), ); let mut staging = StagingAccumulator::with_chunk_limit(&staging_dir.to_string_lossy(), 64).unwrap(); staging.add_partition(&graph); - - let error = staging.finish().unwrap_err(); - - let crate::error::NativeError::MemoryBudgetExceeded(error) = error else { - panic!("expected a structured memory budget error"); - }; - assert_eq!(error.phase, "staged"); - assert_eq!(error.limit_bytes, 64); - assert!(error.accounted_bytes > error.limit_bytes); + let result = staging.finish().unwrap(); + assert_eq!(result.node_rows, 1); + let rows = read_json_array(&staging_dir.join("file.json")); + assert_eq!(rows[0]["id"], "file:one"); + assert_eq!(rows[0]["label"], "label".repeat(128)); } fn partition( diff --git a/src/staging_writer/writer.rs b/src/staging_writer/writer.rs index 8701663c..f0f304c7 100644 --- a/src/staging_writer/writer.rs +++ b/src/staging_writer/writer.rs @@ -14,15 +14,12 @@ pub(crate) fn write_graph_rows( } else { request.schema_statements.clone() }; - let buffer_pool_bytes = request.database_buffer_pool_bytes()?; // Ladybug COPY uses additional native working memory per execution thread. // Materialization parallelism is bounded separately; keep the database phase - // single-threaded so its 256 MiB buffer pool remains the dominant allocation. + // single-threaded so the configured buffer pool remains the dominant allocation. let max_num_threads = 1; write_database_with_metrics(LadybugWriteRequest { db_path: request.db_path.clone(), - worker_memory_bytes: request.worker_memory_mib.saturating_mul(1024 * 1024), - buffer_pool_bytes, max_num_threads, defer_hash_indexes: true, include_fts: use_ladybug_fts, diff --git a/src/storage/containment.rs b/src/storage/containment.rs index 66d03c90..1ac954cb 100644 --- a/src/storage/containment.rs +++ b/src/storage/containment.rs @@ -37,7 +37,6 @@ pub(crate) struct ExitOutcome { pub code: Option, pub signal: Option, pub high_water_bytes: u64, - pub budget_reason: Option, pub cancelled: bool, pub containment_empty: bool, #[serde(default)] @@ -45,9 +44,6 @@ pub(crate) struct ExitOutcome { } impl fmt::Display for ExitOutcome { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(reason) = &self.budget_reason { - return write!(f, "{reason}"); - } if let Some(reason) = &self.supervision_error { return write!(f, "{reason}"); } @@ -68,6 +64,28 @@ impl fmt::Display for ExitOutcome { /// Missing registries are inactive. Existing unproven evidence is retained. /// Retirement is written only while holding an existing guardian lease. pub(crate) fn probe_registry(root: &Path) -> Result { + probe_registry_mode(root, LifecycleProbe::Retire) +} + +#[derive(Clone, Copy)] +enum LifecycleProbe { + Retire, + VerifyRetired, +} + +/// Final closure check under the actual workspace startup gate. Every marker +/// must already exist; this path never creates files or reacquires that gate. +pub(crate) fn ensure_retired_registry_empty(root: &Path) -> Result<(), String> { + match probe_registry_mode(root, LifecycleProbe::VerifyRetired)? { + true => Ok(()), + false => Err(format!( + "containment_unknown: registry {} is not retired and empty", + root.display() + )), + } +} + +fn probe_registry_mode(root: &Path, mode: LifecycleProbe) -> Result { let anchored; let root = if root.is_relative() { anchored = std::env::current_dir() @@ -90,7 +108,10 @@ pub(crate) fn probe_registry(root: &Path) -> Result { .and_then(|n| n.to_str()) .is_some_and(|n| n.starts_with(".lifecycle-")) { - return probe_lifecycle(root); + return match mode { + LifecycleProbe::Retire => probe_lifecycle(root), + LifecycleProbe::VerifyRetired => probe_lifecycle_mode(root, mode), + }; } for entry in fs::read_dir(root).map_err(|e| e.to_string())? { let entry = match entry { @@ -106,7 +127,7 @@ pub(crate) fn probe_registry(root: &Path) -> Result { if metadata.is_symlink() { return Ok(false); } - if metadata.is_dir() && !probe_registry(&entry.path())? { + if metadata.is_dir() && !probe_registry_mode(&entry.path(), mode)? { return Ok(false); } } @@ -126,16 +147,30 @@ pub(crate) fn ensure_registry_empty(root: &Path) -> Result<(), String> { } } pub(crate) fn probe_lifecycle(path: &Path) -> Result { + probe_lifecycle_mode(path, LifecycleProbe::Retire) +} +fn probe_lifecycle_mode(path: &Path, mode: LifecycleProbe) -> Result { if !real_directory(path) { return Ok(false); } + if matches!(mode, LifecycleProbe::VerifyRetired) + && !fs::symlink_metadata(path.join("retired")) + .is_ok_and(|m| m.is_file() && !m.file_type().is_symlink()) + { + return Ok(false); + } let lease = match OpenOptions::new() .read(true) .write(true) .open(path.join("lease")) { Ok(f) => f, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return retire_prelease_startup(path), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return match mode { + LifecycleProbe::Retire => retire_prelease_startup(path), + LifecycleProbe::VerifyRetired => Ok(false), + } + } Err(_) => return Ok(false), }; if lease.try_lock_exclusive().is_err() { @@ -170,9 +205,68 @@ pub(crate) fn probe_lifecycle(path: &Path) -> Result { return Ok(false); } } - retire_lifecycle(path)?; + if matches!(mode, LifecycleProbe::Retire) { + retire_lifecycle(path)?; + } Ok(true) } +/// Nonmutating proof check for maintenance holding the exact lifecycle lease. +/// Reuse the guardian's v2 receipt/FIFO/job policy; never authorize persisted PIDs. +#[cfg(test)] +pub(crate) fn lifecycle_empty_with_owned( + path: &Path, + lease: &super::locks::LockedFile, +) -> Result { + if !lease + .verifies(&path.join("lease"), super::locks::LockMode::Exclusive) + .map_err(|e| e.to_string())? + || !real_directory(path) + { + return Ok(false); + } + for name in ["ready.json", "proof.json", "receipt.json"] { + match fs::symlink_metadata(path.join(name)) { + Ok(metadata) => { + if metadata.file_type().is_symlink() || !metadata.is_file() { + return Ok(false); + } + let file = fs::File::open(path.join(name)).map_err(|error| error.to_string())?; + if super::locks::file_link_count(&file).map_err(|error| error.to_string())? != 1 { + return Ok(false); + } + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => (), + Err(e) => return Err(e.to_string()), + } + } + let bytes = match fs::read(path.join("ready.json")) { + Ok(bytes) => Some(bytes), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + match fs::read(path.join("proof.json")) { + Ok(bytes) => Some(bytes), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e.to_string()), + } + } + Err(e) => return Err(e.to_string()), + }; + let Some(bytes) = bytes else { + return Ok(prelease_directory_is_empty(path, true)); + }; + let descriptor: BuildDescriptorV2 = match serde_json::from_slice(&bytes) { + Ok(value) => value, + Err(_) => return Ok(false), + }; + if descriptor.version != 2 || !same_private_directory(&descriptor.lifecycle_dir, path) { + return Ok(false); + } + let receipt = fs::read(path.join("receipt.json")) + .ok() + .and_then(|bytes| serde_json::from_slice::(&bytes).ok()) + .is_some_and(|outcome| outcome.containment_empty); + Ok(receipt || probe_descriptor(&descriptor)?) +} + fn real_directory(path: &Path) -> bool { fs::symlink_metadata(path) .is_ok_and(|metadata| metadata.is_dir() && !metadata.file_type().is_symlink()) @@ -273,18 +367,23 @@ pub(crate) fn collect_registry(root: &Path) -> Result<(), String> { Ok(()) } pub(crate) fn retire_lifecycle(path: &Path) -> Result<(), String> { - match OpenOptions::new() - .create_new(true) - .write(true) - .open(path.join("retired")) - { - Ok(mut f) => { - f.write_all(b"retired-v2\n").map_err(|e| e.to_string())?; - f.sync_all().map_err(|e| e.to_string()) - } - Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()), - Err(e) => Err(e.to_string()), + let marker = path.join("retired"); + let fresh = match crate::process_lifecycle::FreshLifecycleFile::create(&marker) { + Ok(fresh) => fresh, + // Existing tombstones retain their historical no-op behavior and are + // never converted into creation evidence, including a racing creator. + Err(_) if fs::symlink_metadata(&marker).is_ok() => return Ok(()), + Err(error) => return Err(error), + }; + let mut output = + super::maintenance_controls::WorkspaceControlFile::from_created(fresh, &marker); + if let Some(tracked) = output.tracking() { + let _ = super::maintenance_origin::record_created_workspace_control(tracked); } + output + .write_all(b"retired-v2\n") + .map_err(|e| e.to_string())?; + output.finish_created().map_err(|e| e.to_string()) } #[cfg(unix)] pub(crate) fn probe_descriptor(d: &BuildDescriptorV2) -> Result { diff --git a/src/storage/direct.rs b/src/storage/direct.rs index 091a7e6b..838d94c7 100644 --- a/src/storage/direct.rs +++ b/src/storage/direct.rs @@ -1,6 +1,6 @@ use crate::error::NativeError; use crate::storage::atomic::{sync_dir, write_json_atomically}; -use crate::storage::containment::{collect_registry, ensure_registry_empty, probe_registry}; +use crate::storage::containment::{collect_registry, ensure_registry_empty}; use crate::storage::layout::{direct_bundle_paths, DirectLayout, DIRECT_DB_SIDECAR_SUFFIXES}; use crate::storage::locks::{open_locked, LockMode, WriterLease}; use serde::{Deserialize, Serialize}; @@ -42,9 +42,16 @@ pub(crate) struct DirectWriteSession { layout: DirectLayout, writer_lease: Option, finished: bool, + retirement_issue: Option, } impl DirectWriteSession { + pub(crate) fn writer_lease(&self) -> Result<&WriterLease, NativeError> { + self.writer_lease + .as_ref() + .ok_or_else(|| NativeError::InvalidInput("write session is finished".into())) + } + pub(crate) fn phase_workspace_root(&self) -> PathBuf { self.layout.phase_workspace_root() } @@ -66,9 +73,25 @@ impl DirectWriteSession { if result.is_ok() || self.layout.journal_path().exists() { self.finished = true; } + if result.is_ok() { + // Publication is already committed. Optional lifecycle attestation + // cannot turn that primary result into a failed publication. + self.retirement_issue = match self.writer_lease() { + Ok(writer) => super::maintenance_retirement::mark_explicit_direct_rebuild_active( + &self.layout, + writer, + ) + .err(), + Err(error) => Some(error), + }; + } result } + pub(crate) fn take_retirement_issue(&mut self) -> Option { + self.retirement_issue.take() + } + pub(crate) fn finish(mut self) { self.finished = true; drop(self.writer_lease.take()); @@ -97,8 +120,63 @@ impl DirectStore { } pub(crate) fn begin_write(&self) -> Result { + self.begin_write_with_rebuild(false) + } + pub(crate) fn begin_write_explicit_rebuild(&self) -> Result { + self.begin_write_with_rebuild(true) + } + fn begin_write_with_rebuild( + &self, + explicit_rebuild: bool, + ) -> Result { + let parent = self + .layout + .db_path() + .parent() + .ok_or_else(|| NativeError::InvalidInput("direct parent missing".into()))?; + let fresh = super::layout::create_fresh_reference_root(parent)?; self.ensure_parents()?; - let writer_lease = self.open_writer_lock()?; + let mut writer_lease = self.open_writer_lock()?; + if explicit_rebuild { + // Reactivation needs its own actual source/family/root exclusives; + // run it before retaining this session's ordinary Shared uses. + super::maintenance_legacy_guard::reactivate_legacy_source( + self.layout.db_path(), + self.layout.manifest_path(), + &writer_lease, + )?; + } + super::maintenance_ownership::register_direct_pair(&self.layout)?; + for resource in [self.layout.db_path(), self.layout.manifest_path()] { + writer_lease.retain( + super::maintenance_ownership::DatabaseUseLease::acquire_shared(resource)? + .into_lock()?, + ); + } + super::maintenance_ownership::ArtifactUseLease::ensure_root( + &self.layout.artifact_root_path(), + )?; + let domain = super::maintenance_ownership::ReferenceDomain::direct(&self.layout)?; + if let Some(fresh) = &fresh { + // A fresh DB parent cannot bless an existing external manifest. + if self + .layout + .manifest_path() + .parent() + .is_some_and(|manifest_parent| { + super::inventory::same_physical_path(parent, manifest_parent) + }) + { + super::maintenance_ownership::initialize_fresh_reference_domain( + &domain, + fresh, + &writer_lease, + )?; + } + } + writer_lease.retain( + super::maintenance_ownership::ArtifactUseLease::acquire_in_domain(&domain)?.into_lock(), + ); collect_registry(&self.layout.phase_workspace_root()).map_err(NativeError::InvalidInput)?; self.recover_with_lock()?; cleanup_candidate_set(&self.layout)?; @@ -106,27 +184,61 @@ impl DirectStore { layout: self.layout.clone(), writer_lease: Some(writer_lease), finished: false, + retirement_issue: None, }) } - pub(crate) fn begin_read(&self) -> Result { - let writer_lease = self.open_writer_lock()?; - if !matches!( - probe_registry(&self.layout.phase_workspace_root()), - Ok(true) + pub(crate) fn ensure_not_retired(&self) -> Result<(), NativeError> { + use super::maintenance_retirement::{observe_direct, DirectDisposition}; + if matches!( + observe_direct(&self.layout), + Ok(DirectDisposition::Retiring | DirectDisposition::Retired) ) { - if self.layout.journal_path().exists() { - return Err(NativeError::InvalidInput( - "containment_unknown: direct publish replay blocked by protected phase".into(), - )); + return Err(NativeError::InvalidInput( + "direct graph destination was retired; rebuild required with an explicit full materialization".into(), + )); + } + // Missing, unreadable or untrusted optional lifecycle metadata supplies + // no cleanup authority and must not block an otherwise committed read. + Ok(()) + } + + pub(crate) fn begin_read(&self) -> Result { + let mut writer_lease = self.open_writer_lock()?; + self.ensure_not_retired()?; + // A committed reader only needs its lifetime guards. Registration is + // producer overlap evidence; creating or repairing it is not required + // to serve an existing pair and cannot establish cleanup eligibility. + for resource in [self.layout.db_path(), self.layout.manifest_path()] { + writer_lease.retain( + super::maintenance_ownership::DatabaseUseLease::acquire_shared(resource)? + .into_lock()?, + ); + } + // No transaction journal means the committed pair needs no replay. + // Optional residue and phase inventories belong to the joined janitor, + // not this ordinary-read path. An uncertain journal remains required. + match fs::symlink_metadata(self.layout.journal_path()) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + return writer_lease.relock_shared(); } - return writer_lease.relock_shared(); + Err(error) => return Err(error.into()), + Ok(_) => {} } self.recover_with_lock()?; - cleanup_candidate_set(&self.layout)?; writer_lease.relock_shared() } + /// Noncreating residue diagnostic; uncertainty keeps optional cleanup pending. + pub(crate) fn candidate_cleanup_pending(&self) -> bool { + let mut paths = direct_bundle_paths(&self.layout.db_candidate_path()); + paths.push(self.layout.manifest_candidate_path()); + paths.iter().any(|path| { + !matches!(fs::symlink_metadata(path), + Err(error) if error.kind() == std::io::ErrorKind::NotFound) + }) + } + fn publish_candidate_with_lock( &self, request: &DirectPublishRequest, @@ -161,12 +273,16 @@ impl DirectStore { if !journal_path.exists() { return Ok(()); } + // Unlike an ordinary committed read, replay mutates the destination. + // Preserve strict producer registration before any recovery effects. + super::maintenance_ownership::register_direct_pair(&self.layout)?; + let domain = super::maintenance_ownership::ReferenceDomain::direct(&self.layout)?; + let _mutation = super::maintenance_ownership::ReferenceMutation::begin_in_domain(&domain)?; let journal = self.read_journal()?; self.validate_journal_destinations(&journal)?; self.validate_journal_sources(&journal)?; self.resume_publish(journal)?; self.remove_journal_and_shadows()?; - cleanup_candidate_set(&self.layout)?; Ok(()) } @@ -362,6 +478,16 @@ fn replace_with_shadow(from: &Path, to: &Path) -> Result<(), NativeError> { } fn cleanup_candidate_set(layout: &DirectLayout) -> Result<(), NativeError> { + let mut candidates = direct_bundle_paths(&layout.db_candidate_path()); + candidates.push(layout.manifest_candidate_path()); + if !candidates + .iter() + .any(|path| fs::symlink_metadata(path).is_ok()) + { + return Ok(()); + } + let domain = super::maintenance_ownership::ReferenceDomain::direct(layout)?; + let _mutation = super::maintenance_ownership::ReferenceMutation::begin_in_domain(&domain)?; ensure_registry_empty(&layout.phase_workspace_root()).map_err(NativeError::InvalidInput)?; for path in direct_bundle_paths(&layout.db_candidate_path()) { remove_if_safe_if_present(&path)?; diff --git a/src/storage/inventory.rs b/src/storage/inventory.rs new file mode 100644 index 00000000..885f8654 --- /dev/null +++ b/src/storage/inventory.rs @@ -0,0 +1,3203 @@ +//! Read-only, advisory observations. An inventory never grants deletion authority. +#[cfg(test)] +use super::layout::validate_generation_id; +use super::layout::{direct_bundle_paths, DirectLayout}; +#[cfg(test)] +use fs2::FileExt; +use serde::{Deserialize, Serialize}; +#[cfg(test)] +use std::collections::{BTreeMap, BTreeSet}; +#[cfg(test)] +use std::fs::File; +use std::fs::{self, Metadata}; +use std::path::{Component, Path, PathBuf}; +#[cfg(test)] +use std::time::{SystemTime, UNIX_EPOCH}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub(crate) struct MaintenanceTarget { + pub repository_root: PathBuf, + pub state_root: PathBuf, + pub storage_root: Option, + pub direct_pair: Option, + #[serde(default)] + pub config_path: Option, +} +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub(crate) struct DirectPair { + pub database: PathBuf, + pub manifest: PathBuf, +} +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +#[cfg(test)] +pub(crate) enum InventoryStatus { + Complete, + Partial, + Busy, + Missing, + Invalid, +} +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +#[cfg(test)] +pub(crate) enum InventoryCategory { + ActiveGraph, + ActiveCache, + LeasedGeneration, + LiveRun, + RecoveryEvidence, + ArtifactTemporary, + LegacyStaging, + RetiredDirect, + Coordination, + Unknown, +} +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +#[cfg(test)] +pub(crate) enum Disposition { + Retain, + PotentialReclamation, + Deferred, + Unknown, +} +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg(test)] +pub(crate) struct InventoryEntry { + pub path: PathBuf, + pub category: InventoryCategory, + pub disposition: Disposition, + pub logical_bytes: Option, + pub allocated_bytes: Option, + pub files: u64, + pub directories: u64, + pub reason: String, +} +/// Byte sums count a physical identity once, in deterministic path order. Null +/// totals mean at least one size was unavailable; known subtotals remain useful. +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] +#[cfg(test)] +pub(crate) struct InventoryTotals { + pub logical_bytes: Option, + pub allocated_bytes: Option, + pub known_logical_bytes: u64, + pub known_allocated_bytes: u64, + pub files: u64, + pub directories: u64, + pub duplicate_files: u64, + pub measurements_complete: bool, + pub observation_complete: bool, +} +#[derive(Debug, Serialize, Deserialize)] +#[cfg(test)] +pub(crate) struct StorageInventory { + pub observed_at_unix_ms: u64, + pub target: MaintenanceTarget, + pub active_generation: Option, + pub status: InventoryStatus, + pub entries: Vec, + pub totals: InventoryTotals, + pub issues: Vec, + #[serde(skip)] + _account: Option, +} +#[cfg(test)] +struct DetailedAccount { + _reservation: super::maintenance_budget::MemoryReservation, +} +#[cfg(test)] +impl std::fmt::Debug for DetailedAccount { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("DetailedAccount") + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg(test)] +struct Stamp { + len: u64, + modified: Option, + identity: Option<(u64, u64)>, + changed: Option<(i64, i64)>, + kind: u8, +} +#[cfg(test)] +fn stamp(m: &Metadata) -> Stamp { + Stamp { + len: m.len(), + modified: m.modified().ok(), + identity: identity(m), + changed: changed(m), + kind: if m.file_type().is_symlink() { + 2 + } else if m.is_dir() { + 1 + } else { + 0 + }, + } +} +#[cfg(unix)] +#[cfg(test)] +fn changed(m: &Metadata) -> Option<(i64, i64)> { + use std::os::unix::fs::MetadataExt; + Some((m.ctime(), m.ctime_nsec())) +} +#[cfg(not(unix))] +#[cfg(test)] +fn changed(_: &Metadata) -> Option<(i64, i64)> { + None +} +#[cfg(unix)] +fn identity(m: &Metadata) -> Option<(u64, u64)> { + use std::os::unix::fs::MetadataExt; + Some((m.dev(), m.ino())) +} +#[cfg(not(unix))] +fn identity(_: &Metadata) -> Option<(u64, u64)> { + None +} +#[cfg(unix)] +fn allocation(m: &Metadata) -> Option { + use std::os::unix::fs::MetadataExt; + m.blocks().checked_mul(512) +} +#[cfg(not(unix))] +fn allocation(_: &Metadata) -> Option { + None +} + +// Check every existing component: leaf checks alone allow symlinked ancestors. +pub(crate) fn real_path(path: &Path) -> bool { + real_path_with_metadata(path, |path| fs::symlink_metadata(path)) +} + +// Windows UNC prefixes require lookahead in the same iterator. +#[allow(clippy::while_let_on_iterator)] +fn real_path_with_metadata( + path: &Path, + mut metadata: impl FnMut(&Path) -> std::io::Result, +) -> bool { + if !path.is_absolute() { + return false; + } + let mut prefix = PathBuf::new(); + let mut components = path.components().peekable(); + while let Some(component) = components.next() { + match component { + Component::Prefix(value) => { + #[cfg(windows)] + let unc_root = match value.kind() { + std::path::Prefix::Disk(_) | std::path::Prefix::VerbatimDisk(_) => false, + std::path::Prefix::UNC(server, share) + | std::path::Prefix::VerbatimUNC(server, share) + if !server.is_empty() && !share.is_empty() => + { + true + } + _ => return false, + }; + #[cfg(not(windows))] + let _ = value; + // Preserve the original spelling. A disk prefix needs RootDir + // before it can be probed, whereas a complete verbatim UNC root + // can consist solely of Prefix when its trailing slash is absent. + prefix.push(component); + #[cfg(windows)] + if components.peek().is_none() { + return unc_root + && metadata(&prefix) + .is_ok_and(|m| m.is_dir() && !m.file_type().is_symlink()); + } + continue; + } + Component::CurDir => continue, + Component::ParentDir => { + if !metadata(&prefix).is_ok_and(|m| m.is_dir()) || !prefix.pop() { + return false; + } + } + _ => prefix.push(component), + } + match metadata(&prefix) { + Ok(m) if !m.file_type().is_symlink() => (), + _ => return false, + } + } + true +} +#[cfg(test)] +fn read_observed(path: &Path) -> Option { + if !real_path(path) { + return None; + } + let before = fs::symlink_metadata(path).ok()?; + // The detailed adapter is finite too; production byte observation below + // streams arbitrary manifests under the shared account. + if !before.is_file() || before.len() > 32 * 1024 { + return None; + } + let file = File::open(path).ok()?; + if stamp(&before) != stamp(&file.metadata().ok()?) { + return None; + } + let value = serde_json::from_reader(std::io::BufReader::new(&file)).ok()?; + if !real_path(path) || stamp(&before) != stamp(&fs::symlink_metadata(path).ok()?) { + return None; + } + Some(value) +} + +#[cfg(test)] +fn read_json(path: &Path) -> Option { + read_observed(path) +} +#[derive(Deserialize)] +#[cfg(test)] +struct ManifestKeyView { + files: BTreeMap, +} +#[derive(Deserialize)] +#[cfg(test)] +struct ArtifactKeyView { + #[serde(default)] + artifact_key: Option, +} +#[cfg(test)] +fn read_manifest_keys(path: &Path) -> Option> { + let manifest: ManifestKeyView = read_observed(path)?; + let mut keys = BTreeSet::new(); + for entry in manifest.files.into_values() { + if let Some(key) = entry.artifact_key { + if !valid_key(&key) { + return None; + } + keys.insert(key); + } + } + Some(keys) +} +#[derive(Debug, PartialEq)] +#[cfg(test)] +pub(crate) struct ObservedBinding { + pub(crate) storage: PathBuf, + pub(crate) current_direct: Option, + config: Option, +} +pub(crate) fn default_direct(target: &MaintenanceTarget) -> DirectPair { + DirectPair { + database: target.state_root.join(format!( + "{}_graph.ldb", + repository_name(&target.repository_root) + )), + manifest: target.state_root.join("manifest.json"), + } +} +pub(crate) fn same_physical_path(a: &Path, b: &Path) -> bool { + match (fs::symlink_metadata(a), fs::symlink_metadata(b)) { + (Ok(a), Ok(b)) if identity(&a).is_some() => identity(&a) == identity(&b), + _ => match (fs::canonicalize(a), fs::canonicalize(b)) { + (Ok(a), Ok(b)) => a == b, + _ => a == b, + }, + } +} +fn physically_within(path: &Path, root: &Path) -> bool { + path.ancestors() + .any(|ancestor| same_physical_path(ancestor, root)) +} +#[cfg(test)] +fn reconcile_observation_roots(mut roots: Vec) -> Vec { + roots.sort(); + let mut disjoint: Vec = Vec::new(); + for candidate in roots { + if disjoint + .iter() + .any(|root| physically_within(&candidate, root)) + { + continue; + } + disjoint.retain(|root| !physically_within(root, &candidate)); + disjoint.push(candidate); + } + disjoint.sort(); + disjoint +} +fn managed_direct_roles_overlap( + storage: &Path, + direct_paths: &[PathBuf], + artifacts: &Path, +) -> bool { + direct_paths + .iter() + .map(PathBuf::as_path) + .chain(std::iter::once(artifacts)) + .filter(|path| real_path(path)) + .any(|path| physically_within(path, storage) || physically_within(storage, path)) +} +#[cfg(test)] +pub(crate) fn bound_roles_overlap(target: &MaintenanceTarget, binding: &ObservedBinding) -> bool { + bound_roles_overlap_paths(target, &binding.storage, binding.current_direct.as_ref()) +} +pub(crate) fn bound_roles_overlap_paths( + target: &MaintenanceTarget, + storage: &Path, + selected: Option<&DirectPair>, +) -> bool { + let default = default_direct(target); + let direct = selected.unwrap_or(&default); + let mut paths = direct_bundle_paths(&direct.database); + paths.push(direct.manifest.clone()); + real_path(storage) + && managed_direct_roles_overlap( + storage, + &paths, + &DirectLayout::new(&direct.database, &direct.manifest).artifact_root_path(), + ) +} +#[cfg(test)] +fn observed_relative(path: &Path, root: &Path) -> Option { + if let Ok(relative) = path.strip_prefix(root) { + return Some(relative.to_owned()); + } + path.ancestors() + .find(|ancestor| same_physical_path(ancestor, root)) + .and_then(|ancestor| path.strip_prefix(ancestor).ok()) + .map(Path::to_owned) +} +#[cfg(test)] +fn config_raw_path( + config: &serde_json::Value, + key: &str, + config_path: &Path, +) -> Result, String> { + let Some(value) = config.get(key) else { + return Ok(None); + }; + if value.is_null() { + return Ok(None); + } + let value = value + .as_str() + .ok_or_else(|| format!("invalid_configuration_path_{key}"))?; + let path = Path::new(value); + let absolute = if path.is_absolute() { + path.to_owned() + } else { + config_path + .parent() + .ok_or("configuration_parent_missing")? + .join(path) + }; + Ok(Some(absolute)) +} +#[cfg(test)] +fn config_value_path( + config: &serde_json::Value, + key: &str, + config_path: &Path, +) -> Result, String> { + let Some(absolute) = config_raw_path(config, key, config_path)? else { + return Ok(None); + }; + // Validate the ORIGINAL traversal before using it for binding. Removing + // `hop/..` first could conceal a symlink or nonexistent component and select + // an unrelated lexical sibling. Preserve the validated spelling as well. + if !real_path(&absolute) { + return Err(format!("configuration_{key}_path_missing_or_symlinked")); + } + Ok(Some(absolute)) +} +/// Revalidate supplied selectors rather than treating struct construction or +/// deserialization as proof of association. Conventional state has one exact +/// location; a configured root must be named by a currently repository-bound +/// config. Explicit Direct selectors preserve their raw lock/journal spelling. +#[cfg(test)] +pub(crate) fn observe_binding(target: &MaintenanceTarget) -> Result { + let conventional = target.repository_root.join(".codebaseGraph"); + if !target.repository_root.is_absolute() + || !real_path(&target.repository_root) + || target.state_root != conventional + { + return Err("invalid_repository_or_state_identity".into()); + } + let default_config = conventional.join("config.json"); + let config_path = target.config_path.as_ref().unwrap_or(&default_config); + let config = match fs::symlink_metadata(config_path) { + Ok(_) => Some(read_json(config_path).ok_or("configuration_unreadable_or_invalid")?), + Err(e) if e.kind() == std::io::ErrorKind::NotFound && target.config_path.is_none() => None, + _ => return Err("configuration_unreadable_or_invalid".into()), + }; + let mut managed = false; + let mut configured_storage = None; + let default = default_direct(target); + let mut configured_direct = default.clone(); + if let Some(config) = &config { + if !config.is_object() { + return Err("invalid_configuration_object".into()); + } + let repository = config_value_path(config, "repo_root", config_path)?; + if let Some(repository) = repository { + if !real_path(&repository) || !same_physical_path(&repository, &target.repository_root) + { + return Err("configuration_repository_binding_mismatch".into()); + } + } else if config_path != &default_config { + return Err("external_configuration_repository_binding_required".into()); + } + match config.get("schema_version").filter(|v| !v.is_null()) { + Some(version) if matches!(version.as_u64(), Some(2 | 3)) => managed = true, + Some(version) if version.as_u64() == Some(1) => (), + None => (), + _ => return Err("unsupported_configuration_schema".into()), + } + if managed { + configured_storage = config_value_path(config, "storage_root", config_path)?; + } + configured_direct.database = config_raw_path(config, "database_path", config_path)? + .unwrap_or(default.database.clone()); + configured_direct.manifest = config_raw_path(config, "manifest_path", config_path)? + .unwrap_or(default.manifest.clone()); + } + let default_storage = conventional.join("storage"); + let expected_storage = configured_storage.clone().unwrap_or(default_storage); + let storage = target + .storage_root + .clone() + .unwrap_or_else(|| expected_storage.clone()); + if physically_within(&target.repository_root, &storage) + || physically_within(&conventional, &storage) + { + return Err("storage_root_overlaps_repository_or_state_ancestor".into()); + } + if storage != expected_storage + && !(real_path(&expected_storage) + && real_path(&storage) + && same_physical_path(&expected_storage, &storage)) + { + return Err("managed_storage_repository_binding_required".into()); + } + // A caller may select a repository-local pair. External pair observation + // requires the repository-bound config to register those exact destinations. + let current_direct = target + .direct_pair + .clone() + .or_else(|| (!managed).then_some(configured_direct.clone())); + if let Some(pair) = ¤t_direct { + let local = pair.database.starts_with(&conventional) + && pair.manifest.starts_with(&conventional) + && !pair + .database + .components() + .chain(pair.manifest.components()) + .any(|c| matches!(c, Component::ParentDir)); + if !local && (config.is_none() || pair != &configured_direct) { + return Err("direct_destination_repository_binding_required".into()); + } + if pair.database == pair.manifest { + return Err("ambiguous_direct_pair_retained".into()); + } + } + Ok(ObservedBinding { + storage, + current_direct, + config, + }) +} +#[cfg(test)] +fn classify_artifact( + path: &Path, + root: &Path, + keys: &BTreeSet, +) -> (InventoryCategory, Disposition, &'static str) { + let parts: Vec<_> = path + .strip_prefix(root) + .unwrap_or(path) + .iter() + .map(|p| p.to_string_lossy()) + .collect(); + if parts + .iter() + .any(|p| p.starts_with(".tmp-") || p.starts_with(".replaced-")) + { + return ( + InventoryCategory::ArtifactTemporary, + Disposition::Deferred, + "artifact_producer_or_replacement_recovery_required", + ); + } + if parts.len() >= 2 && valid_key(&parts[1]) && parts[0] == parts[1][..2] { + return if keys.contains(parts[1].as_ref()) { + ( + InventoryCategory::ActiveCache, + Disposition::Retain, + "current_direct_manifest_artifact_key", + ) + } else { + ( + InventoryCategory::ActiveCache, + Disposition::Deferred, + "direct_key_execution_and_recovery_authority_required", + ) + }; + } + ( + InventoryCategory::Unknown, + Disposition::Unknown, + "unrecognized_direct_cache_state_retained", + ) +} + +#[cfg(test)] +struct Scan { + entries: Vec, + stamps: BTreeMap, + duplicates: u64, + issues: Vec, + retained_bytes: usize, + limited: bool, + memory: super::maintenance_budget::CleanupMemory, +} +#[cfg(test)] +impl Scan { + fn new(memory: &super::maintenance_budget::CleanupMemory) -> Self { + Self { + entries: Vec::new(), + stamps: BTreeMap::new(), + duplicates: 0, + issues: Vec::new(), + retained_bytes: 0, + limited: false, + memory: memory.clone(), + } + } + fn walk(&mut self, path: &Path) { + let memory = self.memory.clone(); + let result = physical::path(path, &memory, &mut |entry| { + let bytes = entry + .path + .as_os_str() + .len() + .saturating_mul(3) + .saturating_add(1024); + let next = self.retained_bytes.checked_add(bytes); + if self.entries.len() >= 4096 || next.is_none_or(|n| n > 2 * 1024 * 1024) { + return Err(physical::Problem::Limit); + } + self.retained_bytes = next.unwrap(); + let Some(metadata) = entry.metadata else { + self.issues + .push(format!("unmeasurable {}", entry.path.display())); + self.entries + .push(unknown(entry.path, "metadata_unavailable")); + return Ok(false); + }; + let mut observed = stamp(metadata); + if let Some(identity) = entry.identity { + observed.identity = Some(identity.fixed_words()); + } + self.stamps.insert(entry.path.to_owned(), observed); + if (!metadata.is_file() && !metadata.is_dir()) + || (metadata.is_dir() && entry.identity.is_none()) + { + self.issues + .push(format!("untraversed {}", entry.path.display())); + self.entries + .push(unknown(entry.path, "symlink_or_special_file_retained")); + return Ok(false); + } + self.entries.push(InventoryEntry { + path: entry.path.to_owned(), + category: InventoryCategory::Unknown, + disposition: Disposition::Unknown, + logical_bytes: Some(if metadata.is_dir() { 0 } else { metadata.len() }), + allocated_bytes: allocation(metadata), + files: u64::from(metadata.is_file()), + directories: u64::from(metadata.is_dir()), + reason: "unrecognized_state_retained".into(), + }); + Ok(true) + }); + if let Err(problem) = result { + match problem { + physical::Problem::Limit => { + self.limited = true; + self.issues.push("inventory_resource_limit_retained".into()); + } + physical::Problem::Cancelled => { + self.limited = true; + self.issues.push("inventory_cancelled_retained".into()); + } + physical::Problem::Io(kind) => self + .issues + .push(format!("unreadable_directory {}: {kind:?}", path.display())), + } + if let Some(entry) = self.entries.iter_mut().find(|entry| entry.path == path) { + entry.logical_bytes = None; + entry.allocated_bytes = None; + } + } + } + fn finish(&mut self) { + self.entries.sort_by(|a, b| a.path.cmp(&b.path)); + let mut seen = BTreeSet::new(); + for entry in &mut self.entries { + if entry.files != 0 + && self + .stamps + .get(&entry.path) + .and_then(|stamp| stamp.identity) + .is_some_and(|identity| !seen.insert(identity)) + { + self.duplicates += 1; + entry.logical_bytes = Some(0); + entry.allocated_bytes = Some(0); + entry.files = 0; + entry.reason = "physical_identity_counted_at_first_path".into(); + } + } + } +} + +#[cfg(test)] +fn unknown(path: &Path, reason: &str) -> InventoryEntry { + InventoryEntry { + path: path.to_owned(), + category: InventoryCategory::Unknown, + disposition: Disposition::Unknown, + logical_bytes: None, + allocated_bytes: None, + files: 0, + directories: 0, + reason: reason.into(), + } +} + +/// Observe only supplied repository state. All locks are opened without create; +/// plain File drops only release the observation locks, never run recovery. +#[cfg(test)] +pub(crate) fn inspect_storage(target: &MaintenanceTarget) -> StorageInventory { + inspect_with_hook(target, || {}) +} +#[cfg(test)] +fn inspect_with_hook(target: &MaintenanceTarget, between_scans: impl FnOnce()) -> StorageInventory { + let memory = super::maintenance_budget::CleanupMemory::new(8 * 1024 * 1024).ok(); + let account = memory + .as_ref() + .and_then(|memory| memory.reserve(6 * 1024 * 1024).ok()) + .map(|reservation| DetailedAccount { + _reservation: reservation, + }); + let mut result = StorageInventory { + observed_at_unix_ms: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() + .try_into() + .unwrap_or(u64::MAX), + target: target.clone(), + active_generation: None, + status: InventoryStatus::Partial, + entries: Vec::new(), + totals: InventoryTotals::default(), + issues: Vec::new(), + _account: account, + }; + let Some(memory) = memory.filter(|_| result._account.is_some()) else { + result + .issues + .push("inventory_resource_limit_retained".into()); + return result; + }; + let binding = match observe_binding(target) { + Ok(binding) => binding, + Err(reason) => { + result.status = InventoryStatus::Invalid; + result.issues.push(reason); + return result; + } + }; + let storage = binding.storage.clone(); + let mut roots = Vec::new(); + match fs::symlink_metadata(&target.state_root) { + Ok(m) if m.is_dir() && real_path(&target.state_root) => { + roots.push(target.state_root.clone()) + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + result.issues.push("state_root_missing".into()) + } + _ => result.issues.push("invalid_state_root_retained".into()), + } + let valid_storage = + real_path(&storage) && fs::symlink_metadata(&storage).is_ok_and(|m| m.is_dir()); + if valid_storage { + if physically_within(&target.repository_root, &storage) + || physically_within(&target.state_root, &storage) + { + result.status = InventoryStatus::Invalid; + result + .issues + .push("storage_root_overlaps_repository_or_state_ancestor".into()); + return result; + } + if !roots.iter().any(|root| physically_within(&storage, root)) { + roots.push(storage.clone()); + } + } + // Direct scope contains only exact registered files/cache, never its parent. + let default = default_direct(target); + let direct = binding.current_direct.as_ref().unwrap_or(&default); + let layout = DirectLayout::new(&direct.database, &direct.manifest); + let mut direct_paths = direct_bundle_paths(&direct.database); + direct_paths.push(direct.manifest.clone()); + let direct_artifacts = layout.artifact_root_path(); + let mut recognized_direct = real_path(&direct.database) + && real_path(&direct.manifest) + && fs::symlink_metadata(&direct.database).is_ok_and(|m| m.is_file()) + && fs::symlink_metadata(&direct.manifest).is_ok_and(|m| m.is_file()); + if direct_paths.iter().any(|p| { + fs::symlink_metadata(p) + .is_ok_and(|m| m.file_type().is_symlink() || ambiguous_file_identity(&m)) + }) { + recognized_direct = false; + result + .issues + .push("ambiguous_direct_physical_identity_retained".into()); + } + if binding.current_direct.is_some() { + for path in direct_paths + .iter() + .chain(std::iter::once(&direct_artifacts)) + { + if real_path(path) + && fs::symlink_metadata(path).is_ok_and(|m| { + if path == &direct_artifacts { + m.is_dir() + } else { + m.is_file() + } + }) + && !roots.iter().any(|root| physically_within(path, root)) + { + roots.push(path.clone()); + } + } + } + if bound_roles_overlap(target, &binding) { + result.status = InventoryStatus::Invalid; + result + .issues + .push("managed_direct_storage_role_overlap".into()); + return result; + } + let roots = reconcile_observation_roots(roots); + if roots.is_empty() { + result.status = InventoryStatus::Missing; + result.issues.push("bound_storage_missing".into()); + return result; + } + let mut locks = Vec::new(); + let mut busy = false; + if valid_storage { + for name in ["writer.lock", "state.lock"] { + let path = storage.join(name); + match existing_shared_lock(&path) { + Ok(file) => { + // Reader state sections remain short. Scans below recheck + // the pointer/tree and hold only non-reader-facing guards. + if name == "writer.lock" { + locks.push(file); + } + } + Err(e) => { + busy |= super::locks::is_lock_contended(&e); + result.issues.push(format!( + "observation_lock_unavailable {}: {e}", + path.display() + )); + } + } + } + } + let artifact_roots = if binding.current_direct.is_some() { + vec![direct_artifacts.clone()] + } else { + vec![storage.join("artifacts")] + }; + let mut artifact_observations = Vec::new(); + for root in artifact_roots { + match super::maintenance_ownership::observe_artifact_cohort(&root) { + Ok(Some(guard)) => artifact_observations.push(guard), + _ => result.issues.push("artifact_root_cohort_unverified".into()), + } + } + let pointer = if valid_storage { + read_json(&storage.join("active.json")) + } else { + None + }; + result.active_generation = pointer + .as_ref() + .filter(|v| v.get("schema_version").and_then(|v| v.as_u64()) == Some(2)) + .and_then(|v| v.get("generation_id")) + .and_then(|v| v.as_str()) + .filter(|s| validate_generation_id(s).is_ok()) + .map(str::to_owned); + let mut keys = BTreeSet::new(); + if let Some(id) = &result.active_generation { + let manifest = storage + .join("generations") + .join(format!("gen-{id}")) + .join("manifest.json"); + match read_manifest_keys(&manifest) { + Some(manifest_keys) => keys = manifest_keys, + None => result + .issues + .push("active_manifest_unreadable_or_invalid".into()), + } + } else if binding.current_direct.is_none() { + result + .issues + .push("active_pointer_missing_or_invalid".into()); + } + let direct_keys = if binding.current_direct.is_some() && recognized_direct { + read_manifest_keys(&direct.manifest) + } else { + None + }; + if binding.current_direct.is_some() && direct_keys.is_none() { + result + .issues + .push("current_direct_manifest_unreadable_or_invalid".into()); + } + direct_paths.push(direct_artifacts.clone()); + let mut scan = Scan::new(&memory); + for root in &roots { + scan.walk(root); + } + scan.finish(); + let mut lease_observations = BTreeMap::new(); + for entry in &mut scan.entries { + if entry.logical_bytes.is_none() { + continue; + } + let (category, disposition, reason) = classify( + &entry.path, + &storage, + result.active_generation.as_deref(), + &keys, + &target.state_root, + recognized_direct && binding.current_direct.is_none(), + &direct_paths, + ); + if entry.reason != "physical_identity_counted_at_first_path" { + entry.reason = reason.into(); + } + entry.category = category; + entry.disposition = disposition; + if binding.current_direct.is_some() && recognized_direct { + if entry.path.starts_with(&direct_artifacts) { + let (category, disposition, reason) = classify_artifact( + &entry.path, + &direct_artifacts, + direct_keys.as_ref().unwrap_or(&BTreeSet::new()), + ); + entry.category = category; + entry.disposition = disposition; + entry.reason = reason.into(); + } else if direct_paths + .iter() + .filter(|p| *p != &direct_artifacts) + .any(|p| entry.path == *p) + { + entry.category = InventoryCategory::ActiveGraph; + entry.disposition = Disposition::Retain; + entry.reason = "current_selected_direct_bundle".into(); + } + } + if matches!( + category, + InventoryCategory::LeasedGeneration | InventoryCategory::LiveRun + ) { + if let Some(relative) = observed_relative(&entry.path, &storage) { + let components: Vec<_> = relative.iter().collect(); + if components.len() >= 2 { + let lease_path = storage + .join(components[0]) + .join(components[1]) + .join("lease.lock"); + entry.reason = match lease_observations + .entry(lease_path.clone()) + .or_insert_with(|| lease_observation(&lease_path)) + { + LeaseObservation::InUse => { + entry.disposition = Disposition::Retain; + "existing_lease_in_use_retained" + } + LeaseObservation::Available => { + "lease_available_execution_and_recovery_authority_still_required" + } + LeaseObservation::Unknown => { + "lease_missing_or_unverifiable_execution_may_survive" + } + } + .into(); + } + } + } + } + between_scans(); + let mut after = Scan::new(&memory); + for root in &roots { + after.walk(root); + } + after.finish(); + if scan.stamps != after.stamps + || observe_binding(target).ok().as_ref() != Some(&binding) + || direct_keys + != (if binding.current_direct.is_some() && recognized_direct { + read_manifest_keys(&direct.manifest) + } else { + None + }) + || pointer + != (if valid_storage { + read_json(&storage.join("active.json")) + } else { + None + }) + { + result + .issues + .push("storage_changed_during_observation".into()); + } + result.issues.extend(scan.issues); + result.issues.extend(after.issues); + result.entries = scan.entries; + let mut totals = InventoryTotals { + logical_bytes: Some(0), + allocated_bytes: Some(0), + duplicate_files: scan.duplicates, + measurements_complete: true, + ..Default::default() + }; + for entry in &result.entries { + totals.files = totals.files.saturating_add(entry.files); + totals.directories = totals.directories.saturating_add(entry.directories); + totals.logical_bytes = totals + .logical_bytes + .zip(entry.logical_bytes) + .and_then(|(a, b)| a.checked_add(b)); + totals.allocated_bytes = totals + .allocated_bytes + .zip(entry.allocated_bytes) + .and_then(|(a, b)| a.checked_add(b)); + totals.known_logical_bytes = totals + .known_logical_bytes + .saturating_add(entry.logical_bytes.unwrap_or(0)); + totals.known_allocated_bytes = totals + .known_allocated_bytes + .saturating_add(entry.allocated_bytes.unwrap_or(0)); + } + totals.measurements_complete = totals.logical_bytes.is_some() + && totals.allocated_bytes.is_some() + && identity_supported() + && result.issues.is_empty(); + // Artifact exclusion alone cannot establish coherence of native helpers, + // startup ownership and recovery state. Keep this distinction explicit. + result + .issues + .push("worker_and_recovery_coherence_unverified".into()); + totals.observation_complete = false; + if scan.limited || after.limited { + totals.logical_bytes = None; + totals.allocated_bytes = None; + totals.measurements_complete = false; + } + result.totals = totals; + result.status = if busy { + InventoryStatus::Busy + } else { + InventoryStatus::Partial + }; + result.issues.sort(); + result.issues.dedup(); + drop(locks); + result +} +#[cfg(test)] +fn identity_supported() -> bool { + cfg!(unix) +} +#[cfg(test)] +enum LeaseObservation { + InUse, + Available, + Unknown, +} +#[cfg(test)] +fn lease_observation(path: &Path) -> LeaseObservation { + if !real_path(path) { + return LeaseObservation::Unknown; + } + let Ok(before) = fs::symlink_metadata(path) else { + return LeaseObservation::Unknown; + }; + if !before.is_file() { + return LeaseObservation::Unknown; + } + let Ok(file) = File::open(path) else { + return LeaseObservation::Unknown; + }; + if file.metadata().ok().as_ref().map(stamp) != Some(stamp(&before)) { + return LeaseObservation::Unknown; + } + match FileExt::try_lock_exclusive(&file) { + Ok(()) => { + if !real_path(path) + || fs::symlink_metadata(path).ok().as_ref().map(stamp) != Some(stamp(&before)) + { + LeaseObservation::Unknown + } else { + LeaseObservation::Available + } + } + Err(e) + if e.kind() == std::io::ErrorKind::WouldBlock + || e.raw_os_error() == fs2::lock_contended_error().raw_os_error() => + { + LeaseObservation::InUse + } + Err(_) => LeaseObservation::Unknown, + } +} +#[cfg(test)] +fn existing_shared_lock(path: &Path) -> std::io::Result { + if !real_path(path) { + return Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + "missing_or_symlinked_lock", + )); + } + let before = fs::symlink_metadata(path)?; + if !before.is_file() { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "non_file_lock", + )); + } + let file = File::open(path)?; + FileExt::try_lock_shared(&file)?; + if stamp(&before) != stamp(&file.metadata()?) + || !real_path(path) + || stamp(&before) != stamp(&fs::symlink_metadata(path)?) + { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "lock_identity_changed", + )); + } + Ok(file) +} +#[cfg(unix)] +#[cfg(test)] +fn ambiguous_file_identity(metadata: &Metadata) -> bool { + use std::os::unix::fs::MetadataExt; + metadata.is_file() && metadata.nlink() != 1 +} +#[cfg(not(unix))] +#[cfg(test)] +fn ambiguous_file_identity(_: &Metadata) -> bool { + false +} +fn repository_name(root: &Path) -> String { + let normalized: String = root + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("repository") + .chars() + .map(|c| { + if c.is_alphanumeric() || c == '-' || c == '_' { + c + } else { + '_' + } + }) + .collect(); + let trimmed = normalized.trim_matches(['.', '_', '-']); + if trimmed.is_empty() { + "repository".into() + } else { + trimmed.into() + } +} +#[cfg(test)] +fn valid_key(key: &str) -> bool { + key.len() == 64 + && key + .bytes() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)) +} +#[cfg(test)] +fn classify<'a>( + path: &Path, + storage: &Path, + active: Option<&str>, + keys: &BTreeSet, + state: &Path, + direct: bool, + direct_paths: &[PathBuf], +) -> (InventoryCategory, Disposition, &'a str) { + use Disposition::*; + use InventoryCategory::*; + if path.starts_with(state.join("native-staging")) { + return (LegacyStaging, Deferred, "legacy_quiescence_required"); + } + if direct + && direct_paths + .iter() + .any(|p| path == p || path.starts_with(p)) + { + return ( + RetiredDirect, + Deferred, + "direct_destination_ownership_and_legacy_quiescence_required", + ); + } + if let Some(relative) = observed_relative(path, storage) { + let parts: Vec<_> = relative.iter().map(|p| p.to_string_lossy()).collect(); + match parts.first().map(|s| s.as_ref()) { + Some("generations") if parts.len() >= 2 => { + if active.is_some_and(|id| parts[1] == format!("gen-{id}")) { + return (ActiveGraph, Retain, "active_generation"); + } + return ( + LeasedGeneration, + Deferred, + "generation_lease_and_recovery_authority_required", + ); + } + Some("runs") if parts.len() >= 2 => { + return (LiveRun, Deferred, "run_execution_or_recovery_may_be_live") + } + Some("phases") => return (RecoveryEvidence, Retain, "phase_containment_evidence"), + Some("artifacts") if parts.len() >= 2 => { + if parts + .iter() + .skip(1) + .any(|s| s.starts_with(".tmp-") || s.starts_with(".replaced-")) + { + return ( + ArtifactTemporary, + Deferred, + "artifact_producer_or_replacement_recovery_required", + ); + } + if parts.len() >= 3 && valid_key(&parts[2]) && parts[1] == parts[2][..2] { + if keys.contains(parts[2].as_ref()) { + return (ActiveCache, Retain, "active_manifest_artifact_key"); + } + return ( + ActiveCache, + Deferred, + "unreferenced_key_run_recovery_and_root_ownership_required", + ); + } + } + Some(name) + if parts.len() == 1 + && (name.ends_with(".lock") + || matches!(name, "active.json" | "coordinator.json")) => + { + return (Coordination, Retain, "stable_coordination_metadata") + } + Some("worker.json") => { + return (RecoveryEvidence, Retain, "worker_containment_evidence") + } + _ => (), + } + } + if path.file_name().is_some_and(|n| { + let n = n.to_string_lossy(); + n.contains("journal") + || n.contains("cleanup-barrier") + || n.starts_with(".direct-worker") + || n.starts_with(".direct-workers") + }) { + return (RecoveryEvidence, Retain, "unresolved_recovery_evidence"); + } + if path.extension().is_some_and(|e| e == "lock") { + return (Coordination, Retain, "stable_lock_inode"); + } + ( + InventoryCategory::Unknown, + Disposition::Unknown, + "unrecognized_state_retained", + ) +} + +/// The common noncreating physical visitor. It retains no entries or paths +/// beyond its charged path/scratch buffers, and never follows a symlink. +mod physical { + use super::super::locks::{file_identity, path_identity, FileIdentity, RootedDirectory}; + use super::super::maintenance_budget::{CleanupMemory, ReservedBytes, ResourceLimit}; + use std::fs::{self, Metadata}; + use std::io; + use std::path::Path; + pub(super) const MAX_DEPTH: usize = 64; + const PATH_BYTES: usize = 65536; + #[derive(Clone, Copy)] + pub(super) enum Problem { + Io(io::ErrorKind), + Limit, + Cancelled, + } + impl From for Problem { + fn from(error: io::Error) -> Self { + Self::Io(error.kind()) + } + } + impl From for Problem { + fn from(_: ResourceLimit) -> Self { + Self::Limit + } + } + pub(super) struct Entry<'a> { + #[cfg(test)] + pub path: &'a Path, + pub name: &'a [u8], + pub depth: usize, + pub metadata: Option<&'a Metadata>, + pub identity: Option, + } + struct Visitor<'a, 'b> { + memory: &'a CleanupMemory, + cancel: &'a dyn Fn() -> bool, + path: ReservedBytes, + len: usize, + emit: &'b mut dyn FnMut(Entry<'_>) -> Result, + } + impl Visitor<'_, '_> { + fn path(&self) -> &Path { + // SAFETY: roots and names originate in OsStr::as_encoded_bytes on + // this platform. Separators preserve their encoding boundaries. + Path::new(unsafe { + std::ffi::OsStr::from_encoded_bytes_unchecked(&self.path.as_slice()[..self.len]) + }) + } + fn push(&mut self, name: &[u8]) -> Result { + let before = self.len; + let separator = usize::from( + !self.path.as_slice()[..before] + .last() + .is_some_and(|byte| std::path::is_separator(char::from(*byte))), + ); + let end = before + .checked_add(separator) + .and_then(|n| n.checked_add(name.len())) + .ok_or(Problem::Limit)?; + if end > self.path.as_slice().len() { + return Err(Problem::Limit); + } + if separator != 0 { + self.path.as_mut_slice()[before] = std::path::MAIN_SEPARATOR as u8; + } + self.path.as_mut_slice()[before + separator..end].copy_from_slice(name); + self.len = end; + Ok(before) + } + fn emit( + &mut self, + name: &[u8], + depth: usize, + metadata: Option<&Metadata>, + identity: Option, + ) -> Result { + // The detailed reference adapter borrows the same physical path; + // shipping measurement consumes only fixed per-entry facts. + (self.emit)(Entry { + #[cfg(test)] + path: Path::new(unsafe { + std::ffi::OsStr::from_encoded_bytes_unchecked(&self.path.as_slice()[..self.len]) + }), + name, + depth, + metadata, + identity, + }) + } + fn unknown(&mut self, name: &[u8], depth: usize) -> Result<(), Problem> { + let metadata = fs::symlink_metadata(self.path()).ok(); + self.emit(name, depth, metadata.as_ref(), None)?; + Ok(()) + } + fn directory( + &mut self, + directory: RootedDirectory, + name: &[u8], + depth: usize, + ) -> Result<(), Problem> { + if (self.cancel)() { + return Err(Problem::Cancelled); + } + if depth >= MAX_DEPTH { + return Err(Problem::Limit); + } + let metadata = directory.metadata()?; + if !self.emit(name, depth, Some(&metadata), Some(directory.identity()))? { + return Ok(()); + } + let mut scratch = self.memory.reserve(4096)?.bytes(4096)?; + let scratch: &mut [u8; 4096] = scratch + .as_mut_slice() + .try_into() + .expect("fixed directory scratch"); + let mut failure = None; + let result = directory.visit_entries(scratch, &mut |name| { + let result = (|| -> Result<(), Problem> { + if (self.cancel)() { + return Err(Problem::Cancelled); + } + let before = self.push(name)?; + let result = match directory.open_directory_readonly(name) { + Ok(child) => self.directory(child, name, depth + 1), + Err(_) => match directory.open_readonly_file(name) { + Ok(file) => { + let metadata = file.metadata()?; + let identity = file_identity(&file).ok(); + self.emit(name, depth + 1, Some(&metadata), identity) + .map(|_| ()) + } + Err(_) => self.unknown(name, depth + 1), + }, + }; + self.len = before; + result + })(); + if let Err(error) = result { + failure = Some(error); + return Err(io::ErrorKind::Interrupted.into()); + } + Ok(()) + }); + if let Some(error) = failure { + return Err(error); + } + result.map_err(Problem::from) + } + } + pub(super) fn rooted( + path: &Path, + directory: RootedDirectory, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + emit: &mut dyn FnMut(Entry<'_>) -> Result, + ) -> Result<(), Problem> { + let mut bytes = memory.reserve(PATH_BYTES)?.bytes(PATH_BYTES)?; + let root = path.as_os_str().as_encoded_bytes(); + if root.len() > PATH_BYTES { + return Err(Problem::Limit); + } + bytes.as_mut_slice()[..root.len()].copy_from_slice(root); + let mut visitor = Visitor { + memory, + cancel, + path: bytes, + len: root.len(), + emit, + }; + visitor.directory(directory, b"", 0) + } + pub(super) fn path( + path: &Path, + memory: &CleanupMemory, + emit: &mut dyn FnMut(Entry<'_>) -> Result, + ) -> Result<(), Problem> { + let metadata = fs::symlink_metadata(path).ok(); + if !super::real_path(path) + || metadata + .as_ref() + .is_none_or(|m| !m.is_dir() && !m.is_file()) + { + emit(Entry { + #[cfg(test)] + path, + name: b"", + depth: 0, + metadata: metadata.as_ref(), + identity: None, + })?; + return Ok(()); + } + if metadata.as_ref().is_some_and(Metadata::is_dir) { + let identity = + path_identity(path).map_err(|_| Problem::Io(io::ErrorKind::InvalidData))?; + rooted( + path, + RootedDirectory::open_readonly_root(path, identity)?, + memory, + &|| false, + emit, + ) + } else { + let opened = path.parent().and_then(|parent| { + let root = RootedDirectory::open_readonly_root(parent, path_identity(parent).ok()?) + .ok()?; + let file = root + .open_readonly_file(path.file_name()?.as_encoded_bytes()) + .ok()?; + Some((file_identity(&file).ok()?, file.metadata().ok()?)) + }); + let (identity, actual) = match opened { + Some((identity, metadata)) => (Some(identity), Some(metadata)), + None => (None, metadata), + }; + emit(Entry { + #[cfg(test)] + path, + name: path + .file_name() + .map_or(b"".as_slice(), |n| n.as_encoded_bytes()), + depth: 0, + metadata: actual.as_ref(), + identity, + })?; + if identity.is_some() && path_identity(path).ok() != identity { + return Err(Problem::Io(io::ErrorKind::InvalidData)); + } + Ok(()) + } + } +} + +/// Bounded advisory measurements consume the same physical entry visitor as +/// detailed inventory, while retaining only fixed aggregate counters. +pub(crate) mod measurement { + use super::super::locks::{path_identity, RootedDirectory}; + use super::super::maintenance_budget::ReservedBytes; + use super::super::maintenance_budget::{CleanupMemory, ResourceLimit}; + use super::super::maintenance_json::{MaintenanceJsonReader, TokenKind}; + use super::MaintenanceTarget; + use sha2::{Digest, Sha256}; + use std::fs::{File, Metadata}; + use std::io::{self, Read}; + use std::path::Path; + + const SLOTS: usize = 4096; + const ID_SLOT: usize = 17; + const KEY_SLOT: usize = 65; + const MAX_DEPTH: usize = super::physical::MAX_DEPTH; + const ACTIVE_GRAPH: usize = 0; + const ACTIVE_CACHE: usize = 1; + const LEASED_GENERATION: usize = 2; + const LIVE_RUN: usize = 3; + const RECOVERY: usize = 4; + const TEMPORARY: usize = 5; + const LEGACY: usize = 6; + const COORDINATION: usize = 8; + const UNKNOWN: usize = 9; + #[derive(Clone, Copy, Default)] + pub(crate) struct Bucket { + pub logical: Option, + pub allocated: Option, + seen: bool, + } + #[derive(Clone, Copy)] + pub(crate) struct Measurement { + pub total: Bucket, + pub retained: Bucket, + pub deferred: Bucket, + pub unknown: Bucket, + pub categories: [Bucket; 10], + pub complete: bool, + pub references_complete: bool, + pub resource_limited: bool, + pub allocation_unknown: bool, + pub unknown_entries: bool, + } + impl Default for Measurement { + fn default() -> Self { + Self { + total: Bucket::default(), + retained: Bucket::default(), + deferred: Bucket::default(), + unknown: Bucket::default(), + categories: [Bucket::default(); 10], + complete: true, + references_complete: false, + resource_limited: false, + allocation_unknown: false, + unknown_entries: false, + } + } + } + #[derive(Clone, Copy)] + enum Role { + State, + Managed, + Generations, + ActiveGraph, + Artifacts, + ArtifactShard([u8; 2]), + Artifact { active: bool }, + Temporary, + Run, + Phase, + Coordination, + Legacy, + OtherGeneration, + } + struct Walker<'a> { + memory: &'a CleanupMemory, + cancel: &'a dyn Fn() -> bool, + ids: ReservedBytes, + keys: ReservedBytes, + measurement: Measurement, + unknown_sizes: bool, + active: [u8; 132], + active_len: usize, + storage_name: Option<&'a [u8]>, + direct_artifacts: Option<&'a [u8]>, + direct_db: Option<&'a [u8]>, + direct_manifest: Option<&'a [u8]>, + } + fn add(bucket: &mut Bucket, logical: u64, allocated: Option) { + if !bucket.seen { + bucket.logical = Some(logical); + bucket.allocated = allocated; + bucket.seen = true; + } else { + bucket.logical = bucket.logical.and_then(|total| total.checked_add(logical)); + bucket.allocated = bucket + .allocated + .zip(allocated) + .and_then(|(total, value)| total.checked_add(value)); + } + } + fn index(bytes: &[u8]) -> usize { + let mut hash = 14695981039346656037u64; + for byte in bytes { + hash = (hash ^ u64::from(*byte)).wrapping_mul(1099511628211); + } + hash as usize % SLOTS + } + impl Walker<'_> { + fn remember(&mut self, identity: super::super::locks::FileIdentity) -> bool { + let (volume, inode) = identity.fixed_words(); + let mut key = [0u8; 16]; + key[..8].copy_from_slice(&volume.to_le_bytes()); + key[8..].copy_from_slice(&inode.to_le_bytes()); + let start = index(&key); + for offset in 0..SLOTS { + let slot = (start + offset) % SLOTS * ID_SLOT; + let bytes = &mut self.ids.as_mut_slice()[slot..slot + ID_SLOT]; + if bytes[0] == 0 { + bytes[0] = 1; + bytes[1..].copy_from_slice(&key); + return true; + } + if bytes[1..] == key { + return false; + } + } + self.measurement.complete = false; + self.unknown_sizes = true; + self.measurement.resource_limited = true; + false + } + fn key(&mut self, key: &[u8], insert: bool) -> bool { + if key.len() != 64 || !key.iter().all(u8::is_ascii_hexdigit) { + return false; + } + let start = index(key); + for offset in 0..SLOTS { + let slot = (start + offset) % SLOTS * KEY_SLOT; + let bytes = &mut self.keys.as_mut_slice()[slot..slot + KEY_SLOT]; + if bytes[0] == 0 { + if insert { + bytes[0] = 1; + bytes[1..].copy_from_slice(key); + } + return insert; + } + if bytes[1..] == *key { + return true; + } + } + if insert { + self.measurement.references_complete = false; + self.measurement.resource_limited = true; + } + false + } + fn account(&mut self, metadata: &Metadata, category: usize, disposition: usize) { + if category == UNKNOWN { + self.measurement.complete = false; + self.measurement.unknown_entries = true; + } + let allocation = super::allocation(metadata); + if allocation.is_none() { + self.measurement.complete = false; + self.measurement.allocation_unknown = true; + } + add(&mut self.measurement.total, metadata.len(), allocation); + add( + &mut self.measurement.categories[category], + metadata.len(), + allocation, + ); + match disposition { + 0 => add(&mut self.measurement.retained, metadata.len(), allocation), + 1 => add(&mut self.measurement.deferred, metadata.len(), allocation), + _ => add(&mut self.measurement.unknown, metadata.len(), allocation), + } + } + fn file_role(&self, role: Role, name: &[u8]) -> (usize, usize) { + if !name.is_empty() + && (name.ends_with(b".lock") + || matches!(name, b".use-cohort.json" | b".reference.epoch")) + { + return (COORDINATION, 0); + } + if matches!(role, Role::State) + && self.direct_db.is_some_and(|base| { + name == base + || (name.starts_with(base) + && name.get(base.len()) == Some(&b'.') + && super::super::layout::DIRECT_DB_SIDECAR_SUFFIXES.iter().any( + |suffix| name.get(base.len() + 1..) == Some(suffix.as_bytes()), + )) + }) + { + return (ACTIVE_GRAPH, 0); + } + match role { + Role::State + | Role::Managed + | Role::Generations + | Role::Artifacts + | Role::ArtifactShard(_) + if name.is_empty() => + { + (COORDINATION, 0) + } + Role::ActiveGraph => (ACTIVE_GRAPH, 0), + Role::Artifact { active: true } => (ACTIVE_CACHE, 0), + Role::Artifact { active: false } => (UNKNOWN, 2), + Role::Temporary => (TEMPORARY, 1), + Role::Run => (LIVE_RUN, 1), + Role::Phase => (RECOVERY, 0), + Role::Coordination => (COORDINATION, 0), + Role::Legacy => (LEGACY, 1), + Role::OtherGeneration => (LEASED_GENERATION, 1), + Role::State + if Some(name) == self.direct_db || Some(name) == self.direct_manifest => + { + (ACTIVE_GRAPH, 0) + } + Role::State | Role::Managed | Role::Artifacts + if matches!( + name, + b"writer.lock" + | b"state.lock" + | b"use.lock" + | b"lease.lock" + | b"config.json" + | b"active.json" + | b"coordinator.lock" + | b"coordinator.json" + ) || name.starts_with(b".reference-") + || name.starts_with(b".publication.") => + { + (COORDINATION, 0) + } + _ => (UNKNOWN, 2), + } + } + fn child_role(&mut self, role: Role, name: &[u8]) -> Option { + match role { + Role::State | Role::Managed | Role::Artifacts if name == b".legacy-coverage" => { + Some(Role::Coordination) + } + Role::State if Some(name) == self.storage_name => Some(Role::Managed), + Role::State if Some(name) == self.direct_artifacts => Some(Role::Artifacts), + Role::State if matches!(name, b"native-staging" | b"snapshots") => { + Some(Role::Legacy) + } + Role::State if name == b"direct-runs" => Some(Role::Run), + Role::Managed if name == b"generations" => Some(Role::Generations), + Role::Managed if name == b"artifacts" => Some(Role::Artifacts), + Role::Managed if name == b"runs" => Some(Role::Run), + Role::Managed if name == b"phases" => Some(Role::Phase), + Role::Generations if name == &self.active[..self.active_len] => { + Some(Role::ActiveGraph) + } + Role::Generations if name.starts_with(b"gen-") => Some(Role::OtherGeneration), + Role::Artifacts if name.len() == 2 && name.iter().all(u8::is_ascii_hexdigit) => { + Some(Role::ArtifactShard([name[0], name[1]])) + } + Role::Artifacts | Role::ArtifactShard(_) + if name.starts_with(b".tmp-") || name.starts_with(b".replaced-") => + { + Some(Role::Temporary) + } + Role::ArtifactShard(prefix) + if name.len() == 64 + && name[..2] == prefix + && name.iter().all(u8::is_ascii_hexdigit) => + { + Some(Role::Artifact { + active: self.measurement.references_complete && self.key(name, false), + }) + } + Role::Run + if name.starts_with(b"run-") + || matches!(name, b"candidate" | b"staging" | b"workers") => + { + Some(Role::Run) + } + Role::Phase => Some(Role::Phase), + // Unknown nested directories are observed as incomplete; a + // program-looking parent is not proof that their contents are + // in the registered observation scope. + Role::Legacy + | Role::Temporary + | Role::ActiveGraph + | Role::OtherGeneration + | Role::Artifact { .. } => None, + _ => None, + } + } + fn walk(&mut self, directory: RootedDirectory, role: Role, path: &Path) -> io::Result<()> { + let mut roles = [None; MAX_DEPTH]; + let memory = self.memory.clone(); + let cancel = self.cancel; + let result = super::physical::rooted(path, directory, &memory, cancel, &mut |entry| { + let Some(metadata) = entry.metadata else { + self.measurement.complete = false; + self.unknown_sizes = true; + return Ok(false); + }; + let Some(identity) = entry.identity else { + self.measurement.complete = false; + self.unknown_sizes = true; + return Ok(false); + }; + if metadata.is_dir() { + let selected = if entry.depth == 0 { + Some(role) + } else { + roles[entry.depth - 1] + .and_then(|parent| self.child_role(parent, entry.name)) + }; + let Some(selected) = selected else { + self.measurement.complete = false; + self.unknown_sizes = true; + return Ok(false); + }; + if !self.remember(identity) { + return Ok(false); + } + roles[entry.depth] = Some(selected); + let (category, disposition) = self.file_role(selected, b""); + self.account(metadata, category, disposition); + Ok(true) + } else if metadata.is_file() { + if self.remember(identity) { + let parent = if entry.depth == 0 { + role + } else { + roles[entry.depth - 1].ok_or(super::physical::Problem::Limit)? + }; + let (category, disposition) = self.file_role(parent, entry.name); + self.account(metadata, category, disposition); + } + Ok(false) + } else { + self.measurement.complete = false; + self.unknown_sizes = true; + Ok(false) + } + }); + match result { + Ok(()) => Ok(()), + Err(super::physical::Problem::Limit) => { + self.measurement.resource_limited = true; + self.unknown_sizes = true; + Err(io::ErrorKind::OutOfMemory.into()) + } + Err(super::physical::Problem::Cancelled) => { + self.unknown_sizes = true; + Err(io::ErrorKind::Interrupted.into()) + } + Err(super::physical::Problem::Io(kind)) => { + self.unknown_sizes = true; + Err(kind.into()) + } + } + } + fn file(&mut self, path: &Path) -> io::Result<()> { + match std::fs::symlink_metadata(path) { + Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(error) => return Err(error), + Ok(_) => {} + } + let memory = self.memory.clone(); + super::physical::path(path, &memory, &mut |entry| { + match (entry.metadata, entry.identity) { + (Some(metadata), Some(identity)) if metadata.is_file() => { + if self.remember(identity) { + self.account(metadata, ACTIVE_GRAPH, 0); + } + } + _ => { + self.measurement.complete = false; + self.unknown_sizes = true; + } + } + Ok(false) + }) + .map_err(|problem| match problem { + super::physical::Problem::Io(kind) => kind.into(), + super::physical::Problem::Limit => io::ErrorKind::OutOfMemory.into(), + super::physical::Problem::Cancelled => io::ErrorKind::Interrupted.into(), + }) + } + fn scan_references(&mut self, file: File) -> io::Result<()> { + let source = file.try_clone()?; + fn read_at(file: &File, offset: u64, output: &mut [u8]) -> io::Result { + #[cfg(unix)] + { + use std::os::unix::fs::FileExt; + file.read_at(output, offset) + } + #[cfg(windows)] + { + use std::os::windows::fs::FileExt; + file.seek_read(output, offset) + } + #[cfg(not(any(unix, windows)))] + { + let _ = (file, offset, output); + Err(io::ErrorKind::Unsupported.into()) + } + } + let mut reader = MaintenanceJsonReader::new(file, self.memory) + .map_err(|_| io::ErrorKind::InvalidData)?; + let files_hash: [u8; 32] = Sha256::digest(b"files").into(); + let key_hash: [u8; 32] = Sha256::digest(b"artifact_key").into(); + let mut depth = 0usize; + let mut files = false; + let mut want_files = false; + let mut want_key = false; + let schema_hash: [u8; 32] = Sha256::digest(b"schema_version").into(); + let mut want_schema = false; + let mut schema_valid = false; + let mut saw_files = false; + let mut entry_key_seen = false; + self.measurement.references_complete = true; + while let Some(token) = reader + .next_token() + .map_err(|_| io::ErrorKind::InvalidData)? + { + if (self.cancel)() { + return Err(io::ErrorKind::Interrupted.into()); + } + match token.kind { + TokenKind::ObjectStart | TokenKind::ArrayStart => { + depth += 1; + if want_files && depth == 2 { + files = true; + if saw_files { + self.measurement.references_complete = false; + } + saw_files = true; + } + if files && depth == 3 { + entry_key_seen = false; + } + want_files = false; + want_schema = false; + } + TokenKind::ObjectEnd | TokenKind::ArrayEnd => { + if files && depth == 2 { + files = false; + } + depth = depth.saturating_sub(1); + } + TokenKind::String { + key: true, + decoded_sha256, + .. + } => { + want_files = depth == 1 && decoded_sha256 == files_hash; + want_key = files && depth == 3 && decoded_sha256 == key_hash; + want_schema = depth == 1 && decoded_sha256 == schema_hash; + if want_key { + if entry_key_seen { + self.measurement.references_complete = false; + } + entry_key_seen = true; + } + } + TokenKind::String { + key: false, + decoded_len: 64, + .. + } if want_key => { + let mut raw_key = [0u8; 66]; + if token.end.saturating_sub(token.start) != 66 + || read_at(&source, token.start, &mut raw_key).ok() != Some(66) + || raw_key[0] != b'"' + || raw_key[65] != b'"' + || !self.key(&raw_key[1..65], true) + { + self.measurement.references_complete = false; + } + want_key = false; + } + TokenKind::Number if want_schema => { + let mut version = [0u8; 1]; + schema_valid = token.end.saturating_sub(token.start) == 1 + && read_at(&source, token.start, &mut version).ok() == Some(1) + && version[0] == b'5'; + want_schema = false; + } + TokenKind::Null if want_key => want_key = false, + _ if want_key => { + self.measurement.references_complete = false; + want_key = false; + } + _ => {} + } + } + reader.finish().map_err(|_| io::ErrorKind::InvalidData)?; + self.measurement.references_complete &= schema_valid && saw_files; + Ok(()) + } + } + pub(crate) fn observe( + target: &MaintenanceTarget, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + ) -> Result { + let scratch_size = [&target.repository_root, &target.state_root] + .iter() + .map(|p| p.as_os_str().len()) + .sum::() + .checked_add( + target + .storage_root + .as_ref() + .map_or(0, |p| p.as_os_str().len()), + ) + .and_then(|n| { + n.checked_add(target.direct_pair.as_ref().map_or(0, |p| { + p.database.as_os_str().len() + p.manifest.as_os_str().len() + })) + }) + .and_then(|n| n.checked_mul(16)) + .and_then(|n| n.checked_add(65536)) + .ok_or(ResourceLimit::ArithmeticOverflow)?; + let _scratch = memory.reserve(scratch_size)?; + let direct = target + .direct_pair + .as_ref() + .map(|pair| super::super::layout::DirectLayout::new(&pair.database, &pair.manifest)); + let artifacts = direct.as_ref().map(|layout| layout.artifact_root_path()); + let domain = match (&direct, &target.storage_root) { + (Some(layout), _) => { + super::super::maintenance_ownership::ReferenceDomain::direct(layout).ok() + } + (_, Some(storage)) => super::super::maintenance_ownership::ReferenceDomain::managed( + &super::super::layout::ManagedLayout::new(storage), + ) + .ok(), + _ => None, + }; + let binding_before = + super::super::maintenance_admission::observe_maintenance_binding(target, memory).ok(); + let epoch_before = domain.as_ref().and_then(|domain| { + match super::super::maintenance_ownership::observe_reference_epoch(domain).ok()? { + super::super::maintenance_ownership::EpochObservation::Verified(epoch) => { + Some(epoch) + } + _ => None, + } + }); + let storage_name = target + .storage_root + .as_ref() + .filter(|path| path.parent() == Some(target.state_root.as_path())) + .and_then(|path| path.file_name()) + .map(|name| name.as_encoded_bytes()); + let direct_artifacts = artifacts + .as_ref() + .filter(|path| path.parent() == Some(target.state_root.as_path())) + .and_then(|path| path.file_name()) + .map(|name| name.as_encoded_bytes()); + let direct_db = target + .direct_pair + .as_ref() + .filter(|pair| pair.database.parent() == Some(target.state_root.as_path())) + .and_then(|pair| pair.database.file_name()) + .map(|name| name.as_encoded_bytes()); + let direct_manifest = target + .direct_pair + .as_ref() + .filter(|pair| pair.manifest.parent() == Some(target.state_root.as_path())) + .and_then(|pair| pair.manifest.file_name()) + .map(|name| name.as_encoded_bytes()); + let mut walker = Walker { + memory, + cancel, + ids: memory.reserve(SLOTS * ID_SLOT)?.bytes(SLOTS * ID_SLOT)?, + keys: memory.reserve(SLOTS * KEY_SLOT)?.bytes(SLOTS * KEY_SLOT)?, + measurement: Measurement::default(), + unknown_sizes: false, + active: [0; 132], + active_len: 0, + storage_name, + direct_artifacts, + direct_db, + direct_manifest, + }; + walker.ids.as_mut_slice().fill(0); + walker.keys.as_mut_slice().fill(0); + let open = |path: &Path| { + path_identity(path) + .map_err(|_| io::Error::from(io::ErrorKind::InvalidData)) + .and_then(|identity| RootedDirectory::open_readonly_root(path, identity)) + }; + if let Some(storage) = &target.storage_root { + if let Ok(root) = open(storage) { + let active = (|| -> Result<_, io::Error> { + let mut file = root.open_readonly_file(b"active.json")?; + if file.metadata()?.len() > 1024 { + return Err(io::ErrorKind::InvalidData.into()); + } + let mut bytes = [0u8; 1024]; + let mut filled = 0; + loop { + let n = file.read(&mut bytes[filled..])?; + if n == 0 { + break; + } + filled += n; + if filled == bytes.len() { + break; + } + } + let active: super::super::managed::ActiveGeneration = + serde_json::from_slice(&bytes[..filled]) + .map_err(|_| io::ErrorKind::InvalidData)?; + super::super::layout::validate_generation_id(&active.generation_id) + .map_err(|_| io::ErrorKind::InvalidData)?; + walker.active[..4].copy_from_slice(b"gen-"); + walker.active[4..4 + active.generation_id.len()] + .copy_from_slice(active.generation_id.as_bytes()); + walker.active_len = 4 + active.generation_id.len(); + let generations = root.open_directory_readonly(b"generations")?; + let generation = + generations.open_directory_readonly(&walker.active[..walker.active_len])?; + walker.scan_references(generation.open_readonly_file(b"manifest.json")?)?; + let generation_path = storage.join("generations").join( + std::str::from_utf8(&walker.active[..walker.active_len]) + .map_err(|_| io::ErrorKind::InvalidData)?, + ); + walker.walk(generation, Role::ActiveGraph, &generation_path)?; + Ok(()) + })(); + if active.is_err() { + walker.measurement.references_complete = false; + walker.measurement.complete = false; + } + // Give known retained physical objects category priority over + // aliases encountered later in the general state namespace. + match root.open_directory_readonly(b"artifacts") { + Ok(artifacts) => { + if walker + .walk(artifacts, Role::Artifacts, &storage.join("artifacts")) + .is_err() + { + walker.measurement.complete = false; + walker.unknown_sizes = true; + } + } + Err(_) => walker.measurement.complete = false, + } + } + } else if let Some(pair) = &target.direct_pair { + if let Some(parent) = pair.manifest.parent() { + if let (Ok(root), Some(name)) = (open(parent), pair.manifest.file_name()) { + if root + .open_readonly_file(name.as_encoded_bytes()) + .and_then(|file| walker.scan_references(file)) + .is_err() + { + walker.measurement.references_complete = false; + } + } + } + } + if let Some(pair) = &target.direct_pair { + let mut observe_file = |path: &Path| { + if walker.file(path).is_err() { + walker.measurement.complete = false; + walker.unknown_sizes = true; + } + }; + observe_file(&pair.database); + observe_file(&pair.manifest); + for suffix in super::super::layout::DIRECT_DB_SIDECAR_SUFFIXES { + let mut sidecar = pair.database.as_os_str().to_os_string(); + sidecar.push("."); + sidecar.push(suffix); + observe_file(Path::new(&sidecar)); + } + if let Some(artifacts) = &artifacts { + if open(artifacts) + .and_then(|root| walker.walk(root, Role::Artifacts, artifacts)) + .is_err() + { + walker.measurement.complete = false; + walker.unknown_sizes = true; + } + } + } + if open(&target.state_root) + .and_then(|root| walker.walk(root, Role::State, &target.state_root)) + .is_err() + { + walker.measurement.complete = false; + walker.unknown_sizes = true; + } + if let Some(storage) = &target.storage_root { + if open(storage) + .and_then(|root| walker.walk(root, Role::Managed, storage)) + .is_err() + { + walker.measurement.complete = false; + walker.unknown_sizes = true; + } + } + let epoch_after = domain.as_ref().and_then(|domain| { + match super::super::maintenance_ownership::observe_reference_epoch(domain).ok()? { + super::super::maintenance_ownership::EpochObservation::Verified(epoch) => { + Some(epoch) + } + _ => None, + } + }); + walker.measurement.complete &= epoch_before.is_some() && epoch_before == epoch_after; + let binding_after = + super::super::maintenance_admission::observe_maintenance_binding(target, memory).ok(); + walker.measurement.complete &= binding_before.is_some() && binding_before == binding_after; + walker.measurement.complete &= walker.measurement.references_complete && !cancel(); + if walker.unknown_sizes { + walker.measurement.total.logical = None; + walker.measurement.total.allocated = None; + walker.measurement.unknown.logical = None; + walker.measurement.unknown.allocated = None; + walker.measurement.categories[UNKNOWN].logical = None; + walker.measurement.categories[UNKNOWN].allocated = None; + } + Ok(walker.measurement) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::atomic::{AtomicU64, Ordering}; + static SEQUENCE: AtomicU64 = AtomicU64::new(0); + struct Fixture { + root: PathBuf, + target: MaintenanceTarget, + } + impl Fixture { + fn new() -> Self { + let root = std::env::temp_dir().canonicalize().unwrap().join(format!( + "inventory-fixture-{}-{}", + std::process::id(), + SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir(&root).unwrap(); + let target = MaintenanceTarget { + repository_root: root.clone(), + state_root: root.join(".codebaseGraph"), + storage_root: None, + direct_pair: None, + config_path: None, + }; + Self { root, target } + } + fn put(&self, path: &str, bytes: &[u8]) { + let path = self.target.state_root.join(path); + fs::create_dir_all(path.parent().unwrap()).unwrap(); + fs::write(path, bytes).unwrap(); + } + fn active(&self) { + self.put("config.json", br#"{"schema_version":3}"#); + self.put("storage/writer.lock", b""); + self.put("storage/state.lock", b""); + self.put( + "storage/active.json", + br#"{"schema_version":2,"generation_id":"current"}"#, + ); + self.put("storage/generations/gen-current/graph.ldb", b"graph"); + self.put( + "storage/generations/gen-current/manifest.json", + format!( + r#"{{"files":{{"a":{{"artifact_key":"{}"}}}}}}"#, + "a".repeat(64) + ) + .as_bytes(), + ); + self.put( + &format!("storage/artifacts/aa/{}/partition.json", "a".repeat(64)), + b"cached", + ); + } + } + impl Drop for Fixture { + fn drop(&mut self) { + fs::remove_dir_all(&self.root).unwrap(); + } + } + fn tree(root: &Path) -> BTreeMap>)> { + fn walk(path: &Path, out: &mut BTreeMap>)>) { + let m = fs::symlink_metadata(path).unwrap(); + out.insert( + path.to_owned(), + ( + stamp(&m), + if m.is_file() { + Some(fs::read(path).unwrap()) + } else { + None + }, + ), + ); + if m.is_dir() { + for entry in fs::read_dir(path).unwrap() { + walk(&entry.unwrap().path(), out); + } + } + } + let mut out = BTreeMap::new(); + walk(root, &mut out); + out + } + #[test] + fn bounded_detailed_inventory_retains_overdeep_state_and_reports_unknown_totals() { + let fixture = Fixture::new(); + fixture.active(); + let mut path = fixture.target.state_root.join("native-staging"); + for _ in 0..70 { + path.push("d"); + } + fs::create_dir_all(&path).unwrap(); + fs::write(path.join("untouched.csv"), b"foreign bytes").unwrap(); + let before = tree(&fixture.root); + let observation = inspect_storage(&fixture.target); + assert_eq!(observation.status, InventoryStatus::Partial); + assert!(observation + .issues + .iter() + .any(|reason| reason == "inventory_resource_limit_retained")); + assert_eq!(observation.totals.logical_bytes, None); + assert_eq!(observation.totals.allocated_bytes, None); + assert_eq!(tree(&fixture.root), before); + } + + #[cfg(windows)] + #[test] + fn dc_s1_windows_canonical_prefix_is_probed_only_as_complete_root() { + let fixture = Fixture::new(); + let canonical = fs::canonicalize(&fixture.root).unwrap(); + assert!( + matches!(canonical.components().next(), Some(Component::Prefix(p)) if matches!(p.kind(), std::path::Prefix::VerbatimDisk(_) | std::path::Prefix::VerbatimUNC(_, _))) + ); + assert!(real_path(&canonical)); + assert!(real_path(canonical.ancestors().last().unwrap())); + assert!(!real_path(&canonical.join("missing-component"))); + assert!(!real_path(Path::new(r"\\.\C:\"))); + // Ordinary default storage observation must reach its real state, + // rather than fail centrally before any candidate-specific checks. + let inventory = inspect_storage(&fixture.target); + assert_eq!(inventory.status, InventoryStatus::Missing); + assert!(!inventory + .issues + .iter() + .any(|issue| issue == "invalid_repository_or_state_identity")); + } + + #[cfg(windows)] + #[test] + fn dc_s1_windows_prefix_only_malformed_roots_are_rejected_without_io() { + for input in [ + r"\\?\C:", + r"\\?\UNC\", + r"\\?\UNC\server", + r"\\?\UNC\\share", + r"\\?\GLOBALROOT", + r"\\.\C:", + ] { + assert!( + !real_path_with_metadata(Path::new(input), |_| { + panic!("malformed or unsupported root must not be probed: {input}") + }), + "{input}" + ); + } + } + + #[cfg(windows)] + #[test] + fn dc_s1_windows_prefix_only_unavailable_unc_root_is_rejected() { + let root = Path::new(r"\\?\UNC\localhost\codebasegraph-unavailable-share"); + assert_eq!(root.components().count(), 1); + let mut calls = 0; + assert!(!real_path_with_metadata(root, |observed| { + assert_eq!(observed.as_os_str(), root.as_os_str()); + calls += 1; + Err(std::io::Error::from(std::io::ErrorKind::NotFound)) + })); + assert_eq!(calls, 1); + } + + #[cfg(windows)] + #[test] + fn dc_s1_windows_prefix_only_available_unc_root_requires_directory_metadata() { + let fixture = Fixture::new(); + let directory = fs::symlink_metadata(&fixture.root).unwrap(); + let file = fixture.root.join("file"); + fs::write(&file, b"not a directory").unwrap(); + let file_metadata = fs::symlink_metadata(file).unwrap(); + // Supply deterministic existing metadata: Windows CI need not expose + // an administrative/network share to exercise its native path parser. + let root = Path::new(r"\\?\UNC\server\share"); + assert_eq!(root.components().count(), 1); + let mut calls = 0; + assert!(real_path_with_metadata(root, |observed| { + assert_eq!(observed.as_os_str(), root.as_os_str()); + calls += 1; + Ok(directory.clone()) + })); + assert_eq!(calls, 1); + assert!(!real_path_with_metadata( + root, + |_| Ok(file_metadata.clone()) + )); + assert!(!real_path_with_metadata(root, |_| { + Err(std::io::Error::from(std::io::ErrorKind::PermissionDenied)) + })); + } + + #[cfg(windows)] + #[test] + fn dc_s1_windows_root_prefix_fix_keeps_symlink_ancestor_rejection() { + let fixture = Fixture::new(); + let foreign = Fixture::new(); + fs::create_dir(foreign.root.join("nested")).unwrap(); + let alias = fixture.root.join("foreign-alias"); + std::os::windows::fs::symlink_dir(&foreign.root, &alias).unwrap(); + assert!(!real_path(&alias)); + assert!(!real_path(&alias.join("nested"))); + assert!(!real_path(&alias.join("nested/../nested"))); + assert!(!real_path(Path::new( + r"\\?\GLOBALROOT\Device\HarddiskVolume1\" + ))); + fs::remove_dir(alias).unwrap(); + } + + #[test] + fn dc_s1_a1_classifies_and_counts_active_live_recovery_and_legacy() { + let fixture = Fixture::new(); + fixture.active(); + for (path, bytes) in [ + ( + "storage/runs/run-live/journal.json", + b"publishing".as_slice(), + ), + ("storage/runs/run-orphan/staging/source", b"orphan"), + ("storage/generations/gen-old/graph.ldb", b"old"), + ("storage/phases/unknown/proof.json", b"proof"), + ("native-staging/run-old/import.csv", b"legacy"), + ( + "storage/artifacts/aa/.replaced-key-nonce/partition.json", + b"backup", + ), + ("storage/artifacts/.tmp-key-nonce/partition.json", b"temp"), + ] { + fixture.put(path, bytes); + } + let before = tree(&fixture.root); + let inventory = inspect_storage(&fixture.target); + for (suffix, category, disposition) in [ + ( + "gen-current/graph.ldb", + InventoryCategory::ActiveGraph, + Disposition::Retain, + ), + ( + "run-live/journal.json", + InventoryCategory::LiveRun, + Disposition::Deferred, + ), + ( + "run-orphan/staging/source", + InventoryCategory::LiveRun, + Disposition::Deferred, + ), + ( + "gen-old/graph.ldb", + InventoryCategory::LeasedGeneration, + Disposition::Deferred, + ), + ( + "run-old/import.csv", + InventoryCategory::LegacyStaging, + Disposition::Deferred, + ), + ( + ".replaced-key-nonce/partition.json", + InventoryCategory::ArtifactTemporary, + Disposition::Deferred, + ), + ( + "unknown/proof.json", + InventoryCategory::RecoveryEvidence, + Disposition::Retain, + ), + ] { + let entry = inventory + .entries + .iter() + .find(|e| e.path.ends_with(suffix)) + .unwrap(); + assert_eq!((entry.category, entry.disposition), (category, disposition)); + } + assert!(inventory + .entries + .iter() + .any(|e| e.category == InventoryCategory::ActiveCache + && e.disposition == Disposition::Retain)); + let expected: u64 = before + .values() + .filter_map(|(_, bytes)| bytes.as_ref().map(|b| b.len() as u64)) + .sum(); + assert_eq!(inventory.totals.logical_bytes, Some(expected)); + assert_eq!( + inventory.totals.files, + before.values().filter(|(_, b)| b.is_some()).count() as u64 + ); + assert_eq!(before, tree(&fixture.root)); + } + #[test] + fn dc_s1_a2_missing_and_dropped_observations_do_not_create_or_repair() { + let fixture = Fixture::new(); + let before = tree(&fixture.root); + assert_eq!( + inspect_storage(&fixture.target).status, + InventoryStatus::Missing + ); + assert_eq!(before, tree(&fixture.root)); + fixture.active(); + fixture.put("storage/runs/run-abandoned/journal.json", b"corrupt"); + fixture.put("storage/.publish.journal.json", b"recoverable"); + let before = tree(&fixture.root); + drop(inspect_storage(&fixture.target)); + assert_eq!(before, tree(&fixture.root)); + let caught = std::panic::catch_unwind(|| { + inspect_with_hook(&fixture.target, || panic!("interrupted observation")) + }); + assert!(caught.is_err()); + assert_eq!(before, tree(&fixture.root)); + let file = File::open(fixture.target.state_root.join("storage/writer.lock")).unwrap(); + FileExt::try_lock_exclusive(&file).unwrap(); + } + #[test] + fn dc_s1_a3_constant_pointer_does_not_hide_artifact_and_run_mutations() { + let fixture = Fixture::new(); + fixture.active(); + fixture.put("storage/runs/run-live/staging/source", b"before"); + let inventory = inspect_with_hook(&fixture.target, || { + fixture.put("storage/runs/run-live/staging/source", b"after-longer"); + fixture.put("storage/artifacts/.tmp-new/partition.json", b"new"); + }); + assert_eq!(inventory.active_generation.as_deref(), Some("current")); + assert_eq!(inventory.status, InventoryStatus::Partial); + assert!(inventory + .issues + .iter() + .any(|s| s == "storage_changed_during_observation")); + assert!(!inventory.totals.observation_complete); + } + #[test] + fn dc_s1_a3_invalid_pointer_and_foreign_direct_are_retained() { + let fixture = Fixture::new(); + fixture.active(); + fixture.put( + "storage/active.json", + br#"{"schema_version":2,"generation_id":"../escape"}"#, + ); + let mut target = fixture.target.clone(); + target.direct_pair = Some(DirectPair { + database: fixture.root.join("custom.ldb"), + manifest: fixture.root.join("custom.json"), + }); + let before = tree(&fixture.root); + let inventory = inspect_storage(&target); + assert_eq!(inventory.active_generation, None); + assert_eq!(inventory.status, InventoryStatus::Invalid); + assert!(inventory + .issues + .iter() + .any(|s| s == "direct_destination_repository_binding_required")); + assert!(inventory + .entries + .iter() + .all(|e| e.disposition != Disposition::PotentialReclamation)); + assert_eq!(before, tree(&fixture.root)); + } + #[cfg(unix)] + #[test] + fn dc_s1_a3_symlinks_are_not_followed_and_sizes_are_unknown() { + use std::os::unix::fs::symlink; + let fixture = Fixture::new(); + fixture.active(); + fs::create_dir(fixture.root.join("outside")).unwrap(); + fs::write(fixture.root.join("outside/sentinel"), b"outside").unwrap(); + symlink( + fixture.root.join("outside"), + fixture.target.state_root.join("foreign"), + ) + .unwrap(); + let before = tree(&fixture.root); + let inventory = inspect_storage(&fixture.target); + let link = inventory + .entries + .iter() + .find(|e| e.path.ends_with("foreign")) + .unwrap(); + assert_eq!(link.logical_bytes, None); + assert!(!inventory + .entries + .iter() + .any(|e| e.path.ends_with("foreign/sentinel"))); + assert_eq!(inventory.totals.logical_bytes, None); + assert_eq!(before, tree(&fixture.root)); + } + #[test] + fn dc_s1_a4_repeat_observation_is_deterministic() { + let fixture = Fixture::new(); + fixture.active(); + let a = inspect_storage(&fixture.target); + let b = inspect_storage(&fixture.target); + assert_eq!(a.entries, b.entries); + assert_eq!(a.totals, b.totals); + assert_eq!(a.issues, b.issues); + assert_eq!(serde_json::to_value(a.status).unwrap(), "partial"); + } + #[cfg(unix)] + #[test] + fn physical_hardlinks_count_once_and_allocation_uses_filesystem_blocks() { + use std::os::unix::fs::MetadataExt; + let fixture = Fixture::new(); + fixture.active(); + fixture.put("first", b"hardlink-content"); + fs::hard_link( + fixture.target.state_root.join("first"), + fixture.target.state_root.join("second"), + ) + .unwrap(); + let inventory = inspect_storage(&fixture.target); + assert_eq!(inventory.totals.duplicate_files, 1); + let second = inventory + .entries + .iter() + .find(|e| e.path.ends_with("second")) + .unwrap(); + assert_eq!( + (second.files, second.logical_bytes, second.allocated_bytes), + (0, Some(0), Some(0)) + ); + let mut seen = BTreeSet::new(); + let expected: u64 = tree(&fixture.target.state_root) + .keys() + .filter_map(|p| { + let m = fs::symlink_metadata(p).unwrap(); + if seen.insert((m.dev(), m.ino())) { + Some(m.blocks() * 512) + } else { + None + } + }) + .sum(); + assert_eq!(inventory.totals.allocated_bytes, Some(expected)); + } + #[test] + fn busy_writer_is_reported_without_waiting_or_mutation() { + let fixture = Fixture::new(); + fixture.active(); + let before = tree(&fixture.root); + let lock = File::open(fixture.target.state_root.join("storage/writer.lock")).unwrap(); + FileExt::try_lock_exclusive(&lock).unwrap(); + let inventory = inspect_storage(&fixture.target); + drop(lock); + assert_eq!(inventory.status, InventoryStatus::Busy); + assert_eq!(before, tree(&fixture.root)); + } + #[test] + fn recognized_direct_pair_uses_repository_identity() { + let fixture = Fixture::new(); + fixture.active(); + fixture.put( + &format!("{}_graph.ldb", repository_name(&fixture.root)), + b"direct", + ); + fixture.put("manifest.json", b"{}"); + let inventory = inspect_storage(&fixture.target); + assert!(inventory + .entries + .iter() + .any(|e| e.category == InventoryCategory::RetiredDirect + && e.path.extension().is_some_and(|e| e == "ldb"))); + } + #[test] + fn configured_ancestor_root_is_rejected_without_scanning_sources() { + let fixture = Fixture::new(); + fixture.active(); + let mut target = fixture.target.clone(); + target.storage_root = Some(fixture.root.clone()); + let inventory = inspect_storage(&target); + assert_eq!(inventory.status, InventoryStatus::Invalid); + assert!(inventory.entries.is_empty()); + } + #[test] + fn existing_live_lease_is_observed_without_recovery() { + let fixture = Fixture::new(); + fixture.active(); + fixture.put("storage/runs/run-live/lease.lock", b""); + let file = File::open( + fixture + .target + .state_root + .join("storage/runs/run-live/lease.lock"), + ) + .unwrap(); + FileExt::try_lock_exclusive(&file).unwrap(); + let inventory = inspect_storage(&fixture.target); + assert!(inventory + .entries + .iter() + .any(|e| e.category == InventoryCategory::LiveRun + && e.reason == "existing_lease_in_use_retained" + && e.disposition == Disposition::Retain)); + } + #[cfg(unix)] + #[test] + fn hardlinked_direct_identity_is_unknown() { + let fixture = Fixture::new(); + fixture.active(); + let name = format!("{}_graph.ldb", repository_name(&fixture.root)); + fixture.put(&name, b"direct"); + fs::hard_link( + fixture.target.state_root.join(&name), + fixture.target.state_root.join("manifest.json"), + ) + .unwrap(); + let inventory = inspect_storage(&fixture.target); + assert!(inventory + .issues + .iter() + .any(|i| i == "ambiguous_direct_physical_identity_retained")); + assert!(!inventory + .entries + .iter() + .any(|e| e.category == InventoryCategory::RetiredDirect)); + } + fn write_config(path: &Path, storage: &Path, repository: &Path) { + fs::write(path,serde_json::to_vec(&serde_json::json!({"schema_version":3,"repo_root":repository,"storage_root":storage})).unwrap()).unwrap(); + } + #[test] + fn review_scope_rejects_source_state_and_unregistered_external_storage() { + let fixture = Fixture::new(); + fixture.active(); + fs::create_dir(fixture.root.join("knowledge")).unwrap(); + fs::write(fixture.root.join("knowledge/source.md"), b"curated source").unwrap(); + let foreign = Fixture::new(); + foreign.put("storage/foreign/sentinel", b"foreign"); + let mut target = fixture.target.clone(); + target.state_root = fixture.root.join("knowledge"); + let before = tree(&fixture.root); + let outside_before = tree(&foreign.root); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Invalid); + assert!(observation.entries.is_empty()); + target = fixture.target.clone(); + target.storage_root = Some(foreign.target.state_root.join("storage")); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Invalid); + assert!(observation.entries.is_empty()); + assert_eq!(before, tree(&fixture.root)); + assert_eq!(outside_before, tree(&foreign.root)); + } + #[test] + fn review_bound_external_store_survives_missing_conventional_state() { + let fixture = Fixture::new(); + let external = Fixture::new(); + external.active(); + let storage = external.target.state_root.join("storage"); + let config = fixture.root.join("external-config.json"); + write_config(&config, &storage, &fixture.root); + let mut target = fixture.target.clone(); + target.config_path = Some(config); + target.storage_root = Some(storage.clone()); + let before = tree(&fixture.root); + let external_before = tree(&external.root); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Partial); + assert_eq!(observation.active_generation.as_deref(), Some("current")); + assert!(observation.entries.iter().any(|e| e.path + == storage.join("generations/gen-current/graph.ldb") + && e.category == InventoryCategory::ActiveGraph)); + assert!(!target.state_root.exists()); + assert_eq!(before, tree(&fixture.root)); + assert_eq!(external_before, tree(&external.root)); + } + #[test] + fn review_external_config_requires_matching_repository_binding() { + let fixture = Fixture::new(); + let external = Fixture::new(); + external.active(); + let storage = external.target.state_root.join("storage"); + let config = fixture.root.join("external-config.json"); + write_config(&config, &storage, &external.root); + let mut target = fixture.target.clone(); + target.config_path = Some(config); + target.storage_root = Some(storage); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Invalid); + assert!(observation.entries.is_empty()); + } + fn direct_fixture(fixture: &Fixture) -> DirectPair { + let pair = default_direct(&fixture.target); + fixture.put( + pair.database.file_name().unwrap().to_str().unwrap(), + b"selected direct", + ); + fixture.put("manifest.json",serde_json::to_string(&serde_json::json!({"schema_version":2,"files":{"a":{"artifact_key":"b".repeat(64)}}})).unwrap().as_bytes()); + let layout = DirectLayout::new(&pair.database, &pair.manifest); + let artifact = layout + .artifact_root_path() + .join("bb") + .join("b".repeat(64)) + .join("partition.json"); + fs::create_dir_all(artifact.parent().unwrap()).unwrap(); + fs::write(artifact, b"current cache").unwrap(); + pair + } + fn assert_current_direct(observation: &StorageInventory, pair: &DirectPair) { + assert!(observation.entries.iter().any(|e| e.path == pair.database + && e.category == InventoryCategory::ActiveGraph + && e.disposition == Disposition::Retain)); + assert!(observation + .entries + .iter() + .any(|e| e.path.ends_with("partition.json") + && e.category == InventoryCategory::ActiveCache + && e.disposition == Disposition::Retain)); + assert!(!observation + .entries + .iter() + .any(|e| e.path == pair.database && e.category == InventoryCategory::RetiredDirect)); + } + #[test] + fn review_default_direct_only_is_current_graph_and_cache() { + let fixture = Fixture::new(); + let pair = direct_fixture(&fixture); + let before = tree(&fixture.root); + let observation = inspect_storage(&fixture.target); + assert_current_direct(&observation, &pair); + assert_eq!(before, tree(&fixture.root)); + } + #[test] + fn review_explicit_direct_alongside_managed_is_current() { + let fixture = Fixture::new(); + fixture.active(); + let pair = direct_fixture(&fixture); + let mut target = fixture.target.clone(); + target.direct_pair = Some(pair.clone()); + assert_current_direct(&inspect_storage(&target), &pair); + } + #[test] + fn review_managed_config_makes_unselected_default_direct_former() { + let fixture = Fixture::new(); + fixture.active(); + let pair = direct_fixture(&fixture); + let observation = inspect_storage(&fixture.target); + assert!(observation.entries.iter().any(|e| e.path == pair.database + && e.category == InventoryCategory::RetiredDirect + && e.disposition == Disposition::Deferred)); + assert!(observation + .entries + .iter() + .any(|e| e.path.ends_with("gen-current/graph.ldb") + && e.category == InventoryCategory::ActiveGraph)); + } + #[test] + fn review_case_alias_roots_are_rejected_without_double_counting() { + let fixture = Fixture::new(); + fixture.active(); + let alias = fixture.root.join(".CODEBASEGRAPH"); + if !alias.exists() { + eprintln!("case-alias scenario unavailable on this case-sensitive fixture filesystem"); + return; + } + assert!(same_physical_path(&alias, &fixture.target.state_root)); + let baseline = inspect_storage(&fixture.target); + let mut target = fixture.target.clone(); + target.storage_root = Some(alias); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Invalid); + assert!(observation.entries.is_empty()); + assert!(observation.totals.directories <= baseline.totals.directories); + } + #[test] + fn review_current_configuration_is_revalidated_on_every_observation() { + let fixture = Fixture::new(); + let external = Fixture::new(); + external.active(); + let storage = external.target.state_root.join("storage"); + let config = fixture.root.join("external-config.json"); + write_config(&config, &storage, &fixture.root); + let mut target = fixture.target.clone(); + target.config_path = Some(config.clone()); + target.storage_root = Some(storage.clone()); + let observation = + inspect_with_hook(&target, || write_config(&config, &storage, &external.root)); + assert!(observation + .issues + .iter() + .any(|i| i == "storage_changed_during_observation")); + let second = inspect_storage(&target); + assert_eq!(second.status, InventoryStatus::Invalid); + assert!(second.entries.is_empty()); + } + #[test] + fn review_external_current_direct_config_observes_exact_bundle_without_local_state() { + let fixture = Fixture::new(); + let external = Fixture::new(); + let pair = direct_fixture(&external); + let config = fixture.root.join("direct-config.json"); + fs::write(&config,serde_json::to_vec(&serde_json::json!({"repo_root":fixture.root,"database_path":pair.database,"manifest_path":pair.manifest})).unwrap()).unwrap(); + fs::write(external.root.join("foreign-sentinel"), b"foreign source").unwrap(); + let mut target = fixture.target.clone(); + target.config_path = Some(config); + let before = tree(&external.root); + let observation = inspect_storage(&target); + assert_current_direct(&observation, &pair); + assert!(!observation + .entries + .iter() + .any(|e| e.path.ends_with("foreign-sentinel"))); + assert!(!target.state_root.exists()); + assert_eq!(before, tree(&external.root)); + } + #[test] + fn review_configured_storage_cannot_be_overridden_with_unselected_local_root() { + let fixture = Fixture::new(); + fixture.active(); + let external = Fixture::new(); + external.active(); + let config = fixture.target.state_root.join("config.json"); + write_config( + &config, + &external.target.state_root.join("storage"), + &fixture.root, + ); + let mut target = fixture.target.clone(); + target.storage_root = Some(target.state_root.join("storage")); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Invalid); + assert!(observation.entries.is_empty()); + } + // Fixture-only DOS/UNC spelling. Joining a canonical verbatim Windows + // prefix normalizes dot/parent components before the fixture can test raw + // address keys; an ordinary prefix preserves the intended distinct input. + #[cfg(windows)] + fn ordinary_windows_fixture_path(path: &Path) -> PathBuf { + use std::path::Prefix; + let Some(Component::Prefix(prefix)) = path.components().next() else { + panic!("absolute Windows fixture prefix"); + }; + let mut result = match prefix.kind() { + Prefix::VerbatimDisk(drive) => PathBuf::from(format!("{}:\\", char::from(drive))), + Prefix::VerbatimUNC(server, share) => { + let mut root = std::ffi::OsString::from(r"\\"); + root.push(server); + root.push(r"\"); + root.push(share); + root.push(r"\"); + PathBuf::from(root) + } + _ => return path.to_owned(), + }; + for component in path.components() { + if let Component::Normal(name) = component { + result.push(name); + } + } + assert_eq!( + fs::canonicalize(&result).unwrap(), + fs::canonicalize(path).unwrap() + ); + result + } + + #[test] + fn review_raw_config_direct_spelling_selects_matching_cache() { + for address in ["./", "../"] { + let owner = Fixture::new(); + let external = Fixture::new(); + let config_parent = if address == "../" { + external.root.join("config") + } else { + external.root.clone() + }; + fs::create_dir_all(&config_parent).unwrap(); + #[cfg(windows)] + let config_parent = ordinary_windows_fixture_path(&config_parent); + let raw_db = format!("{address}graph.ldb"); + let raw_manifest = format!("{address}manifest.json"); + let pair = DirectPair { + database: config_parent.join(&raw_db), + manifest: config_parent.join(&raw_manifest), + }; + fs::write(&pair.database, b"raw addressed graph").unwrap(); + fs::write( + &pair.manifest, + serde_json::to_vec( + &serde_json::json!({"files":{"a":{"artifact_key":"c".repeat(64)}}}), + ) + .unwrap(), + ) + .unwrap(); + let raw_layout = DirectLayout::new(&pair.database, &pair.manifest); + let artifact = raw_layout + .artifact_root_path() + .join("cc") + .join("c".repeat(64)) + .join("partition.json"); + fs::create_dir_all(artifact.parent().unwrap()).unwrap(); + fs::write(&artifact, b"raw-key cache").unwrap(); + let normalized_layout = DirectLayout::new( + fs::canonicalize(&pair.database).unwrap(), + fs::canonicalize(&pair.manifest).unwrap(), + ); + assert_ne!( + raw_layout.artifact_root_path(), + normalized_layout.artifact_root_path() + ); + let config = config_parent.join("config.json"); + fs::write(&config,serde_json::to_vec(&serde_json::json!({"repo_root":owner.root,"database_path":raw_db,"manifest_path":raw_manifest})).unwrap()).unwrap(); + let mut target = owner.target.clone(); + target.config_path = Some(config); + let before = tree(&external.root); + let observation = inspect_storage(&target); + assert!( + observation.entries.iter().any(|e| e.path == artifact + && e.category == InventoryCategory::ActiveCache + && e.disposition == Disposition::Retain), + "missing active raw-key cache for {address}: {:?}", + observation.issues + ); + assert_eq!( + observe_binding(&target) + .unwrap() + .current_direct + .unwrap() + .database + .as_os_str(), + pair.database.as_os_str() + ); + assert_eq!(before, tree(&external.root)); + assert!(!target.state_root.exists()); + } + } + fn overlapping_role_fixture(managed_inside_direct: bool) { + let owner = Fixture::new(); + let external = Fixture::new(); + let parent = if managed_inside_direct { + external.root.clone() + } else { + external.root.join("managed") + }; + fs::create_dir_all(&parent).unwrap(); + let pair = DirectPair { + database: parent.join("graph.ldb"), + manifest: parent.join("manifest.json"), + }; + fs::write(&pair.database, b"graph").unwrap(); + fs::write(&pair.manifest, br#"{"files":{}}"#).unwrap(); + let artifacts = DirectLayout::new(&pair.database, &pair.manifest).artifact_root_path(); + fs::create_dir_all(&artifacts).unwrap(); + let managed = if managed_inside_direct { + artifacts.join("managed") + } else { + parent + }; + fs::create_dir_all(managed.join("generations/gen-current")).unwrap(); + fs::write( + managed.join("active.json"), + br#"{"schema_version":2,"generation_id":"current"}"#, + ) + .unwrap(); + fs::write( + managed.join("generations/gen-current/manifest.json"), + br#"{"files":{}}"#, + ) + .unwrap(); + let config = external.root.join("config.json"); + fs::write(&config,serde_json::to_vec(&serde_json::json!({"repo_root":owner.root,"schema_version":3,"storage_root":managed,"database_path":pair.database,"manifest_path":pair.manifest})).unwrap()).unwrap(); + let mut target = owner.target.clone(); + target.config_path = Some(config); + target.storage_root = Some(managed); + target.direct_pair = Some(pair); + let before = tree(&external.root); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Invalid); + assert!(observation.entries.is_empty()); + assert_eq!(observation.totals.directories, 0); + assert!(observation + .issues + .iter() + .any(|i| i == "managed_direct_storage_role_overlap")); + assert_eq!(before, tree(&external.root)); + } + #[test] + fn review_managed_root_inside_direct_cache_is_rejected() { + overlapping_role_fixture(true); + } + #[test] + fn review_direct_cache_inside_managed_root_is_rejected() { + overlapping_role_fixture(false); + } + #[test] + fn review_complete_root_reconciliation_is_insertion_order_independent() { + let fixture = Fixture::new(); + fixture.active(); + let ancestor = fixture.target.state_root.clone(); + let descendant = ancestor.join("storage"); + for roots in [ + vec![descendant.clone(), ancestor.clone()], + vec![ancestor.clone(), descendant.clone()], + ] { + let roots = reconcile_observation_roots(roots); + assert_eq!(roots, vec![ancestor.clone()]); + let memory = + super::super::maintenance_budget::CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let mut scan = Scan::new(&memory); + for root in roots { + scan.walk(&root); + } + let expected = tree(&ancestor) + .values() + .filter(|(stamp, _)| stamp.kind == 1) + .count() as u64; + assert_eq!( + scan.entries.iter().map(|e| e.directories).sum::(), + expected + ); + } + } + #[cfg(unix)] + #[test] + fn review_config_storage_parent_traversal_cannot_erase_symlink_or_missing_component() { + use std::os::unix::fs::symlink; + for symlinked in [true, false] { + let owner = Fixture::new(); + let external = Fixture::new(); + let config_parent = external.root.join("config"); + let actual_parent = external.root.join("actual"); + fs::create_dir_all(config_parent.join("storage")).unwrap(); + fs::create_dir_all(actual_parent.join("child")).unwrap(); + fs::create_dir_all(actual_parent.join("storage")).unwrap(); + fs::write( + config_parent.join("storage/unconfigured-sibling"), + b"must not observe", + ) + .unwrap(); + fs::write( + actual_parent.join("storage/raw-destination"), + b"must not follow symlink", + ) + .unwrap(); + if symlinked { + symlink(actual_parent.join("child"), config_parent.join("hop")).unwrap(); + } + let config = config_parent.join("config.json"); + fs::write(&config,serde_json::to_vec(&serde_json::json!({"schema_version":3,"repo_root":owner.root,"storage_root":"hop/../storage"})).unwrap()).unwrap(); + let mut target = owner.target.clone(); + target.config_path = Some(config); + let before = tree(&external.root); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Invalid); + assert!(observation.entries.is_empty()); + assert!(observation + .issues + .iter() + .any(|i| i == "configuration_storage_root_path_missing_or_symlinked")); + assert_eq!(before, tree(&external.root)); + } + } + #[cfg(unix)] + #[test] + fn review_config_repository_parent_traversal_cannot_erase_symlink() { + use std::os::unix::fs::symlink; + let owner = Fixture::new(); + let external = Fixture::new(); + external.active(); + fs::create_dir(external.root.join("child")).unwrap(); + symlink(external.root.join("child"), owner.root.join("hop")).unwrap(); + let config = owner.root.join("external-config.json"); + fs::write(&config,serde_json::to_vec(&serde_json::json!({"schema_version":3,"repo_root":"hop/..","storage_root":external.target.state_root.join("storage")})).unwrap()).unwrap(); + let mut target = owner.target.clone(); + target.config_path = Some(config); + let before = tree(&external.root); + let observation = inspect_storage(&target); + assert_eq!(observation.status, InventoryStatus::Invalid); + assert!(observation.entries.is_empty()); + assert!(observation + .issues + .iter() + .any(|i| i == "configuration_repo_root_path_missing_or_symlinked")); + assert_eq!(before, tree(&external.root)); + } + #[test] + fn review_real_directory_dot_parent_config_paths_remain_supported() { + let owner = Fixture::new(); + let external = Fixture::new(); + external.active(); + fs::create_dir(owner.root.join("real-hop")).unwrap(); + fs::create_dir(external.target.state_root.join("real-hop")).unwrap(); + for storage in ["./storage", "real-hop/../storage"] { + let config = external.target.state_root.join("bound-config.json"); + fs::write(&config,serde_json::to_vec(&serde_json::json!({"schema_version":3,"repo_root":owner.root.join("real-hop/.."),"storage_root":storage})).unwrap()).unwrap(); + let mut target = owner.target.clone(); + target.config_path = Some(config); + let observation = inspect_storage(&target); + assert_eq!(observation.active_generation.as_deref(), Some("current")); + assert!(observation + .entries + .iter() + .any(|e| e.path.ends_with("gen-current/graph.ldb") + && e.category == InventoryCategory::ActiveGraph)); + assert!(!target.state_root.exists()); + } + } +} diff --git a/src/storage/layout.rs b/src/storage/layout.rs index e9b5cff5..7432de5d 100644 --- a/src/storage/layout.rs +++ b/src/storage/layout.rs @@ -18,6 +18,44 @@ pub(crate) const DIRECT_DB_SIDECAR_SUFFIXES: &[&str] = &[ "search.document_offsets.bin", ]; +/// Producer-only evidence that the complete reference namespace was created +/// by this operation. An existing cache or lock can never construct this token. +#[derive(Debug)] +pub(crate) struct FreshReferenceRoot { + path: PathBuf, + identity: super::locks::FileIdentity, +} +impl FreshReferenceRoot { + pub(crate) fn path(&self) -> &Path { + &self.path + } + pub(crate) fn identity(&self) -> super::locks::FileIdentity { + self.identity + } +} +pub(crate) fn create_fresh_reference_root( + path: &Path, +) -> Result, NativeError> { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + match fs::create_dir(path) { + Ok(()) => Ok(Some(FreshReferenceRoot { + path: path.to_owned(), + identity: super::locks::path_identity(path)?, + })), + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => { + if !fs::symlink_metadata(path)?.is_dir() { + return Err(NativeError::InvalidInput( + "reference_root_not_directory".into(), + )); + } + Ok(None) + } + Err(error) => Err(error.into()), + } +} + #[derive(Debug, Clone)] pub(crate) struct RepositoryLayout { state_root: PathBuf, @@ -120,7 +158,6 @@ impl ManagedLayout { self.storage_root().to_path_buf(), self.generations_root(), self.runs_root(), - self.artifacts_root(), ] { fs::create_dir_all(&root)?; let metadata = fs::symlink_metadata(&root)?; @@ -131,6 +168,7 @@ impl ManagedLayout { ))); } } + super::maintenance_ownership::ArtifactUseLease::ensure_root(&self.artifacts_root())?; Ok(()) } } @@ -285,6 +323,16 @@ impl DirectLayout { )) } + pub(crate) fn registration_path(&self) -> PathBuf { + self.db_path + .parent() + .unwrap_or_else(|| Path::new(".")) + .join(format!( + ".direct-registration-{}.json", + self.destination_key() + )) + } + pub(crate) fn artifact_root_path(&self) -> PathBuf { let parent = self.db_path.parent().unwrap_or_else(|| Path::new(".")); parent.join(format!(".direct-artifacts-{}", self.destination_key())) diff --git a/src/storage/locks.rs b/src/storage/locks.rs index 87ec0076..8407242e 100644 --- a/src/storage/locks.rs +++ b/src/storage/locks.rs @@ -3,6 +3,37 @@ use fs2::{lock_contended_error, FileExt}; use std::fs::{self, File, OpenOptions}; use std::path::{Path, PathBuf}; +/// Exact OS error compatibility for the declared Rust1.82 minimum. +pub(crate) fn is_directory_not_empty(error: &std::io::Error) -> bool { + #[cfg(unix)] + { + error.raw_os_error() == Some(libc::ENOTEMPTY) + } + #[cfg(windows)] + { + error.raw_os_error() == Some(145) + } + #[cfg(not(any(unix, windows)))] + { + let _ = error; + false + } +} +pub(crate) fn directory_not_empty_error() -> std::io::Error { + #[cfg(unix)] + { + std::io::Error::from_raw_os_error(libc::ENOTEMPTY) + } + #[cfg(windows)] + { + std::io::Error::from_raw_os_error(145) + } + #[cfg(not(any(unix, windows)))] + { + std::io::Error::new(std::io::ErrorKind::Other, "directory not empty") + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum LockMode { Shared, @@ -14,9 +45,45 @@ pub(crate) struct LockedFile { path: PathBuf, file: Option, mode: LockMode, + companions: Vec, } impl LockedFile { + /// Verify actual held ownership, including lock inode replacement. Labels + /// and paths supplied by callers never constitute ownership on their own. + pub(crate) fn verifies(&self, path: &Path, mode: LockMode) -> Result { + let Some(file) = &self.file else { + return Ok(false); + }; + Ok(self.path == path + && self.mode == mode + && file_identity(file)? == path_identity(path)? + && file_link_count(file)? == 1 + && regular_unaliased(path)?) + } + + pub(crate) fn path(&self) -> &Path { + &self.path + } + + pub(crate) fn mode(&self) -> LockMode { + self.mode + } + pub(crate) fn is_current(&self) -> Result { + self.verifies(&self.path, self.mode) + } + pub(crate) fn retain(&mut self, companion: LockedFile) { + self.companions.push(companion); + } + + pub(crate) fn identity(&self) -> Result { + file_identity( + self.file + .as_ref() + .ok_or_else(|| NativeError::InvalidInput("released lock".into()))?, + ) + } + pub(crate) fn relock_shared(mut self) -> Result { let file = self.file.as_ref().ok_or_else(|| { NativeError::InvalidInput(format!( @@ -30,6 +97,152 @@ impl LockedFile { } } +// Fixed creation-generation header for persisted root cohort bindings. Native +// (device,inode) pairs can be recycled once all descriptors to a removed empty +// lock close; those numeric pairs alone do not identify a file's creation. +pub(crate) const COORDINATION_HEADER_BYTES: usize = 72; +#[cfg(windows)] +#[repr(align(8))] +struct NativeIoBytes([u8; N]); +const COORDINATION_HEADER_MAGIC: &[u8; 8] = b"CGCOOR02"; +pub(crate) fn encode_coordination_incarnation( + incarnation: [u8; 32], +) -> [u8; COORDINATION_HEADER_BYTES] { + use sha2::{Digest, Sha256}; + let mut bytes = [0; COORDINATION_HEADER_BYTES]; + bytes[..8].copy_from_slice(COORDINATION_HEADER_MAGIC); + bytes[8..40].copy_from_slice(&incarnation); + let checksum = Sha256::digest(&bytes[..40]); + bytes[40..].copy_from_slice(&checksum); + bytes +} +fn decode_coordination_incarnation(bytes: &[u8]) -> Option<[u8; 32]> { + use sha2::{Digest, Sha256}; + if bytes.len() != COORDINATION_HEADER_BYTES + || &bytes[..8] != COORDINATION_HEADER_MAGIC + || Sha256::digest(&bytes[..40]).as_slice() != &bytes[40..] + { + return None; + } + bytes[8..40].try_into().ok() +} +impl LockedFile { + /// Called only by the genuine fresh-root creator, or a future explicit + /// controlled-migration operation, while owning actual EXCLUSIVE exclusion. + /// Mere file emptiness, ordinary use and observational checks never call it. + pub(crate) fn initialize_creation_incarnation( + &self, + incarnation: [u8; 32], + ) -> Result<(), NativeError> { + use std::io::{Seek, SeekFrom, Write}; + if self.mode != LockMode::Exclusive || !self.is_current()? { + return Err(NativeError::InvalidInput( + "coordination_creation_not_exclusive".into(), + )); + } + let mut file = self + .file + .as_ref() + .ok_or_else(|| NativeError::InvalidInput("released lock".into()))?; + if file.metadata()?.len() != 0 { + return Err(NativeError::InvalidInput( + "coordination_creation_not_empty".into(), + )); + } + let bytes = encode_coordination_incarnation(incarnation); + file.seek(SeekFrom::Start(0))?; + file.write_all(&bytes)?; + file.sync_all()?; + if !self.is_current()? { + return Err(NativeError::InvalidInput( + "coordination_creation_changed".into(), + )); + } + Ok(()) + } + + /// Read actual held bytes without creating, repairing or using a shared + /// cursor. Empty/recreated/torn/unknown headers are unverified, not modern. + pub(crate) fn coordination_incarnation(&self) -> Result, NativeError> { + let file = self + .file + .as_ref() + .ok_or_else(|| NativeError::InvalidInput("released lock".into()))?; + read_coordination_incarnation(file) + } + + /// Windows LockFileEx layers locks rather than atomically converting them. + /// Fresh creation is durable before this gap and no producer effect has + /// started; reacquire Shared and revalidate the current stable inode. + pub(crate) fn share_after_creation(mut self) -> Result { + if self.mode != LockMode::Exclusive || !self.companions.is_empty() { + return Err(NativeError::InvalidInput( + "coordination_creation_mode_mismatch".into(), + )); + } + let file = self + .file + .as_ref() + .ok_or_else(|| NativeError::InvalidInput("released lock".into()))?; + #[cfg(windows)] + FileExt::unlock(file)?; + FileExt::lock_shared(file)?; + self.mode = LockMode::Shared; + if !self.is_current()? { + return Err(NativeError::InvalidInput( + "coordination_creation_changed".into(), + )); + } + Ok(self) + } +} +fn read_coordination_header( + file: &File, + bytes: &mut [u8; COORDINATION_HEADER_BYTES], +) -> Result { + if file.metadata()?.len() != COORDINATION_HEADER_BYTES as u64 { + return Ok(false); + } + #[cfg(unix)] + { + use std::os::unix::fs::FileExt; + file.read_exact_at(bytes, 0)?; + } + #[cfg(windows)] + { + use std::os::windows::fs::FileExt; + let mut read = 0; + while read < bytes.len() { + let count = file.seek_read(&mut bytes[read..], read as u64)?; + if count == 0 { + return Ok(false); + } + read += count; + } + } + #[cfg(not(any(unix, windows)))] + return Ok(false); + Ok(file.metadata()?.len() == COORDINATION_HEADER_BYTES as u64) +} +pub(crate) fn read_coordination_incarnation(file: &File) -> Result, NativeError> { + if file_link_count(file)? != 1 { + return Ok(None); + } + let mut bytes = [0; COORDINATION_HEADER_BYTES]; + match read_coordination_header(file, &mut bytes) { + Ok(true) => {} + Ok(false) => return Ok(None), + #[cfg(windows)] + Err(error) if error.raw_os_error() == Some(33) => { + // A live owner excludes data reads on another handle. Its durable + // birth nonce is also available as metadata without weakening locks. + return Ok(created_file_nonce(file)?); + } + Err(error) => return Err(error.into()), + } + Ok(decode_coordination_incarnation(&bytes)) +} + impl Drop for LockedFile { fn drop(&mut self) { if let Some(file) = self.file.take() { @@ -49,7 +262,7 @@ pub(crate) fn open_locked( path: impl AsRef, mode: LockMode, ) -> Result { - open_locked_inner(path.as_ref(), mode, false, true, true) + open_locked_inner(path.as_ref(), mode, false, true, true, false) } pub(crate) fn try_open_locked( @@ -66,20 +279,65 @@ pub(crate) fn try_open_locked_in_existing_parent( try_open_locked_inner(path.as_ref(), mode, false, true) } +/// Pure observation: never creates a missing parent or file. +pub(crate) fn try_open_existing_locked( + path: &Path, + mode: LockMode, +) -> Result, NativeError> { + try_open_locked_inner(path, mode, false, false) +} + +pub(crate) fn try_open_existing_shared_coordination( + path: &Path, +) -> Result, NativeError> { + match open_locked_inner(path, LockMode::Shared, true, false, false, true) { + Ok(lock) => Ok(Some(lock)), + Err(NativeError::Io(error)) if is_lock_contended(&error) => Ok(None), + Err(error) => Err(error), + } +} + +pub(crate) fn open_existing_shared_coordination(path: &Path) -> Result { + open_locked_inner(path, LockMode::Shared, false, false, false, true) +} +pub(crate) fn open_existing_exclusive(path: &Path) -> Result { + open_locked_inner(path, LockMode::Exclusive, false, false, false, false) +} + +/// Existing readable shared coordination requires no write permission. Create +/// normally only when absent, never recreate a detached parent. A competing +/// creator may publish a read-only file before our create/open; retry that +/// existing file read-only instead of imposing unrelated write permission. +pub(crate) fn open_shared_coordination(path: &Path) -> Result { + match open_locked_inner(path, LockMode::Shared, false, false, false, true) { + Ok(lease) => Ok(lease), + Err(NativeError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { + match open_locked_inner(path, LockMode::Shared, false, false, true, false) { + Ok(lease) => Ok(lease), + Err(create_error) => { + open_locked_inner(path, LockMode::Shared, false, false, false, true) + .or(Err(create_error)) + } + } + } + Err(error) => Err(error), + } +} + fn try_open_locked_inner( path: &Path, mode: LockMode, create_parent: bool, create_file: bool, ) -> Result, NativeError> { - match open_locked_inner(path, mode, true, create_parent, create_file) { + match open_locked_inner(path, mode, true, create_parent, create_file, false) { Ok(locked) => Ok(Some(locked)), Err(NativeError::Io(error)) if is_lock_contended(&error) => Ok(None), Err(other) => Err(other), } } -fn is_lock_contended(error: &std::io::Error) -> bool { +pub(crate) fn is_lock_contended(error: &std::io::Error) -> bool { let expected = lock_contended_error(); error.kind() == std::io::ErrorKind::WouldBlock || (error.raw_os_error().is_some() && error.raw_os_error() == expected.raw_os_error()) @@ -91,6 +349,7 @@ fn open_locked_inner( non_blocking: bool, create_parent: bool, create_file: bool, + read_only: bool, ) -> Result { if let Some(parent) = path.parent() { if create_parent { @@ -104,6 +363,8 @@ fn open_locked_inner( ))); } } + let parent = path.parent().unwrap_or_else(|| Path::new(".")); + let parent_identity = path_identity(parent)?; let metadata = match fs::symlink_metadata(path) { Ok(metadata) => Some(metadata), Err(error) if create_file && error.kind() == std::io::ErrorKind::NotFound => None, @@ -118,18 +379,2868 @@ fn open_locked_inner( let file = OpenOptions::new() .create(create_file) .read(true) - .write(true) + .write(!read_only) .truncate(false) .open(path)?; + if file_link_count(&file)? != 1 || file_identity(&file)? != path_identity(path)? { + return Err(NativeError::InvalidInput(format!( + "lock_identity_ambiguous: {}", + path.display() + ))); + } + #[cfg(test)] + BEFORE_LOCK_HOOK.with(|hook| { + if let Some(hook) = hook.borrow_mut().take() { + hook(); + } + }); match (mode, non_blocking) { (LockMode::Shared, false) => FileExt::lock_shared(&file)?, (LockMode::Exclusive, false) => FileExt::lock_exclusive(&file)?, (LockMode::Shared, true) => FileExt::try_lock_shared(&file)?, (LockMode::Exclusive, true) => FileExt::try_lock_exclusive(&file)?, } + if parent_identity != path_identity(parent)? + || file_identity(&file)? != path_identity(path)? + || file_link_count(&file)? != 1 + || !regular_unaliased(path)? + { + return Err(NativeError::InvalidInput(format!( + "lock_identity_changed: {}", + path.display() + ))); + } Ok(LockedFile { path: path.to_path_buf(), file: Some(file), mode, + companions: Vec::new(), }) } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub(crate) struct FileIdentity { + volume: u64, + index: u64, +} +impl FileIdentity { + /// Fixed persistence facts only; these words confer no ownership. + pub(crate) fn fixed_words(self) -> (u64, u64) { + (self.volume, self.index) + } + pub(crate) fn from_fixed_words(volume: u64, index: u64) -> Self { + Self { volume, index } + } +} + +pub(crate) fn path_identity(path: &Path) -> Result { + let metadata = fs::symlink_metadata(path)?; + if metadata.file_type().is_symlink() { + return Err(NativeError::InvalidInput(format!( + "symlink_identity: {}", + path.display() + ))); + } + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt; + Ok(FileIdentity { + volume: metadata.dev(), + index: metadata.ino(), + }) + } + #[cfg(windows)] + { + use std::os::windows::fs::OpenOptionsExt; + use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS; + let file = OpenOptions::new() + .read(true) + .custom_flags(FILE_FLAG_BACKUP_SEMANTICS) + .open(path)?; + file_identity(&file) + } + #[cfg(not(any(unix, windows)))] + Err(NativeError::InvalidInput( + "physical_identity_unavailable".into(), + )) +} + +pub(crate) fn file_identity(file: &File) -> Result { + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt; + let metadata = file.metadata()?; + Ok(FileIdentity { + volume: metadata.dev(), + index: metadata.ino(), + }) + } + #[cfg(windows)] + { + use std::os::windows::io::AsRawHandle; + use windows_sys::Win32::Storage::FileSystem::{ + GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION, + }; + let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() }; + if unsafe { GetFileInformationByHandle(file.as_raw_handle(), &mut info) } == 0 { + return Err(std::io::Error::last_os_error().into()); + } + Ok(FileIdentity { + volume: u64::from(info.dwVolumeSerialNumber), + index: (u64::from(info.nFileIndexHigh) << 32) | u64::from(info.nFileIndexLow), + }) + } + #[cfg(not(any(unix, windows)))] + Err(NativeError::InvalidInput( + "physical_identity_unavailable".into(), + )) +} + +pub(crate) fn file_link_count(file: &File) -> Result { + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt; + Ok(file.metadata()?.nlink()) + } + #[cfg(windows)] + { + use std::os::windows::io::AsRawHandle; + use windows_sys::Win32::Storage::FileSystem::{ + GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION, + }; + let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() }; + if unsafe { GetFileInformationByHandle(file.as_raw_handle(), &mut info) } == 0 { + return Err(std::io::Error::last_os_error().into()); + } + Ok(u64::from(info.nNumberOfLinks)) + } + #[cfg(not(any(unix, windows)))] + Err(NativeError::InvalidInput( + "physical_link_count_unavailable".into(), + )) +} + +fn regular_unaliased(path: &Path) -> Result { + let metadata = fs::symlink_metadata(path)?; + if !metadata.is_file() || metadata.file_type().is_symlink() { + return Ok(false); + } + let file = File::open(path)?; + Ok(file_identity(&file)? == path_identity(path)? && file_link_count(&file)? == 1) +} + +#[cfg(test)] +thread_local! { + static BEFORE_LOCK_HOOK: std::cell::RefCell>> = const { std::cell::RefCell::new(None) }; +} +#[cfg(test)] +pub(crate) fn before_lock_test_hook(hook: impl FnOnce() + 'static) { + BEFORE_LOCK_HOOK.with(|slot| *slot.borrow_mut() = Some(Box::new(hook))); +} + +/// Fixed facts only. Neither a stamp nor an open handle authorizes retirement. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum FixedControlKind { + File, + Directory, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum FixedControlProblem { + InvalidPath, + PathTooLong, + WrongKind, + Aliased, + Changed, + Oversized, + BufferTooSmall, + InvalidRecord, + EpochMismatch, + EpochExhausted, + GuardMismatch, + Busy, +} +#[derive(Debug)] +pub(crate) enum FixedControlError { + Io(std::io::Error), + Invalid(FixedControlProblem), + Resource(super::maintenance_budget::ResourceLimit), + Grant(super::maintenance_budget::GrantExhausted), +} +impl std::fmt::Display for FixedControlError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Io(error) => std::fmt::Display::fmt(error, f), + Self::Invalid(problem) => write!(f, "invalid cleanup control: {problem:?}"), + Self::Resource(limit) => write!(f, "cleanup resource limit: {limit:?}"), + Self::Grant(limit) => write!(f, "cleanup mutation allowance: {limit:?}"), + } + } +} +impl std::error::Error for FixedControlError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Io(error) => Some(error), + _ => None, + } + } +} +impl From for FixedControlError { + fn from(error: std::io::Error) -> Self { + Self::Io(error) + } +} +impl From for FixedControlError { + fn from(error: super::maintenance_budget::GrantExhausted) -> Self { + Self::Grant(error) + } +} + +/// The caller reserves this fixed native-path/metadata floor in the SAME pool +/// as its preparation buffers, before constructing the scratch. No PathBuf, +/// CString, wide Vec, formatted path/error or growable collection is created. +pub(crate) struct FixedControlScratch { + path: super::maintenance_budget::MemoryBox, +} +#[cfg(unix)] +type FixedControlPath = [u8; libc::PATH_MAX as usize]; +#[cfg(windows)] +type FixedControlPath = [u16; 32768]; +impl FixedControlScratch { + pub(crate) fn required_bytes() -> usize { + std::mem::size_of::() + std::mem::size_of::() + 4096 + } + pub(crate) fn new( + reservation: super::maintenance_budget::MemoryReservation, + ) -> Result { + if reservation.remaining() < Self::required_bytes() { + return Err(FixedControlError::Resource( + super::maintenance_budget::ResourceLimit::CeilingExceeded, + )); + } + let mut path = + super::maintenance_budget::MemoryBox::::new_uninit_reserved( + reservation, + ) + .map_err(FixedControlError::Resource)?; + // SAFETY: the exact aligned allocation is uniquely owned, and every + // element of either native character array accepts the all-zero value. + // Initialize in place so Windows never copies a 64 KiB temporary. + unsafe { path.as_mut_ptr().write_bytes(0, 1) }; + Ok(Self { + // SAFETY: every element was initialized above; ownership transfers once. + path: unsafe { path.assume_init() }, + }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct FixedFileStamp { + identity: FileIdentity, + kind: FixedControlKind, + len: u64, + links: u64, + modified: [i64; 2], + changed: [i64; 2], +} +impl FixedFileStamp { + pub(crate) fn identity(&self) -> FileIdentity { + self.identity + } + pub(crate) fn len(&self) -> u64 { + self.len + } + pub(crate) fn capture(file: &File, kind: FixedControlKind) -> Result { + #[cfg(unix)] + let stamp = { + use std::os::unix::fs::MetadataExt; + let m = file.metadata()?; + let actual = if m.is_file() { + FixedControlKind::File + } else if m.is_dir() { + FixedControlKind::Directory + } else { + return Err(FixedControlError::Invalid(FixedControlProblem::WrongKind)); + }; + Self { + identity: FileIdentity { + volume: m.dev(), + index: m.ino(), + }, + kind: actual, + len: m.len(), + links: m.nlink(), + modified: [m.mtime(), m.mtime_nsec()], + changed: [m.ctime(), m.ctime_nsec()], + } + }; + #[cfg(windows)] + let stamp = { + use std::os::windows::io::AsRawHandle; + use windows_sys::Win32::Storage::FileSystem::{ + FileBasicInfo, GetFileInformationByHandle, GetFileInformationByHandleEx, + GetFileType, BY_HANDLE_FILE_INFORMATION, FILE_ATTRIBUTE_DIRECTORY, + FILE_ATTRIBUTE_REPARSE_POINT, FILE_BASIC_INFO, FILE_TYPE_DISK, + }; + let handle = file.as_raw_handle(); + // SAFETY: File owns a live handle. Both output structs are native + // POD, correctly aligned/sized, and read only after successful APIs. + let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() }; + let mut basic: FILE_BASIC_INFO = unsafe { std::mem::zeroed() }; + if unsafe { GetFileType(handle) } != FILE_TYPE_DISK { + return Err(FixedControlError::Invalid(FixedControlProblem::WrongKind)); + } + if unsafe { GetFileInformationByHandle(handle, &mut info) } == 0 + || unsafe { + GetFileInformationByHandleEx( + handle, + FileBasicInfo, + (&mut basic as *mut FILE_BASIC_INFO).cast(), + std::mem::size_of::() as u32, + ) + } == 0 + { + return Err(std::io::Error::last_os_error().into()); + } + if info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 { + return Err(FixedControlError::Invalid(FixedControlProblem::Aliased)); + } + Self { + identity: FileIdentity { + volume: u64::from(info.dwVolumeSerialNumber), + index: (u64::from(info.nFileIndexHigh) << 32) | u64::from(info.nFileIndexLow), + }, + kind: if info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0 { + FixedControlKind::Directory + } else { + FixedControlKind::File + }, + len: (u64::from(info.nFileSizeHigh) << 32) | u64::from(info.nFileSizeLow), + links: u64::from(info.nNumberOfLinks), + modified: [basic.LastWriteTime, 0], + changed: [basic.ChangeTime, 0], + } + }; + #[cfg(not(any(unix, windows)))] + return Err(FixedControlError::Invalid(FixedControlProblem::WrongKind)); + #[cfg(any(unix, windows))] + { + if stamp.kind != kind { + return Err(FixedControlError::Invalid(FixedControlProblem::WrongKind)); + } + if kind == FixedControlKind::File && stamp.links != 1 { + return Err(FixedControlError::Invalid(FixedControlProblem::Aliased)); + } + Ok(stamp) + } + } + #[track_caller] + pub(crate) fn recheck(&self, file: &File) -> Result<(), FixedControlError> { + let actual = Self::capture(file, self.kind)?; + if actual != *self { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "cleanup stamp changed at {}: expected {self:?}, actual {actual:?}", + std::panic::Location::caller() + ); + } + return Err(FixedControlError::Invalid(FixedControlProblem::Changed)); + } + Ok(()) + } +} + +/// Existing-only, no-follow leaf open. The absolute address remains borrowed; +/// no process CWD lookup, file/parent creation, repair or retirement occurs. +pub(crate) fn open_fixed_control( + path: &Path, + kind: FixedControlKind, + write: bool, + scratch: &mut FixedControlScratch, +) -> Result { + if !path.is_absolute() { + return Err(FixedControlError::Invalid(FixedControlProblem::InvalidPath)); + } + #[cfg(unix)] + { + use std::os::fd::FromRawFd; + let bytes = path.as_os_str().as_encoded_bytes(); + if bytes.contains(&0) { + return Err(FixedControlError::Invalid(FixedControlProblem::InvalidPath)); + } + if bytes.len() >= scratch.path.len() { + return Err(FixedControlError::Invalid(FixedControlProblem::PathTooLong)); + } + scratch.path[..bytes.len()].copy_from_slice(bytes); + scratch.path[bytes.len()] = 0; + let pointer = scratch.path.as_ptr().cast(); + // SAFETY: the caller-reserved array contains one NUL-terminated native + // path and stays borrowed through these calls; stat is writable native + // POD. No create flags/mode argument or symlink-following open is used. + let mut before: libc::stat = unsafe { std::mem::zeroed() }; + if unsafe { libc::lstat(pointer, &mut before) } != 0 { + return Err(std::io::Error::last_os_error().into()); + } + let expected_type = match kind { + FixedControlKind::File => libc::S_IFREG, + FixedControlKind::Directory => libc::S_IFDIR, + }; + if before.st_mode & libc::S_IFMT == libc::S_IFLNK { + return Err(FixedControlError::Invalid(FixedControlProblem::Aliased)); + } + if before.st_mode & libc::S_IFMT != expected_type { + return Err(FixedControlError::Invalid(FixedControlProblem::WrongKind)); + } + let flags = (if write && kind == FixedControlKind::File { + libc::O_RDWR + } else { + libc::O_RDONLY + }) | libc::O_CLOEXEC + | libc::O_NOFOLLOW + | libc::O_NONBLOCK; + let fd = unsafe { libc::open(pointer, flags) }; + if fd < 0 { + return Err(std::io::Error::last_os_error().into()); + } + // SAFETY: successful open returned a new uniquely owned descriptor; + // File now closes it on every success/error/unwind path. + let file = unsafe { File::from_raw_fd(fd) }; + let mut opened: libc::stat = unsafe { std::mem::zeroed() }; + if unsafe { libc::fstat(fd, &mut opened) } != 0 { + return Err(std::io::Error::last_os_error().into()); + } + let mut after: libc::stat = unsafe { std::mem::zeroed() }; + if unsafe { libc::lstat(pointer, &mut after) } != 0 { + return Err(std::io::Error::last_os_error().into()); + } + if kind == FixedControlKind::File && (opened.st_nlink != 1 || after.st_nlink != 1) { + return Err(FixedControlError::Invalid(FixedControlProblem::Aliased)); + } + if opened.st_dev != before.st_dev + || opened.st_ino != before.st_ino + || opened.st_dev != after.st_dev + || opened.st_ino != after.st_ino + || opened.st_mode & libc::S_IFMT != expected_type + || after.st_mode & libc::S_IFMT != expected_type + { + return Err(FixedControlError::Invalid(FixedControlProblem::Changed)); + } + Ok(file) + } + #[cfg(windows)] + { + use std::os::windows::{ffi::OsStrExt, io::FromRawHandle}; + use windows_sys::Win32::{ + Foundation::{GENERIC_READ, GENERIC_WRITE, INVALID_HANDLE_VALUE}, + Storage::FileSystem::{ + CreateFileW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, + FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, + OPEN_EXISTING, + }, + }; + let mut len = 0; + for unit in path.as_os_str().encode_wide() { + if unit == 0 { + return Err(FixedControlError::Invalid(FixedControlProblem::InvalidPath)); + } + if len + 1 >= scratch.path.len() { + return Err(FixedControlError::Invalid(FixedControlProblem::PathTooLong)); + } + scratch.path[len] = unit; + len += 1; + } + scratch.path[len] = 0; + let access = if kind == FixedControlKind::Directory { + use windows_sys::Win32::Storage::FileSystem::{ + FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY, FILE_LIST_DIRECTORY, FILE_WRITE_ATTRIBUTES, + SYNCHRONIZE, + }; + FILE_READ_ATTRIBUTES + | FILE_LIST_DIRECTORY + | SYNCHRONIZE + | if write { + FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_WRITE_ATTRIBUTES + } else { + 0 + } + } else { + GENERIC_READ | if write { GENERIC_WRITE } else { 0 } + }; + // SAFETY: the fixed aligned UTF-16 array is NUL-terminated and lives + // through the call. OPEN_EXISTING creates nothing; the returned unique + // handle is adopted below only after checking INVALID_HANDLE_VALUE. + let handle = unsafe { + CreateFileW( + scratch.path.as_ptr(), + access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + std::ptr::null(), + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + std::ptr::null_mut(), + ) + }; + if handle == INVALID_HANDLE_VALUE { + return Err(std::io::Error::last_os_error().into()); + } + // SAFETY: this is the successful uniquely owned CreateFileW result. + let file = unsafe { File::from_raw_handle(handle) }; + FixedFileStamp::capture(&file, kind)?; + Ok(file) + } + #[cfg(not(any(unix, windows)))] + Err(FixedControlError::Invalid(FixedControlProblem::WrongKind)) +} + +/// Read at most the caller-reserved capacity, including an EOF check, and +/// reject in-place metadata changes. Address recheck uses a fresh fixed open; +/// retaining an old FD alone is not evidence about its current public name. +pub(crate) fn read_fixed_control( + file: &mut File, + stamp: &FixedFileStamp, + buffer: &mut super::maintenance_budget::ReservedBytes, +) -> Result { + use std::io::{Read, Seek, SeekFrom}; + stamp.recheck(file)?; + let len = usize::try_from(stamp.len) + .map_err(|_| FixedControlError::Invalid(FixedControlProblem::Oversized))?; + if len > buffer.as_slice().len() { + return Err(FixedControlError::Invalid(FixedControlProblem::Oversized)); + } + file.seek(SeekFrom::Start(0))?; + file.read_exact(&mut buffer.as_mut_slice()[..len])?; + let mut extra = [0; 1]; + if file.read(&mut extra)? != 0 { + return Err(FixedControlError::Invalid(FixedControlProblem::Changed)); + } + stamp.recheck(file)?; + Ok(len) +} + +/// Path-free fixed authority; cannot cross threads or survive as Send facts. +/// The handle is closed on every failed acquisition, error and unwind. +pub(crate) struct FixedLockedFile { + file: File, + mode: LockMode, + _same_thread: std::marker::PhantomData>, +} +impl FixedLockedFile { + pub(crate) fn try_lock(file: File, mode: LockMode) -> Result, FixedControlError> { + let result = match mode { + LockMode::Shared => FileExt::try_lock_shared(&file), + LockMode::Exclusive => FileExt::try_lock_exclusive(&file), + }; + match result { + Ok(()) => Ok(Some(Self { + file, + mode, + _same_thread: std::marker::PhantomData, + })), + Err(error) if is_lock_contended(&error) => { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "cleanup control lock contended: identity={:?} mode={}", + file_identity(&file), + if mode == LockMode::Exclusive { + "exclusive" + } else { + "shared" + } + ); + } + Ok(None) + } + Err(error) => Err(error.into()), + } + } + pub(crate) fn file(&self) -> &File { + &self.file + } + pub(crate) fn mode(&self) -> LockMode { + self.mode + } +} +impl Drop for FixedLockedFile { + fn drop(&mut self) { + let _ = FileExt::unlock(&self.file); + } +} +impl LockedFile { + /// Read through the ACTUAL lock-owning handle. Windows byte-range locks + /// would reject reading the same file via a second handle while Exclusive. + /// Expected bytes were encoded outside authority; no hashing occurs here. + #[cfg(test)] + pub(crate) fn matches_fixed_header( + &self, + expected: &[u8; COORDINATION_HEADER_BYTES], + ) -> Result { + let Some(file) = self.file.as_ref() else { + return Ok(false); + }; + let stamp = FixedFileStamp::capture(file, FixedControlKind::File)?; + let mut bytes = [0; COORDINATION_HEADER_BYTES]; + if !read_coordination_header(file, &mut bytes)? { + return Ok(false); + } + stamp.recheck(file)?; + Ok(bytes == *expected) + } + + pub(crate) fn matches_fixed_current( + &self, + current: &FixedFileStamp, + mode: LockMode, + ) -> Result { + let Some(file) = self.file.as_ref() else { + return Ok(false); + }; + Ok(self.mode == mode + && FixedFileStamp::capture(file, FixedControlKind::File)?.identity == current.identity) + } +} + +#[cfg(test)] +mod fixed_control_tests { + use super::*; + use crate::storage::maintenance_budget::CleanupMemory; + use std::io::Write; + + #[test] + fn dc_control_scratch_uses_small_worker_frame_and_retains_reserved_path_storage() { + // Exercise the shipping worker's stack limit, including the 64 KiB + // native Windows path buffer, without constructing it on that stack. + std::thread::Builder::new() + .stack_size(256 * 1024) + .spawn(|| { + assert!(std::mem::size_of::() <= 64); + let memory = CleanupMemory::new(128 * 1024).unwrap(); + let before = memory.used(); + let charge = FixedControlScratch::required_bytes(); + let mut scratch = + FixedControlScratch::new(memory.reserve(charge).unwrap()).unwrap(); + assert_eq!(memory.used(), before + charge); + assert!(scratch.path.iter().all(|unit| *unit == 0)); + let last = scratch.path.len() - 1; + scratch.path[last] = 1; + assert_eq!(scratch.path[last], 1); + drop(scratch); + assert_eq!(memory.used(), before); + assert!(FixedControlScratch::new(memory.reserve(charge - 1).unwrap()).is_err()); + assert_eq!(memory.used(), before); + }) + .unwrap() + .join() + .unwrap(); + } + + struct Fixture { + root: PathBuf, + memory: CleanupMemory, + } + impl Fixture { + fn new() -> Self { + let root = std::env::temp_dir().join(format!( + "fixed-control-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + fs::create_dir(&root).unwrap(); + Self { + root, + memory: CleanupMemory::new(1024 * 1024).unwrap(), + } + } + fn scratch(&self) -> FixedControlScratch { + FixedControlScratch::new( + self.memory + .reserve(FixedControlScratch::required_bytes()) + .unwrap(), + ) + .unwrap() + } + } + impl Drop for Fixture { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.root); + } + } + + #[test] + fn dc_coordination_nonce_initializes_only_exclusive_empty_creation_and_is_immutable() { + let f = Fixture::new(); + let path = f.root.join("nonce-lock"); + let shared = open_locked(&path, LockMode::Shared).unwrap(); + assert!(shared.initialize_creation_incarnation([1; 32]).is_err()); + assert!(fs::read(&path).unwrap().is_empty()); + drop(shared); + let exclusive = open_locked(&path, LockMode::Exclusive).unwrap(); + exclusive.initialize_creation_incarnation([1; 32]).unwrap(); + let mut before = [0; COORDINATION_HEADER_BYTES]; + assert!(read_coordination_header(exclusive.file.as_ref().unwrap(), &mut before).unwrap()); + assert_eq!(exclusive.coordination_incarnation().unwrap(), Some([1; 32])); + assert!(exclusive.initialize_creation_incarnation([2; 32]).is_err()); + let mut after = [0; COORDINATION_HEADER_BYTES]; + assert!(read_coordination_header(exclusive.file.as_ref().unwrap(), &mut after).unwrap()); + assert_eq!(after, before); + let shared = exclusive.share_after_creation().unwrap(); + assert!(try_open_existing_locked(&path, LockMode::Shared) + .unwrap() + .is_some()); + assert_eq!(shared.coordination_incarnation().unwrap(), Some([1; 32])); + } + + #[test] + fn dc_fixed_stamp_open_read_and_recheck_are_noncreating_and_bounded() { + let f = Fixture::new(); + let mut scratch = f.scratch(); + let missing = f.root.join("missing"); + match open_fixed_control(&missing, FixedControlKind::File, true, &mut scratch) { + Err(FixedControlError::Io(error)) => { + assert_eq!(error.kind(), std::io::ErrorKind::NotFound) + } + other => panic!("expected typed NotFound, got {other:?}"), + } + assert!(!missing.exists()); + let path = f.root.join("control"); + fs::write(&path, b"four").unwrap(); + let mut file = + open_fixed_control(&path, FixedControlKind::File, false, &mut scratch).unwrap(); + let stamp = FixedFileStamp::capture(&file, FixedControlKind::File).unwrap(); + let mut bytes = f.memory.reserve(4).unwrap().bytes(4).unwrap(); + assert_eq!( + read_fixed_control(&mut file, &stamp, &mut bytes).unwrap(), + 4 + ); + assert_eq!(bytes.as_slice(), b"four"); + let mut too_small = f.memory.reserve(3).unwrap().bytes(3).unwrap(); + assert!(matches!( + read_fixed_control(&mut file, &stamp, &mut too_small), + Err(FixedControlError::Invalid(FixedControlProblem::Oversized)) + )); + assert_eq!(fs::read(&path).unwrap(), b"four"); + assert_eq!(fs::read_dir(&f.root).unwrap().count(), 1); + } + + #[test] + fn dc_fixed_stamp_rejects_in_place_change_and_new_public_incarnation() { + let f = Fixture::new(); + let path = f.root.join("control"); + fs::write(&path, b"before").unwrap(); + let mut scratch = f.scratch(); + let file = open_fixed_control(&path, FixedControlKind::File, false, &mut scratch).unwrap(); + let stamp = FixedFileStamp::capture(&file, FixedControlKind::File).unwrap(); + let mut writer = OpenOptions::new().write(true).open(&path).unwrap(); + writer.write_all(b"longer-after").unwrap(); + writer.sync_all().unwrap(); + assert!(stamp.recheck(&file).is_err()); + fs::rename(&path, f.root.join("old-control")).unwrap(); + fs::write(&path, b"replacement").unwrap(); + let current = + open_fixed_control(&path, FixedControlKind::File, false, &mut scratch).unwrap(); + assert_ne!( + stamp.identity(), + FixedFileStamp::capture(¤t, FixedControlKind::File) + .unwrap() + .identity() + ); + assert!(stamp.recheck(¤t).is_err()); + } + + #[cfg(unix)] + #[test] + fn dc_fixed_stamp_rejects_symlinks_hardlinks_and_nonregular_controls() { + let f = Fixture::new(); + let path = f.root.join("control"); + fs::write(&path, b"data").unwrap(); + let alias = f.root.join("alias"); + std::os::unix::fs::symlink(&path, &alias).unwrap(); + let mut scratch = f.scratch(); + assert!(matches!( + open_fixed_control(&alias, FixedControlKind::File, false, &mut scratch), + Err(FixedControlError::Invalid(FixedControlProblem::Aliased)) + )); + fs::remove_file(&alias).unwrap(); + fs::hard_link(&path, &alias).unwrap(); + assert!(matches!( + open_fixed_control(&path, FixedControlKind::File, false, &mut scratch), + Err(FixedControlError::Invalid(FixedControlProblem::Aliased)) + )); + assert!(matches!( + open_fixed_control(&f.root, FixedControlKind::File, false, &mut scratch), + Err(FixedControlError::Invalid(FixedControlProblem::WrongKind)) + )); + } + + #[test] + fn dc_fixed_stamp_same_length_write_with_restored_mtime_stays_changed() { + let f = Fixture::new(); + let path = f.root.join("control"); + fs::write(&path, b"before").unwrap(); + let mut scratch = f.scratch(); + let file = open_fixed_control(&path, FixedControlKind::File, false, &mut scratch).unwrap(); + let stamp = FixedFileStamp::capture(&file, FixedControlKind::File).unwrap(); + let modified = file.metadata().unwrap().modified().unwrap(); + std::thread::sleep(std::time::Duration::from_millis(2)); + let mut writer = OpenOptions::new().write(true).open(&path).unwrap(); + writer.write_all(b"after!").unwrap(); + writer.sync_all().unwrap(); + writer + .set_times(std::fs::FileTimes::new().set_modified(modified)) + .unwrap(); + let changed = FixedFileStamp::capture(&file, FixedControlKind::File).unwrap(); + assert_eq!(stamp.len, changed.len); + assert_eq!(stamp.modified, changed.modified); + assert_ne!(stamp.changed, changed.changed); + assert!(stamp.recheck(&file).is_err()); + } + + #[test] + fn dc_fixed_stamp_scratch_uses_shared_reservation_and_releases_it() { + let memory = CleanupMemory::new(FixedControlScratch::required_bytes() + 128).unwrap(); + let initial = memory.used(); + let scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .unwrap(), + ) + .unwrap(); + assert!(memory + .reserve(FixedControlScratch::required_bytes()) + .is_err()); + drop(scratch); + assert_eq!(memory.used(), initial); + match FixedControlScratch::new(memory.reserve(1).unwrap()) { + Err(FixedControlError::Resource(limit)) => assert_eq!( + limit, + super::super::maintenance_budget::ResourceLimit::CeilingExceeded + ), + _ => panic!("expected typed scratch resource limit"), + } + assert_eq!(memory.used(), initial); + } + + #[test] + fn dc_fixed_stamp_facts_cross_threads_but_actual_authority_cannot() { + fn send_sync() {} + send_sync::(); + send_sync::(); + trait AmbiguousIfSend { + fn check() {} + } + impl AmbiguousIfSend<()> for T {} + impl AmbiguousIfSend for T {} + let _ = >::check; + trait AmbiguousIfSync { + fn check() {} + } + impl AmbiguousIfSync<()> for T {} + impl AmbiguousIfSync for T {} + let _ = >::check; + } + + #[test] + fn dc_fixed_stamp_path_free_lock_release_and_identity_composition() { + let f = Fixture::new(); + let path = f.root.join("control"); + fs::write(&path, b"").unwrap(); + let mut scratch = f.scratch(); + let file = open_fixed_control(&path, FixedControlKind::File, true, &mut scratch).unwrap(); + let owned = FixedLockedFile::try_lock(file, LockMode::Exclusive) + .unwrap() + .unwrap(); + assert_eq!(owned.mode(), LockMode::Exclusive); + assert!(try_open_existing_locked(&path, LockMode::Exclusive) + .unwrap() + .is_none()); + drop(owned); + let caller = try_open_existing_locked(&path, LockMode::Exclusive) + .unwrap() + .unwrap(); + let current = + open_fixed_control(&path, FixedControlKind::File, false, &mut scratch).unwrap(); + let stamp = FixedFileStamp::capture(¤t, FixedControlKind::File).unwrap(); + assert!(caller + .matches_fixed_current(&stamp, LockMode::Exclusive) + .unwrap()); + assert!(!caller + .matches_fixed_current(&stamp, LockMode::Shared) + .unwrap()); + } +} + +/// Minted only by this module's successful mkfifoat event. +#[cfg(unix)] +pub(crate) struct CreatedContainmentFifo { + file: File, +} +#[cfg(unix)] +impl CreatedContainmentFifo { + pub(crate) fn file(&self) -> &File { + &self.file + } +} + +/// Minted only after the fresh FIFO hardlink and both namespace flushes. +#[cfg(unix)] +pub(crate) struct CreatedFifoPin { + file: File, +} +#[cfg(unix)] +impl CreatedFifoPin { + pub(crate) fn identity(&self) -> Result { + file_identity(&self.file) + } +} + +/// Actual directory anchor for fixed-name creation. Relative native operations +/// address this retained object even if a public ancestor/name is substituted. +/// Opening/preparing the anchor belongs outside a cleanup execution permit. +pub(crate) struct RootedDirectory { + directory: File, + identity: FileIdentity, + _same_thread: std::marker::PhantomData>, +} +impl RootedDirectory { + /// Nonlocking pin of the same actual directory incarnation. Native-use + /// leases retain this handle without exporting namespace mutation authority. + pub(crate) fn pin_handle(&self) -> std::io::Result { + self.directory.try_clone() + } + pub(crate) fn open(path: &Path, expected: FileIdentity) -> std::io::Result { + Self::open_root(path, expected, true) + } + pub(crate) fn open_readonly_root(path: &Path, expected: FileIdentity) -> std::io::Result { + Self::open_root(path, expected, false) + } + fn open_root(path: &Path, expected: FileIdentity, write: bool) -> std::io::Result { + #[cfg(not(windows))] + let _ = write; + let mut options = OpenOptions::new(); + options.read(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.custom_flags(libc::O_DIRECTORY | libc::O_NOFOLLOW | libc::O_CLOEXEC); + } + #[cfg(windows)] + { + use std::os::windows::fs::OpenOptionsExt; + use windows_sys::Win32::Storage::FileSystem::{ + FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY, FILE_FLAG_BACKUP_SEMANTICS, + FILE_FLAG_OPEN_REPARSE_POINT, FILE_LIST_DIRECTORY, FILE_READ_ATTRIBUTES, + FILE_WRITE_ATTRIBUTES, SYNCHRONIZE, + }; + options + .access_mode( + FILE_LIST_DIRECTORY + | FILE_READ_ATTRIBUTES + | SYNCHRONIZE + | if write { + FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_WRITE_ATTRIBUTES + } else { + 0 + }, + ) + .custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); + } + let directory = options.open(path)?; + let metadata = directory.metadata()?; + if !metadata.is_dir() || metadata.file_type().is_symlink() { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + #[cfg(windows)] + { + use std::os::windows::fs::MetadataExt; + if metadata.file_attributes() + & windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT + != 0 + { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + } + let identity = file_identity(&directory).map_err(native_io)?; + if identity != expected { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + Ok(Self { + directory, + identity, + _same_thread: std::marker::PhantomData, + }) + } + pub(crate) fn identity(&self) -> FileIdentity { + self.identity + } + pub(crate) fn create_new(&self, name: &[u8]) -> std::io::Result { + self.child(name, true) + } + pub(crate) fn open_existing(&self, name: &[u8]) -> std::io::Result { + self.child(name, false) + } + fn child(&self, name: &[u8], create: bool) -> std::io::Result { + self.relative_handle(name, create, false, false, true) + } + pub(crate) fn open_readonly_file(&self, name: &[u8]) -> std::io::Result { + self.relative_handle(name, false, false, false, false) + } + /// Query the named inode without requesting data access. Windows permits + /// this while its exclusive creator still denies read/write/delete sharing. + pub(crate) fn open_metadata_file(&self, name: &[u8]) -> std::io::Result { + self.relative_handle_access(name, false, false, false, false, true) + } + pub(crate) fn open_directory_readonly(&self, name: &[u8]) -> std::io::Result { + let directory = self.relative_handle(name, false, true, false, false)?; + let identity = file_identity(&directory).map_err(native_io)?; + Ok(Self { + directory, + identity, + _same_thread: std::marker::PhantomData, + }) + } + pub(crate) fn create_directory(&self, name: &[u8]) -> std::io::Result { + self.child_directory(name, true) + } + pub(crate) fn open_directory(&self, name: &[u8]) -> std::io::Result { + self.child_directory(name, false) + } + fn child_directory(&self, name: &[u8], create: bool) -> std::io::Result { + let directory = self.relative_handle(name, create, true, false, true)?; + let identity = file_identity(&directory).map_err(native_io)?; + Ok(Self { + directory, + identity, + _same_thread: std::marker::PhantomData, + }) + } + fn relative_handle( + &self, + name: &[u8], + create: bool, + directory: bool, + delete: bool, + write: bool, + ) -> std::io::Result { + self.relative_handle_access(name, create, directory, delete, write, false) + } + fn relative_handle_access( + &self, + name: &[u8], + create: bool, + directory: bool, + delete: bool, + write: bool, + metadata_only: bool, + ) -> std::io::Result { + if name.is_empty() + || name.len() >= 256 + || name == b"." + || name == b".." + || name + .iter() + .any(|byte| !byte.is_ascii() || matches!(*byte, b'/' | b'\\' | b':' | 0)) + { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + #[cfg(unix)] + let file = { + let _ = metadata_only; + use std::os::fd::{AsRawFd, FromRawFd}; + let mut native = [0u8; 256]; + native[..name.len()].copy_from_slice(name); + if create && directory { + // SAFETY: retained actual parent FD and one validated basename. + if unsafe { + libc::mkdirat(self.directory.as_raw_fd(), native.as_ptr().cast(), 0o700) + } != 0 + { + return Err(std::io::Error::last_os_error()); + } + } + let _ = delete; + let flags = if directory { + libc::O_RDONLY | libc::O_DIRECTORY + } else if write { + libc::O_RDWR + } else { + libc::O_RDONLY + } | libc::O_NOFOLLOW + | libc::O_CLOEXEC + | libc::O_NONBLOCK + | if create && !directory { + libc::O_CREAT | libc::O_EXCL + } else { + 0 + }; + // SAFETY: actual directory FD is retained; native name is one + // validated NUL-terminated basename with no separators/traversal. + let fd = unsafe { + libc::openat( + self.directory.as_raw_fd(), + native.as_ptr().cast(), + flags, + 0o600, + ) + }; + if fd < 0 { + return Err(std::io::Error::last_os_error()); + } + // SAFETY: successful openat returned a uniquely owned descriptor. + unsafe { File::from_raw_fd(fd) } + }; + #[cfg(windows)] + let file = { + use std::os::windows::io::{AsRawHandle, FromRawHandle}; + use windows_sys::{ + Wdk::{ + Foundation::OBJECT_ATTRIBUTES, + Storage::FileSystem::{ + NtCreateFile, FILE_CREATE, FILE_DIRECTORY_FILE, FILE_NON_DIRECTORY_FILE, + FILE_OPEN, FILE_OPEN_REPARSE_POINT, FILE_SYNCHRONOUS_IO_NONALERT, + FILE_WRITE_THROUGH, + }, + }, + Win32::{ + Foundation::{ + RtlNtStatusToDosError, GENERIC_READ, GENERIC_WRITE, OBJ_CASE_INSENSITIVE, + UNICODE_STRING, + }, + Storage::FileSystem::{ + DELETE, FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY, FILE_ATTRIBUTE_NORMAL, + FILE_LIST_DIRECTORY, FILE_READ_ATTRIBUTES, FILE_READ_EA, FILE_SHARE_DELETE, + FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, + SYNCHRONIZE, + }, + System::IO::IO_STATUS_BLOCK, + }, + }; + let mut native = [0u16; 256]; + for (out, byte) in native.iter_mut().zip(name) { + *out = u16::from(*byte); + } + let mut unicode = UNICODE_STRING { + Length: (name.len() * 2) as u16, + MaximumLength: (name.len() * 2) as u16, + Buffer: native.as_mut_ptr(), + }; + let attributes = OBJECT_ATTRIBUTES { + Length: std::mem::size_of::() as u32, + RootDirectory: self.directory.as_raw_handle(), + ObjectName: &mut unicode, + Attributes: OBJ_CASE_INSENSITIVE, + SecurityDescriptor: std::ptr::null_mut(), + SecurityQualityOfService: std::ptr::null_mut(), + }; + let mut status: IO_STATUS_BLOCK = unsafe { std::mem::zeroed() }; + let mut handle = std::ptr::null_mut(); + // SAFETY: all pointers refer to fixed initialized stack records; + // RootDirectory is the live actual anchor, not a joined pathname. + let result = unsafe { + NtCreateFile( + &mut handle, + (if directory { + FILE_LIST_DIRECTORY + | FILE_READ_ATTRIBUTES + | FILE_READ_EA + | if write { + FILE_ADD_FILE + | FILE_ADD_SUBDIRECTORY + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + } else { + 0 + } + } else if metadata_only { + FILE_READ_ATTRIBUTES + } else { + GENERIC_READ | if write { GENERIC_WRITE } else { 0 } + }) | SYNCHRONIZE + | if delete { DELETE } else { 0 }, + &attributes, + &mut status, + std::ptr::null(), + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + if create { FILE_CREATE } else { FILE_OPEN }, + (if directory { + FILE_DIRECTORY_FILE + } else { + FILE_NON_DIRECTORY_FILE + }) | FILE_OPEN_REPARSE_POINT + | FILE_SYNCHRONOUS_IO_NONALERT + | FILE_WRITE_THROUGH, + std::ptr::null(), + 0, + ) + }; + if result < 0 { + return Err(std::io::Error::from_raw_os_error(unsafe { + RtlNtStatusToDosError(result) + } as i32)); + } + // SAFETY: successful NtCreateFile returned one owned file handle. + unsafe { File::from_raw_handle(handle) } + }; + #[cfg(not(any(unix, windows)))] + return Err(std::io::ErrorKind::Unsupported.into()); + let metadata = file.metadata()?; + if (directory && !metadata.is_dir()) + || (!directory + && (!metadata.is_file() || file_link_count(&file).map_err(native_io)? != 1)) + { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + #[cfg(windows)] + { + use std::os::windows::fs::MetadataExt; + if file.metadata()?.file_attributes() + & windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT + != 0 + { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + } + Ok(file) + } + #[cfg(windows)] + fn reopen_pinned_directory(&self, access: u32) -> std::io::Result { + use std::os::windows::io::{AsRawHandle, FromRawHandle}; + use windows_sys::Win32::{ + Foundation::INVALID_HANDLE_VALUE, + Storage::FileSystem::{ + FileIdType, OpenFileById, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, + FILE_ID_DESCRIPTOR, FILE_ID_DESCRIPTOR_0, FILE_SHARE_DELETE, FILE_SHARE_READ, + FILE_SHARE_WRITE, + }, + }; + let id = FILE_ID_DESCRIPTOR { + dwSize: std::mem::size_of::() as u32, + Type: FileIdType, + Anonymous: FILE_ID_DESCRIPTOR_0 { + FileId: self.identity.index as i64, + }, + }; + // SAFETY: the live pin prevents ID reuse; its own volume supplies + // the hint. The descriptor is fixed-size and fully initialized. + let handle = unsafe { + OpenFileById( + self.directory.as_raw_handle(), + &id, + access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + std::ptr::null(), + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + ) + }; + if handle == INVALID_HANDLE_VALUE { + let error = std::io::Error::last_os_error(); + #[cfg(test)] + eprintln!("namespace reopen failed: {error:?}"); + return Err(error); + } + // SAFETY: successful OpenFileById returned one owned handle. + let reopened = unsafe { File::from_raw_handle(handle) }; + if file_identity(&reopened).map_err(native_io)? != self.identity { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + Ok(reopened) + } + pub(crate) fn sync_namespace(&self) -> std::io::Result<()> { + #[cfg(not(windows))] + { + self.directory.sync_all() + } + #[cfg(windows)] + { + use std::os::windows::io::AsRawHandle; + use windows_sys::{ + Wdk::Storage::FileSystem::NtFlushBuffersFileEx, + Win32::{ + Foundation::{RtlNtStatusToDosError, GENERIC_WRITE}, + Storage::FileSystem::FILE_READ_ATTRIBUTES, + System::IO::IO_STATUS_BLOCK, + }, + }; + // Flush requires write access even when the directory was pinned + // for observation. Reopen the exact object, never its pathname. + let writable = self.reopen_pinned_directory(GENERIC_WRITE | FILE_READ_ATTRIBUTES)?; + let mut status: IO_STATUS_BLOCK = unsafe { std::mem::zeroed() }; + // SAFETY: live actual directory handle and initialized output. A + // denied/unsupported filesystem flush fails closed for attestation. + let result = unsafe { + NtFlushBuffersFileEx( + writable.as_raw_handle(), + 0, + std::ptr::null(), + 0, + &mut status, + ) + }; + if result < 0 { + #[cfg(test)] + eprintln!("namespace flush failed: {result:#x}"); + Err(std::io::Error::from_raw_os_error( + unsafe { RtlNtStatusToDosError(result) } as i32, + )) + } else { + Ok(()) + } + } + } +} +fn native_io(error: NativeError) -> std::io::Error { + match error { + NativeError::Io(error) => error, + _ => std::io::ErrorKind::InvalidInput.into(), + } +} + +fn checked_relative_name(name: &[u8]) -> std::io::Result<[u8; 256]> { + if name.is_empty() + || name.len() >= 256 + || name == b"." + || name == b".." + || name + .iter() + .any(|byte| !byte.is_ascii() || matches!(*byte, b'/' | b'\\' | b':' | 0)) + { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + let mut native = [0; 256]; + native[..name.len()].copy_from_slice(name); + Ok(native) +} +#[cfg(unix)] +fn checked_fifo_name(name: &[u8]) -> std::io::Result<[u8; 256]> { + if name != b"writers.fifo" + && !(name.len() == 69 + && &name[..5] == b"fifo-" + && name[5..] + .iter() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(b))) + { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + checked_relative_name(name) +} +impl RootedDirectory { + /// Low-level entry operation only. Caller retains actual namespace ownership + /// and the exact source's creation/origin proof; this helper grants neither. + pub(crate) fn rename_file_no_replace( + &self, + name: &[u8], + destination: &Self, + target: &[u8], + expected: FileIdentity, + ) -> std::io::Result<()> { + self.rename_no_replace(name, destination, target, expected, false) + } + pub(crate) fn rename_directory_no_replace( + &self, + name: &[u8], + destination: &Self, + target: &[u8], + expected: FileIdentity, + ) -> std::io::Result<()> { + self.rename_no_replace(name, destination, target, expected, true) + } + fn rename_no_replace( + &self, + name: &[u8], + destination: &Self, + target: &[u8], + expected: FileIdentity, + directory: bool, + ) -> std::io::Result<()> { + let source_name = checked_relative_name(name)?; + let target_name = checked_relative_name(target)?; + let source = self.relative_handle(name, false, directory, true, false)?; + if file_identity(&source).map_err(native_io)? != expected { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + #[cfg(unix)] + { + use std::os::fd::AsRawFd; + #[cfg(any(target_os = "macos", target_os = "ios"))] + // SAFETY: both retained directory FDs and fixed NUL basenames live + // through the native atomic exclusive-destination rename. + let result = unsafe { + libc::renameatx_np( + self.directory.as_raw_fd(), + source_name.as_ptr().cast(), + destination.directory.as_raw_fd(), + target_name.as_ptr().cast(), + libc::RENAME_EXCL, + ) + }; + #[cfg(target_os = "linux")] + // SAFETY: same anchored arguments; RENAME_NOREPLACE never overwrites. + let result = unsafe { + libc::renameat2( + self.directory.as_raw_fd(), + source_name.as_ptr().cast(), + destination.directory.as_raw_fd(), + target_name.as_ptr().cast(), + libc::RENAME_NOREPLACE, + ) + }; + #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))] + return Err(std::io::ErrorKind::Unsupported.into()); + if result != 0 { + return Err(std::io::Error::last_os_error()); + } + } + #[cfg(windows)] + { + use std::os::windows::io::AsRawHandle; + use windows_sys::{ + Wdk::Storage::FileSystem::{ + FileRenameInformation, NtSetInformationFile, FILE_RENAME_INFORMATION, + }, + Win32::{ + Foundation::{RtlNtStatusToDosError, HANDLE}, + System::IO::IO_STATUS_BLOCK, + }, + }; + #[repr(C)] + struct Rename { + flags: u32, + root: HANDLE, + length: u32, + name: [u16; 256], + } + const _: () = assert!( + std::mem::offset_of!(Rename, name) + == std::mem::offset_of!(FILE_RENAME_INFORMATION, FileName) + ); + let _ = (source_name, target_name); + let mut rename = Rename { + flags: 0, + root: destination.directory.as_raw_handle(), + length: (target.len() * 2) as u32, + name: [0; 256], + }; + for (out, byte) in rename.name.iter_mut().zip(target) { + *out = u16::from(*byte); + } + let mut status: IO_STATUS_BLOCK = unsafe { std::mem::zeroed() }; + let len = std::mem::offset_of!(Rename, name) + target.len() * 2; + // SAFETY: actual source handle carries DELETE; target is relative + // to the retained destination HANDLE. Zero flags means no replace. + let result = unsafe { + NtSetInformationFile( + source.as_raw_handle(), + &mut status, + (&rename as *const Rename).cast(), + len as u32, + FileRenameInformation, + ) + }; + if result < 0 { + return Err(std::io::Error::from_raw_os_error(unsafe { + RtlNtStatusToDosError(result) + } as i32)); + } + } + #[cfg(not(any(unix, windows)))] + return Err(std::io::ErrorKind::Unsupported.into()); + let moved = destination.relative_handle(target, false, directory, false, false)?; + if file_identity(&moved).map_err(native_io)? != expected { + return Err(std::io::ErrorKind::InvalidData.into()); + } + Ok(()) + } + /// The only hardlinked class admitted by cleanup: a real containment FIFO + /// and its private creation pin. Regular payloads still require one link. + #[cfg(unix)] + pub(crate) fn open_pinned_fifo( + &self, + name: &[u8], + expected: FileIdentity, + links: u64, + ) -> std::io::Result { + use std::os::{ + fd::{AsRawFd, FromRawFd}, + unix::fs::{FileTypeExt, MetadataExt}, + }; + let native = checked_fifo_name(name)?; + if !matches!(links, 1 | 2) { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + // SAFETY: retained actual parent, one validated basename, nonblocking + // read prevents waiting for a writer and NOFOLLOW rejects substitution. + let fd = unsafe { + libc::openat( + self.directory.as_raw_fd(), + native.as_ptr().cast(), + libc::O_RDONLY | libc::O_NONBLOCK | libc::O_NOFOLLOW | libc::O_CLOEXEC, + ) + }; + if fd < 0 { + return Err(std::io::Error::last_os_error()); + } + // SAFETY: successful openat returned this uniquely owned descriptor. + let file = unsafe { File::from_raw_fd(fd) }; + let metadata = file.metadata()?; + if !metadata.file_type().is_fifo() + || metadata.nlink() != links + || file_identity(&file).map_err(native_io)? != expected + { + return Err(std::io::ErrorKind::InvalidData.into()); + } + Ok(file) + } + /// Creates the FIFO itself, so no existing-file observation can mint this + /// event. The retained reader keeps its inode alive until its pin is made. + #[cfg(unix)] + pub(crate) fn create_containment_fifo(&self) -> std::io::Result { + use std::os::fd::{AsRawFd, FromRawFd}; + let name = checked_fifo_name(b"writers.fifo")?; + // SAFETY: actual parent FD and validated NUL-terminated fixed basename. + if unsafe { libc::mkfifoat(self.directory.as_raw_fd(), name.as_ptr().cast(), 0o600) } != 0 { + return Err(std::io::Error::last_os_error()); + } + // SAFETY: retained actual parent FD and validated basename; nonblocking. + let fd = unsafe { + libc::openat( + self.directory.as_raw_fd(), + name.as_ptr().cast(), + libc::O_RDONLY | libc::O_NONBLOCK | libc::O_NOFOLLOW | libc::O_CLOEXEC, + ) + }; + if fd < 0 { + return Err(std::io::Error::last_os_error()); + } + // SAFETY: successful openat returned this uniquely owned descriptor. + let file = unsafe { File::from_raw_fd(fd) }; + let id = file_identity(&file).map_err(native_io)?; + self.open_pinned_fifo(b"writers.fifo", id, 1)?; + Ok(CreatedContainmentFifo { file }) + } + #[cfg(unix)] + pub(crate) fn pin_created_containment_fifo( + &self, + created: &CreatedContainmentFifo, + registry: &Self, + pin_name: &[u8], + ) -> std::io::Result { + use std::os::fd::AsRawFd; + if pin_name == b"writers.fifo" { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + let pin = checked_fifo_name(pin_name)?; + let source = checked_fifo_name(b"writers.fifo")?; + let id = file_identity(created.file()).map_err(native_io)?; + self.open_pinned_fifo(b"writers.fifo", id, 1)?; + // SAFETY: retained source/registry FDs, validated one-component names; + // zero flags link only the existing entry and never replace a pin. + if unsafe { + libc::linkat( + self.directory.as_raw_fd(), + source.as_ptr().cast(), + registry.directory.as_raw_fd(), + pin.as_ptr().cast(), + 0, + ) + } != 0 + { + return Err(std::io::Error::last_os_error()); + } + self.open_pinned_fifo(b"writers.fifo", id, 2)?; + let file = registry.open_pinned_fifo(pin_name, id, 2)?; + self.sync_namespace()?; + registry.sync_namespace()?; + Ok(CreatedFifoPin { file }) + } + /// Caller must retain actual terminal kernel/root-closed authority. This + /// performs only an exact special-class unlink; it confers no authority. + #[cfg(unix)] + pub(crate) fn unlink_pinned_fifo( + &self, + name: &[u8], + expected: FileIdentity, + links: u64, + ) -> std::io::Result<()> { + use std::os::fd::AsRawFd; + let native = checked_fifo_name(name)?; + let _pin = self.open_pinned_fifo(name, expected, links)?; + // SAFETY: actual retained parent FD and validated special FIFO basename. + if unsafe { libc::unlinkat(self.directory.as_raw_fd(), native.as_ptr().cast(), 0) } != 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(()) + } + pub(crate) fn unlink_file(&self, name: &[u8], expected: FileIdentity) -> std::io::Result<()> { + self.remove_entry(name, expected, false) + } + /// Atomic empty-only removal. Unknown siblings always produce NotEmpty; + /// no recursive walk, control recreation or forced-delete fallback exists. + pub(crate) fn remove_empty_directory( + &self, + name: &[u8], + expected: FileIdentity, + ) -> std::io::Result<()> { + self.remove_entry(name, expected, true) + } + fn remove_entry( + &self, + name: &[u8], + expected: FileIdentity, + directory: bool, + ) -> std::io::Result<()> { + let native = checked_relative_name(name)?; + let file = self.relative_handle(name, false, directory, true, false)?; + if file_identity(&file).map_err(native_io)? != expected { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + #[cfg(unix)] + { + use std::os::fd::AsRawFd; + // SAFETY: retained actual parent FD + validated basename; the kernel + // enforces emptiness for directory removal at this instant. + let result = unsafe { + libc::unlinkat( + self.directory.as_raw_fd(), + native.as_ptr().cast(), + if directory { libc::AT_REMOVEDIR } else { 0 }, + ) + }; + if result != 0 { + return Err(std::io::Error::last_os_error()); + } + } + #[cfg(windows)] + { + use std::os::windows::io::AsRawHandle; + use windows_sys::{ + Wdk::Storage::FileSystem::{ + FileDispositionInformationEx, NtSetInformationFile, + FILE_DISPOSITION_INFORMATION_EX, + }, + Win32::{ + Foundation::RtlNtStatusToDosError, + Storage::FileSystem::{ + FILE_DISPOSITION_FLAG_DELETE, FILE_DISPOSITION_FLAG_POSIX_SEMANTICS, + }, + System::IO::IO_STATUS_BLOCK, + }, + }; + let _ = native; + let disposition = FILE_DISPOSITION_INFORMATION_EX { + Flags: FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS, + }; + let mut status: IO_STATUS_BLOCK = unsafe { std::mem::zeroed() }; + // SAFETY: DELETE access was acquired on this exact native object; + // POSIX disposition removes the name now, while retaining native + // DELETE sharing and empty-directory checks, even with our own pin. + let result = unsafe { + NtSetInformationFile( + file.as_raw_handle(), + &mut status, + (&disposition as *const FILE_DISPOSITION_INFORMATION_EX).cast(), + std::mem::size_of::() as u32, + FileDispositionInformationEx, + ) + }; + if result < 0 { + return Err(std::io::Error::from_raw_os_error(unsafe { + RtlNtStatusToDosError(result) + } as i32)); + } + } + #[cfg(not(any(unix, windows)))] + return Err(std::io::ErrorKind::Unsupported.into()); + drop(file); + Ok(()) + } +} + +#[cfg(test)] +mod rooted_directory_tests { + use super::*; + + #[test] + fn metadata_validation_preserves_private_creator_sharing_and_identity() { + let fixture = Fixture::new(); + let root = fixture.anchor(); + let path = fixture.root.join("private-output"); + let fresh = crate::process_lifecycle::FreshLifecycleFile::create(&path).unwrap(); + #[cfg(windows)] + { + let error = root.open_readonly_file(b"private-output").unwrap_err(); + assert_eq!(error.raw_os_error(), Some(32)); + } + let metadata = root.open_metadata_file(b"private-output").unwrap(); + assert_eq!( + file_identity(&metadata).unwrap(), + file_identity(fresh.file()).unwrap() + ); + assert_eq!(file_link_count(&metadata).unwrap(), 1); + drop(metadata); + drop(fresh); + assert_eq!( + root.open_readonly_file(b"private-output") + .unwrap() + .metadata() + .unwrap() + .len(), + 0 + ); + } + #[test] + fn namespace_flush_accepts_an_existing_readonly_directory_anchor() { + let fixture = Fixture::new(); + let id = path_identity(&fixture.root).unwrap(); + let root = RootedDirectory::open_readonly_root(&fixture.root, id).unwrap(); + let file = root.create_new(b"durable-entry").unwrap(); + file.sync_all().unwrap(); + root.sync_namespace().unwrap(); + assert_eq!(root.identity(), id); + } + #[cfg(windows)] + #[test] + fn nested_directory_scans_do_not_consume_the_outer_cursor() { + let fixture = Fixture::new(); + let root = fixture.anchor(); + for name in [b"one".as_slice(), b"two", b"three"] { + drop(root.create_new(name).unwrap()); + } + let mut outer = Vec::new(); + root.visit_entries(&mut [0; 4096], &mut |name| { + let mut inner = 0; + root.visit_entries(&mut [0; 4096], &mut |_| { + inner += 1; + Ok(()) + })?; + assert_eq!(inner, 3); + outer.push(name.to_vec()); + Ok(()) + }) + .unwrap(); + outer.sort(); + assert_eq!(outer, [b"one".to_vec(), b"three".to_vec(), b"two".to_vec()]); + } + #[cfg(windows)] + #[test] + fn metadata_nonce_remains_readable_under_an_exclusive_owner() { + use std::io::Write; + let fixture = Fixture::new(); + let root = fixture.anchor(); + let mut file = root.create_new(b"owner").unwrap(); + file.write_all(&encode_coordination_incarnation([7; 32])) + .unwrap(); + set_created_file_nonce(&file, &[7; 32]).unwrap(); + let owner = FixedLockedFile::try_lock(file, LockMode::Exclusive) + .unwrap() + .unwrap(); + let reader = root.open_readonly_file(b"owner").unwrap(); + let error = std::os::windows::fs::FileExt::seek_read(&reader, &mut [0; 1], 0).unwrap_err(); + assert_eq!(error.raw_os_error(), Some(33)); + assert_eq!( + read_coordination_incarnation(&reader).unwrap(), + Some([7; 32]) + ); + assert!(FileExt::try_lock_shared(&reader).is_err()); + assert_eq!(owner.mode(), LockMode::Exclusive); + } + #[cfg(windows)] + #[test] + fn directory_queries_align_nested_byte_scratch_and_remove_pinned_empty_names() { + let fixture = Fixture::new(); + let root = fixture.anchor(); + let child = root.create_directory(b"owned-empty").unwrap(); + let mut backing = [0; 4104]; + let offset = backing.as_ptr().align_offset(8) + 1; + let scratch = (&mut backing[offset..offset + 4096]).try_into().unwrap(); + let mut seen = false; + root.visit_entries(scratch, &mut |name| { + seen |= name == b"owned-empty"; + Ok(()) + }) + .unwrap(); + assert!(seen); + root.remove_empty_directory(b"owned-empty", child.identity()) + .unwrap(); + assert_eq!( + root.open_directory_readonly(b"owned-empty") + .err() + .unwrap() + .kind(), + std::io::ErrorKind::NotFound + ); + } + struct Fixture { + root: PathBuf, + } + impl Fixture { + fn new() -> Self { + let root = std::env::temp_dir().join(format!( + "cleanup-rooted-{}", + crate::storage::layout::managed_generation_id() + )); + fs::create_dir(&root).unwrap(); + Self { root } + } + fn anchor(&self) -> RootedDirectory { + RootedDirectory::open(&self.root, path_identity(&self.root).unwrap()).unwrap() + } + } + impl Drop for Fixture { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.root); + } + } + #[cfg(any(target_os = "macos", target_os = "linux", windows))] + #[test] + fn dc_nonce_promotion_requires_the_exact_existing_creation_tag() { + let fixture = Fixture::new(); + let root = fixture.anchor(); + let mut file = root.create_new(b"source-state").unwrap(); + std::io::Write::write_all(&mut file, b"unchanged source payload").unwrap(); + let original = [17; 32]; + let promoted = [29; 32]; + assert!(replace_created_file_nonce(&file, &original, &promoted).is_err()); + assert_eq!(created_file_nonce(&file).unwrap(), None); + set_created_file_nonce(&file, &original).unwrap(); + assert!(replace_created_file_nonce(&file, &[18; 32], &promoted).is_err()); + assert_eq!(created_file_nonce(&file).unwrap(), Some(original)); + replace_created_file_nonce(&file, &original, &promoted).unwrap(); + assert_eq!(created_file_nonce(&file).unwrap(), Some(promoted)); + assert_eq!( + fs::read(fixture.root.join("source-state")).unwrap(), + b"unchanged source payload" + ); + assert!(replace_created_file_nonce(&file, &original, &[31; 32]).is_err()); + assert_eq!(created_file_nonce(&file).unwrap(), Some(promoted)); + } + #[test] + fn dc_directory_hints_keep_verified_directories_and_reject_file_aliases() { + let fixture = Fixture::new(); + let root = fixture.anchor(); + for name in [b"first".as_slice(), b"second"] { + root.create_directory(name).unwrap(); + } + for index in 0..64 { + root.create_new(format!("payload-{index}").as_bytes()) + .unwrap(); + } + #[cfg(unix)] + std::os::unix::fs::symlink(fixture.root.join("first"), fixture.root.join("alias")).unwrap(); + let mut scratch = [0; 4096]; + let mut actual = std::collections::BTreeSet::new(); + root.visit_directories(&mut scratch, &mut |name| { + if root.open_directory_readonly(name).is_ok() { + actual.insert(name.to_vec()); + } + Ok(()) + }) + .unwrap(); + assert_eq!( + actual, + std::collections::BTreeSet::from([b"first".to_vec(), b"second".to_vec()]) + ); + // Directory enumeration resets its actual anchor rather than consuming + // an external stream cursor or retaining a collection between calls. + let mut repeated = std::collections::BTreeSet::new(); + root.visit_directories(&mut scratch, &mut |name| { + if root.open_directory_readonly(name).is_ok() { + repeated.insert(name.to_vec()); + } + Ok(()) + }) + .unwrap(); + assert_eq!(actual, repeated); + } + #[cfg(unix)] + #[test] + fn dc_fifo_creation_pin_blocks_inode_aba_and_rejects_aliases() { + use std::os::unix::fs::MetadataExt; + let f = Fixture::new(); + let root = f.anchor(); + let registry = root.create_directory(b"registry").unwrap(); + let mut pin = [b'a'; 69]; + pin[..5].copy_from_slice(b"fifo-"); + let created = root.create_containment_fifo().unwrap(); + let id = file_identity(created.file()).unwrap(); + assert!(root.create_containment_fifo().is_err()); + root.pin_created_containment_fifo(&created, ®istry, &pin) + .unwrap(); + assert_eq!(created.file().metadata().unwrap().nlink(), 2); + assert!(root.open_readonly_file(b"writers.fifo").is_err()); + assert!(registry.open_pinned_fifo(b"payload", id, 2).is_err()); + // A third foreign alias prevents any collector-class unlink. + fs::hard_link(f.root.join("writers.fifo"), f.root.join("foreign")).unwrap(); + assert!(root.unlink_pinned_fifo(b"writers.fifo", id, 2).is_err()); + fs::remove_file(f.root.join("foreign")).unwrap(); + root.unlink_pinned_fifo(b"writers.fifo", id, 2).unwrap(); + drop(created); + // Private pin survives public disappearance and keeps the old inode. + let old = registry.open_pinned_fifo(&pin, id, 1).unwrap(); + let replacement = root.create_containment_fifo().unwrap(); + assert_ne!(file_identity(replacement.file()).unwrap(), id); + assert!(root.unlink_pinned_fifo(b"writers.fifo", id, 1).is_err()); + assert!(root + .open_pinned_fifo( + b"writers.fifo", + file_identity(replacement.file()).unwrap(), + 1 + ) + .is_ok()); + drop(old); + registry.unlink_pinned_fifo(&pin, id, 1).unwrap(); + assert!(!f + .root + .join("registry") + .join(std::str::from_utf8(&pin).unwrap()) + .exists()); + } + #[cfg(unix)] + #[test] + fn dc_fifo_creation_pin_never_adopts_pin_collision_or_regular_file() { + let f = Fixture::new(); + let root = f.anchor(); + let registry = root.create_directory(b"registry").unwrap(); + let mut pin = [b'b'; 69]; + pin[..5].copy_from_slice(b"fifo-"); + let collision = registry.create_new(&pin).unwrap(); + let collision_id = file_identity(&collision).unwrap(); + let created = root.create_containment_fifo().unwrap(); + assert!(root + .pin_created_containment_fifo(&created, ®istry, &pin) + .is_err()); + assert_eq!( + file_identity(®istry.open_readonly_file(&pin).unwrap()).unwrap(), + collision_id + ); + assert!(registry.open_pinned_fifo(&pin, collision_id, 1).is_err()); + assert!(registry.unlink_pinned_fifo(&pin, collision_id, 1).is_err()); + assert!(root + .open_pinned_fifo(b"writers.fifo", file_identity(created.file()).unwrap(), 1) + .is_ok()); + } + #[test] + fn dc_rooted_namespace_no_replace_and_exact_empty_removal_preserve_foreign_entries() { + use std::io::Write; + let f = Fixture::new(); + let root = f.anchor(); + let temp = root.create_directory(b"temp-owned").unwrap(); + let mut payload = temp.create_new(b"partition.json").unwrap(); + payload.write_all(b"own payload").unwrap(); + payload.sync_all().unwrap(); + let payload_id = file_identity(&payload).unwrap(); + drop(payload); + let mut foreign = root.create_new(b"canonical").unwrap(); + foreign.write_all(b"foreign canonical").unwrap(); + drop(foreign); + assert!(root + .rename_directory_no_replace(b"temp-owned", &root, b"canonical", temp.identity()) + .is_err()); + assert_eq!( + fs::read(f.root.join("canonical")).unwrap(), + b"foreign canonical" + ); + assert!(root + .remove_empty_directory(b"temp-owned", temp.identity()) + .is_err()); + let sentinel = temp.create_new(b"unknown-sibling").unwrap(); + drop(sentinel); + temp.unlink_file(b"partition.json", payload_id).unwrap(); + assert!(root + .remove_empty_directory(b"temp-owned", temp.identity()) + .is_err()); + assert!(f.root.join("temp-owned/unknown-sibling").exists()); + let sentinel = temp.open_existing(b"unknown-sibling").unwrap(); + let sentinel_id = file_identity(&sentinel).unwrap(); + drop(sentinel); + // Fixture teardown owns this sentinel; production never adopts unknowns. + temp.unlink_file(b"unknown-sibling", sentinel_id).unwrap(); + drop(temp); + let empty = root.open_directory(b"temp-owned").unwrap(); + let empty_id = empty.identity(); + drop(empty); + root.remove_empty_directory(b"temp-owned", empty_id) + .unwrap(); + assert!(!f.root.join("temp-owned").exists()); + // Also exercise actual directory publication, with only our exact + // completed payload and every descendant handle closed beforehand. + let ready = root.create_directory(b"owned-ready").unwrap(); + let mut file = ready.create_new(b"partition.json").unwrap(); + file.write_all(b"completed own leaf").unwrap(); + file.sync_all().unwrap(); + drop(file); + let ready_id = ready.identity(); + drop(ready); + root.rename_directory_no_replace(b"owned-ready", &root, b"published-owned", ready_id) + .unwrap(); + assert_eq!( + path_identity(&f.root.join("published-owned")).unwrap(), + ready_id + ); + assert_eq!( + fs::read(f.root.join("published-owned/partition.json")).unwrap(), + b"completed own leaf" + ); + assert!(!f.root.join("owned-ready").exists()); + assert_eq!( + fs::read(f.root.join("canonical")).unwrap(), + b"foreign canonical" + ); + } + #[test] + fn dc_rooted_file_rename_is_no_replace_and_checks_exact_source_identity() { + use std::io::Write; + let f = Fixture::new(); + let root = f.anchor(); + let mut source = root.create_new(b"new").unwrap(); + source.write_all(b"new bytes").unwrap(); + let id = file_identity(&source).unwrap(); + drop(source); + let mut target = root.create_new(b"old").unwrap(); + target.write_all(b"old bytes").unwrap(); + let other_id = file_identity(&target).unwrap(); + drop(target); + assert!(root + .rename_file_no_replace(b"new", &root, b"old", id) + .is_err()); + assert_eq!(fs::read(f.root.join("old")).unwrap(), b"old bytes"); + assert!(root + .rename_file_no_replace(b"new", &root, b"renamed", other_id) + .is_err()); + assert!(!f.root.join("renamed").exists()); + root.rename_file_no_replace(b"new", &root, b"renamed", id) + .unwrap(); + assert_eq!(path_identity(&f.root.join("renamed")).unwrap(), id); + assert!(root.unlink_file(b"renamed", other_id).is_err()); + assert_eq!(fs::read(f.root.join("renamed")).unwrap(), b"new bytes"); + root.unlink_file(b"renamed", id).unwrap(); + } + #[test] + fn dc_rooted_component_validation_is_noncreating_and_accepts_exact_key64() { + let f = Fixture::new(); + let root = f.anchor(); + for name in [ + b"".as_slice(), + b".", + b"..", + b"../outside", + b"a/b", + b"a\\b", + b"a:b", + b"a\0b", + ] { + assert!(root.create_new(name).is_err()); + assert!(root.create_directory(name).is_err()); + } + assert_eq!(fs::read_dir(&f.root).unwrap().count(), 0); + let key = [b'a'; 64]; + drop(root.create_new(&key).unwrap()); + assert!(f.root.join("a".repeat(64)).is_file()); + } + #[test] + fn dc_rooted_readonly_payload_access_needs_no_payload_write_permission() { + let f = Fixture::new(); + let child = f.root.join("canonical"); + fs::create_dir(&child).unwrap(); + let target = child.join("readonly-payload"); + fs::write(&target, b"readable payload").unwrap(); + let original = fs::metadata(&target).unwrap().permissions(); + struct Restore { + path: PathBuf, + permissions: fs::Permissions, + } + impl Drop for Restore { + fn drop(&mut self) { + let _ = fs::set_permissions(&self.path, self.permissions.clone()); + } + } + let _restore = Restore { + path: target.clone(), + permissions: original.clone(), + }; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&target, fs::Permissions::from_mode(0o444)).unwrap(); + } + #[cfg(windows)] + { + let mut permissions = original.clone(); + permissions.set_readonly(true); + fs::set_permissions(&target, permissions).unwrap(); + } + let root = + RootedDirectory::open_readonly_root(&f.root, path_identity(&f.root).unwrap()).unwrap(); + let canonical = root.open_directory_readonly(b"canonical").unwrap(); + let mut file = canonical.open_readonly_file(b"readonly-payload").unwrap(); + use std::io::Read; + let mut bytes = [0; 16]; + file.read_exact(&mut bytes).unwrap(); + assert_eq!(&bytes, b"readable payload"); + } + + #[cfg(unix)] + #[test] + fn dc_rooted_operations_remain_on_actual_parent_after_public_name_substitution() { + let f = Fixture::new(); + let public = f.root.join("public"); + fs::create_dir(&public).unwrap(); + let anchor = RootedDirectory::open(&public, path_identity(&public).unwrap()).unwrap(); + fs::rename(&public, f.root.join("old-owned")).unwrap(); + fs::create_dir(&public).unwrap(); + drop(anchor.create_new(b"created-on-original").unwrap()); + assert!(f.root.join("old-owned/created-on-original").is_file()); + assert!(!public.join("created-on-original").exists()); + } + #[cfg(any(unix, windows))] + #[test] + fn dc_rooted_nonfollowing_open_rejects_symlinks_and_hardlinked_controls() { + let f = Fixture::new(); + let root = f.anchor(); + let target = f.root.join("target"); + fs::write(&target, b"foreign target").unwrap(); + #[cfg(unix)] + std::os::unix::fs::symlink(&target, f.root.join("alias")).unwrap(); + #[cfg(windows)] + std::os::windows::fs::symlink_file(&target, f.root.join("alias")).unwrap(); + assert!(root.open_existing(b"alias").is_err()); + assert!(root.create_new(b"alias").is_err()); + fs::hard_link(&target, f.root.join("hardlink")).unwrap(); + assert!(root.open_existing(b"hardlink").is_err()); + assert_eq!(fs::read(target).unwrap(), b"foreign target"); + } +} + +/// Fixed-buffer native directory streaming. Names are borrowed only for the +/// callback; there is no retained union/catalog, libc DIR or child-path join. +impl RootedDirectory { + pub(crate) fn visit_entries( + &self, + bytes: &mut [u8; 4096], + visit: &mut impl FnMut(&[u8]) -> std::io::Result<()>, + ) -> std::io::Result<()> { + self.visit_entries_filtered(bytes, false, visit) + } + /// Advisory directory hints avoid opening every regular payload while + /// looking up a workspace parent. Callers still open and verify directories. + pub(crate) fn visit_directories( + &self, + bytes: &mut [u8; 4096], + visit: &mut impl FnMut(&[u8]) -> std::io::Result<()>, + ) -> std::io::Result<()> { + self.visit_entries_filtered(bytes, true, visit) + } + fn visit_entries_filtered( + &self, + bytes: &mut [u8; 4096], + directories_only: bool, + visit: &mut impl FnMut(&[u8]) -> std::io::Result<()>, + ) -> std::io::Result<()> { + #[cfg(target_os = "macos")] + { + use std::os::fd::AsRawFd; + let mut attributes: libc::attrlist = unsafe { std::mem::zeroed() }; + attributes.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + attributes.commonattr = libc::ATTR_CMN_NAME | libc::ATTR_CMN_RETURNED_ATTRS; + if directories_only { + attributes.commonattr |= libc::ATTR_CMN_OBJTYPE; + } + // SAFETY: actual directory FD, bounded writable caller scratch and + // initialized attrlist. No libc DIR allocation or path expansion. + if unsafe { libc::lseek(self.directory.as_raw_fd(), 0, libc::SEEK_SET) } < 0 { + return Err(std::io::Error::last_os_error()); + } + loop { + let count = unsafe { + libc::getattrlistbulk( + self.directory.as_raw_fd(), + (&mut attributes as *mut libc::attrlist).cast(), + bytes.as_mut_ptr().cast(), + bytes.len(), + 0, + ) + }; + if count < 0 { + return Err(std::io::Error::last_os_error()); + } + if count == 0 { + return Ok(()); + } + let mut offset = 0usize; + for _ in 0..count { + if offset + 32 > bytes.len() { + return Err(std::io::ErrorKind::InvalidData.into()); + } + let length = + u32::from_ne_bytes(bytes[offset..offset + 4].try_into().unwrap()) as usize; + if length < 32 || offset + length > bytes.len() { + return Err(std::io::ErrorKind::InvalidData.into()); + } + let relative = + i32::from_ne_bytes(bytes[offset + 24..offset + 28].try_into().unwrap()); + let name_len = + u32::from_ne_bytes(bytes[offset + 28..offset + 32].try_into().unwrap()) + as usize; + let start = (offset + 24) + .checked_add_signed(relative as isize) + .ok_or(std::io::ErrorKind::InvalidData)?; + let end = start + .checked_add(name_len) + .ok_or(std::io::ErrorKind::InvalidData)?; + if name_len == 0 + || start < offset + || end > offset + length + || bytes[end - 1] != 0 + { + return Err(std::io::ErrorKind::InvalidData.into()); + } + let name = &bytes[start..end - 1]; + let returned = + u32::from_ne_bytes(bytes[offset + 4..offset + 8].try_into().unwrap()); + let known_non_directory = if directories_only + && returned & libc::ATTR_CMN_OBJTYPE != 0 + { + if length < 36 { + return Err(std::io::ErrorKind::InvalidData.into()); + } + // Darwin vnode_type_t: VNON=0, VREG=1, VDIR=2. An + // unavailable type falls back to the verified open. + let kind = + u32::from_ne_bytes(bytes[offset + 32..offset + 36].try_into().unwrap()); + kind != 0 && kind != 2 + } else { + false + }; + if name != b"." && name != b".." && !known_non_directory { + visit(name)?; + } + offset += length; + } + } + } + #[cfg(target_os = "linux")] + { + use std::os::fd::AsRawFd; + if unsafe { libc::lseek(self.directory.as_raw_fd(), 0, libc::SEEK_SET) } < 0 { + return Err(std::io::Error::last_os_error()); + } + loop { + // SAFETY: Linux getdents64 ABI and fixed writable buffer. + let count = unsafe { + libc::syscall( + libc::SYS_getdents64, + self.directory.as_raw_fd(), + bytes.as_mut_ptr(), + bytes.len(), + ) + }; + if count < 0 { + return Err(std::io::Error::last_os_error()); + } + if count == 0 { + return Ok(()); + } + let mut offset = 0usize; + while offset < count as usize { + if offset + 20 > count as usize { + return Err(std::io::ErrorKind::InvalidData.into()); + } + let length = + u16::from_ne_bytes(bytes[offset + 16..offset + 18].try_into().unwrap()) + as usize; + if length < 20 || offset + length > count as usize { + return Err(std::io::ErrorKind::InvalidData.into()); + } + let field = &bytes[offset + 19..offset + length]; + let end = field + .iter() + .position(|x| *x == 0) + .ok_or(std::io::ErrorKind::InvalidData)?; + let name = &field[..end]; + let kind = bytes[offset + 18]; + if name != b"." + && name != b".." + && (!directories_only || kind == libc::DT_UNKNOWN || kind == libc::DT_DIR) + { + visit(name)?; + } + offset += length; + } + } + } + #[cfg(windows)] + { + let _ = directories_only; + // Native directory queries require aligned output. Caller scratch + // may be a byte field within a larger prepared object. + let padding = bytes.as_ptr().align_offset(8); + let bytes = &mut bytes[padding..]; + use std::os::windows::io::AsRawHandle; + use windows_sys::Win32::Storage::FileSystem::{ + FILE_LIST_DIRECTORY, FILE_READ_ATTRIBUTES, SYNCHRONIZE, + }; + use windows_sys::{ + Wdk::Storage::FileSystem::{FileNamesInformation, NtQueryDirectoryFile}, + Win32::{Foundation::STATUS_NO_MORE_FILES, System::IO::IO_STATUS_BLOCK}, + }; + // Reentrant scans need distinct kernel cursors; duplicating a + // handle would still share the outer enumeration's file object. + let directory = self.reopen_pinned_directory( + FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE, + )?; + let mut first = true; + loop { + let mut status: IO_STATUS_BLOCK = unsafe { std::mem::zeroed() }; + // SAFETY: synchronous directory handle and fixed writable + // FILE_NAMES_INFORMATION buffer; request one entry per call. + let result = unsafe { + NtQueryDirectoryFile( + directory.as_raw_handle(), + std::ptr::null_mut(), + None, + std::ptr::null(), + &mut status, + bytes.as_mut_ptr().cast(), + bytes.len() as u32, + FileNamesInformation, + true, + std::ptr::null(), + first, + ) + }; + first = false; + if result == STATUS_NO_MORE_FILES { + return Ok(()); + } + if result < 0 { + return Err(std::io::Error::from_raw_os_error(unsafe { + windows_sys::Win32::Foundation::RtlNtStatusToDosError(result) + } as i32) + .into()); + } + let length = u32::from_le_bytes(bytes[8..12].try_into().unwrap()) as usize; + if length > bytes.len() - 12 || length % 2 != 0 { + return Err(std::io::ErrorKind::InvalidData.into()); + } + // Decode into the disjoint second half of the SAME caller + // buffer. No per-depth stack/Vec name allocation, and no lossy + // interpretation of unpaired UTF-16 surrogates. + if length > 2036 { + return Err(std::io::ErrorKind::InvalidData.into()); + } + let (input, output) = bytes.split_at_mut(2048); + let mut written = 0usize; + for scalar in char::decode_utf16( + input[12..12 + length] + .chunks_exact(2) + .map(|unit| u16::from_le_bytes(unit.try_into().unwrap())), + ) { + let scalar = scalar.map_err(|_| std::io::ErrorKind::InvalidData)?; + let mut encoded = [0u8; 4]; + let text = scalar.encode_utf8(&mut encoded).as_bytes(); + let end = written + .checked_add(text.len()) + .ok_or(std::io::ErrorKind::InvalidData)?; + if end > output.len() { + return Err(std::io::ErrorKind::InvalidData.into()); + } + output[written..end].copy_from_slice(text); + written = end; + } + let name = &output[..written]; + if name != b"." && name != b".." { + visit(name)?; + } + } + } + #[cfg(not(any(target_os = "macos", target_os = "linux", windows)))] + Err(std::io::ErrorKind::Unsupported.into()) + } + pub(crate) fn contains_only( + &self, + allowed: Option<&[u8]>, + bytes: &mut [u8; 4096], + grant: &mut super::maintenance_budget::MutationGrant, + ) -> Result { + let mut seen = false; + let mut stopped = false; + let mut exhausted = None; + let result = self.visit_entries(bytes, &mut |name| { + if let Err(error) = grant.take_entry() { + exhausted = Some(error); + return Err(std::io::ErrorKind::Interrupted.into()); + } + if !seen && allowed == Some(name) { + seen = true; + Ok(()) + } else { + stopped = true; + Err(std::io::ErrorKind::Interrupted.into()) + } + }); + if let Some(error) = exhausted { + return Err(error.into()); + } + if stopped { + return Ok(false); + } + result?; + Ok(true) + } +} + +impl FixedLockedFile { + pub(crate) fn matches_fixed_current( + &self, + current: &FixedFileStamp, + mode: LockMode, + ) -> Result { + Ok(self.mode == mode + && FixedFileStamp::capture(&self.file, FixedControlKind::File)?.identity() + == current.identity()) + } + pub(crate) fn matches_fixed_header( + &self, + expected: &[u8; COORDINATION_HEADER_BYTES], + ) -> Result { + let stamp = FixedFileStamp::capture(&self.file, FixedControlKind::File)?; + let mut bytes = [0; COORDINATION_HEADER_BYTES]; + if !read_coordination_header(&self.file, &mut bytes)? { + return Ok(false); + } + stamp.recheck(&self.file)?; + Ok(bytes == *expected) + } +} + +impl RootedDirectory { + pub(crate) fn open_fixed( + path: &Path, + expected: FileIdentity, + write: bool, + scratch: &mut FixedControlScratch, + ) -> Result { + let directory = open_fixed_control(path, FixedControlKind::Directory, write, scratch)?; + let identity = FixedFileStamp::capture(&directory, FixedControlKind::Directory)?.identity(); + if identity != expected { + return Err(FixedControlError::Invalid(FixedControlProblem::Changed)); + } + Ok(Self { + directory, + identity, + _same_thread: std::marker::PhantomData, + }) + } +} + +impl From for FixedControlError { + fn from(kind: std::io::ErrorKind) -> Self { + Self::Io(kind.into()) + } +} + +impl FixedFileStamp { + pub(crate) fn binding_digest(&self) -> [u8; 32] { + use sha2::{Digest, Sha256}; + let mut hash = Sha256::new(); + let (a, b) = self.identity.fixed_words(); + hash.update(a.to_le_bytes()); + hash.update(b.to_le_bytes()); + hash.update(self.len.to_le_bytes()); + hash.update(self.links.to_le_bytes()); + hash.update([match self.kind { + FixedControlKind::File => 0, + FixedControlKind::Directory => 1, + }]); + for part in self.modified.into_iter().chain(self.changed) { + hash.update(part.to_le_bytes()); + } + hash.finalize().into() + } +} + +impl RootedDirectory { + pub(crate) fn metadata(&self) -> std::io::Result { + self.directory.metadata() + } +} + +/// Genuine namespace creation, used by owned workspace factories. Existing +/// coordination names are never adopted merely because their file is empty. +pub(crate) fn create_new_coordination_in( + root: &RootedDirectory, + name: &[u8], + path: &Path, +) -> Result { + if path.file_name().map(|n| n.as_encoded_bytes()) != Some(name) + || path.parent().map(path_identity).transpose()? != Some(root.identity()) + { + return Err(NativeError::InvalidInput( + "fresh coordination parent mismatch".into(), + )); + } + let file = root.create_new(name)?; + FileExt::lock_exclusive(&file)?; + let lock = LockedFile { + path: path.to_owned(), + file: Some(file), + mode: LockMode::Exclusive, + companions: Vec::new(), + }; + if !lock.is_current()? { + return Err(NativeError::InvalidInput( + "fresh coordination changed".into(), + )); + } + Ok(lock) +} + +pub(crate) fn read_fixed_range(file: &File, offset: u64, bytes: &mut [u8]) -> std::io::Result<()> { + #[cfg(unix)] + { + use std::os::unix::fs::FileExt; + file.read_exact_at(bytes, offset) + } + #[cfg(windows)] + { + use std::os::windows::fs::FileExt; + let mut done = 0; + while done < bytes.len() { + let n = file.seek_read( + &mut bytes[done..], + offset + .checked_add(done as u64) + .ok_or(std::io::ErrorKind::InvalidInput)?, + )?; + if n == 0 { + return Err(std::io::ErrorKind::UnexpectedEof.into()); + } + done += n; + } + Ok(()) + } + #[cfg(not(any(unix, windows)))] + Err(std::io::ErrorKind::Unsupported.into()) +} +pub(crate) fn write_fixed_range( + file: &File, + offset: u64, + bytes: &[u8], + grant: &super::maintenance_budget::MutationGrant, +) -> Result<(), FixedControlError> { + let mut done = 0; + while done < bytes.len() { + grant.check_time()?; + let at = offset + .checked_add(done as u64) + .ok_or(std::io::ErrorKind::InvalidInput)?; + #[cfg(unix)] + let n = { + use std::os::unix::fs::FileExt; + file.write_at(&bytes[done..], at)? + }; + #[cfg(windows)] + let n = { + use std::os::windows::fs::FileExt; + file.seek_write(&bytes[done..], at)? + }; + #[cfg(not(any(unix, windows)))] + return Err(std::io::ErrorKind::Unsupported.into()); + if n == 0 { + return Err(std::io::ErrorKind::WriteZero.into()); + } + done += n; + } + grant.check_time()?; + file.sync_all()?; + Ok(()) +} + +/// Creation-only physical file nonce. Metadata is outside primary CSV/JSON +/// bytes, survives chmod, and prevents numeric inode ABA from adopting a new +/// untagged file. Unsupported filesystems remain optional/untracked. +pub(crate) fn set_created_file_nonce(file: &File, nonce: &[u8; 32]) -> std::io::Result<()> { + #[cfg(target_os = "macos")] + { + use std::os::fd::AsRawFd; + let result = unsafe { + libc::fsetxattr( + file.as_raw_fd(), + c"com.codebasegraph.origin".as_ptr(), + nonce.as_ptr().cast(), + nonce.len(), + 0, + libc::XATTR_CREATE, + ) + }; + if result != 0 { + return Err(std::io::Error::last_os_error()); + } + file.sync_all()?; + Ok(()) + } + #[cfg(target_os = "linux")] + { + use std::os::fd::AsRawFd; + let result = unsafe { + libc::fsetxattr( + file.as_raw_fd(), + c"user.codebasegraph.origin".as_ptr(), + nonce.as_ptr().cast(), + nonce.len(), + libc::XATTR_CREATE, + ) + }; + if result != 0 { + return Err(std::io::Error::last_os_error()); + } + file.sync_all()?; + Ok(()) + } + #[cfg(windows)] + { + use std::os::windows::io::AsRawHandle; + use windows_sys::{ + Wdk::Storage::FileSystem::NtSetEaFile, + Win32::{Foundation::RtlNtStatusToDosError, System::IO::IO_STATUS_BLOCK}, + }; + const NAME: &[u8] = b"CODEBASEGRAPH.ORIGIN"; + let mut aligned = NativeIoBytes([0u8; 64]); + let bytes = &mut aligned.0; + bytes[5] = NAME.len() as u8; + bytes[6..8].copy_from_slice(&32u16.to_le_bytes()); + bytes[8..8 + NAME.len()].copy_from_slice(NAME); + bytes[9 + NAME.len()..41 + NAME.len()].copy_from_slice(nonce); + let mut io: IO_STATUS_BLOCK = unsafe { std::mem::zeroed() }; + let status = unsafe { + NtSetEaFile( + file.as_raw_handle(), + &mut io, + bytes.as_ptr().cast(), + (41 + NAME.len()) as u32, + ) + }; + if status < 0 { + return Err(std::io::Error::from_raw_os_error( + unsafe { RtlNtStatusToDosError(status) } as i32, + )); + } + file.sync_all()?; + Ok(()) + } + #[cfg(not(any(target_os = "macos", target_os = "linux", windows)))] + Err(std::io::ErrorKind::Unsupported.into()) +} +/// Replace only an existing, exactly matched creation nonce while the caller +/// retains its actual source/owner exclusion. This never creates a missing tag. +pub(crate) fn replace_created_file_nonce( + file: &File, + expected: &[u8; 32], + next: &[u8; 32], +) -> std::io::Result<()> { + if created_file_nonce(file)? != Some(*expected) { + return Err(std::io::ErrorKind::InvalidData.into()); + } + #[cfg(target_os = "macos")] + { + use std::os::fd::AsRawFd; + let result = unsafe { + libc::fsetxattr( + file.as_raw_fd(), + c"com.codebasegraph.origin".as_ptr(), + next.as_ptr().cast(), + next.len(), + 0, + libc::XATTR_REPLACE, + ) + }; + if result != 0 { + return Err(std::io::Error::last_os_error()); + } + } + #[cfg(target_os = "linux")] + { + use std::os::fd::AsRawFd; + let result = unsafe { + libc::fsetxattr( + file.as_raw_fd(), + c"user.codebasegraph.origin".as_ptr(), + next.as_ptr().cast(), + next.len(), + libc::XATTR_REPLACE, + ) + }; + if result != 0 { + return Err(std::io::Error::last_os_error()); + } + } + #[cfg(windows)] + set_created_file_nonce(file, next)?; + #[cfg(not(any(target_os = "macos", target_os = "linux", windows)))] + return Err(std::io::ErrorKind::Unsupported.into()); + if created_file_nonce(file)? != Some(*next) { + return Err(std::io::ErrorKind::InvalidData.into()); + } + file.sync_all() +} +pub(crate) fn created_file_nonce(file: &File) -> std::io::Result> { + let mut nonce = [0u8; 32]; + #[cfg(target_os = "macos")] + { + use std::os::fd::AsRawFd; + let count = unsafe { + libc::fgetxattr( + file.as_raw_fd(), + c"com.codebasegraph.origin".as_ptr(), + nonce.as_mut_ptr().cast(), + nonce.len(), + 0, + 0, + ) + }; + if count < 0 { + let error = std::io::Error::last_os_error(); + if matches!(error.raw_os_error(), Some(libc::ENOATTR | libc::ENOTSUP)) { + return Ok(None); + } + return Err(error); + } + Ok((count == 32).then_some(nonce)) + } + #[cfg(target_os = "linux")] + { + use std::os::fd::AsRawFd; + let count = unsafe { + libc::fgetxattr( + file.as_raw_fd(), + c"user.codebasegraph.origin".as_ptr(), + nonce.as_mut_ptr().cast(), + nonce.len(), + ) + }; + if count < 0 { + let error = std::io::Error::last_os_error(); + if matches!(error.raw_os_error(), Some(libc::ENODATA | libc::ENOTSUP)) { + return Ok(None); + } + return Err(error); + } + Ok((count == 32).then_some(nonce)) + } + #[cfg(windows)] + { + use std::os::windows::io::AsRawHandle; + use windows_sys::{ + Wdk::Storage::FileSystem::NtQueryEaFile, + Win32::{ + Foundation::{ + RtlNtStatusToDosError, STATUS_EAS_NOT_SUPPORTED, STATUS_NONEXISTENT_EA_ENTRY, + STATUS_NO_EAS_ON_FILE, + }, + System::IO::IO_STATUS_BLOCK, + }, + }; + const NAME: &[u8] = b"CODEBASEGRAPH.ORIGIN"; + let mut aligned_query = NativeIoBytes([0u8; 32]); + let query = &mut aligned_query.0; + query[4] = NAME.len() as u8; + query[5..5 + NAME.len()].copy_from_slice(NAME); + let mut aligned_bytes = NativeIoBytes([0u8; 128]); + let bytes = &mut aligned_bytes.0; + let mut io: IO_STATUS_BLOCK = unsafe { std::mem::zeroed() }; + let status = unsafe { + NtQueryEaFile( + file.as_raw_handle(), + &mut io, + bytes.as_mut_ptr().cast(), + bytes.len() as u32, + true, + query.as_ptr().cast(), + (6 + NAME.len()) as u32, + std::ptr::null(), + true, + ) + }; + if [ + STATUS_NO_EAS_ON_FILE, + STATUS_NONEXISTENT_EA_ENTRY, + STATUS_EAS_NOT_SUPPORTED, + ] + .contains(&status) + { + return Ok(None); + } + if status < 0 { + return Err(std::io::Error::from_raw_os_error( + unsafe { RtlNtStatusToDosError(status) } as i32, + )); + } + let name_len = bytes[5] as usize; + let value_len = u16::from_le_bytes(bytes[6..8].try_into().unwrap()) as usize; + if io.Information < 8 + name_len + 1 + value_len + || name_len != NAME.len() + || value_len != 32 + || bytes[8..8 + name_len] != *NAME + { + return Ok(None); + } + nonce.copy_from_slice(&bytes[9 + name_len..41 + name_len]); + Ok(Some(nonce)) + } + #[cfg(not(any(target_os = "macos", target_os = "linux", windows)))] + Ok(None) +} + +impl FixedFileStamp { + pub(crate) fn encode_checkpoint(&self, out: &mut [u8; 80]) { + out.fill(0); + out[..8].copy_from_slice(&1u64.to_le_bytes()); + let (a, b) = self.identity.fixed_words(); + out[8..16].copy_from_slice(&a.to_le_bytes()); + out[16..24].copy_from_slice(&b.to_le_bytes()); + out[24] = match self.kind { + FixedControlKind::File => 0, + FixedControlKind::Directory => 1, + }; + out[32..40].copy_from_slice(&self.len.to_le_bytes()); + out[40..48].copy_from_slice(&self.links.to_le_bytes()); + for (index, value) in self.modified.into_iter().chain(self.changed).enumerate() { + out[48 + index * 8..56 + index * 8].copy_from_slice(&value.to_le_bytes()); + } + } + pub(crate) fn decode_checkpoint(input: &[u8; 80]) -> Result { + if u64::from_le_bytes(input[..8].try_into().unwrap()) != 1 + || input[25..32].iter().any(|b| *b != 0) + { + return Err(FixedControlProblem::InvalidRecord.into()); + } + let kind = match input[24] { + 0 => FixedControlKind::File, + 1 => FixedControlKind::Directory, + _ => return Err(FixedControlProblem::InvalidRecord.into()), + }; + let links = u64::from_le_bytes(input[40..48].try_into().unwrap()); + if kind == FixedControlKind::File && links != 1 { + return Err(FixedControlProblem::Aliased.into()); + } + let time = |offset| i64::from_le_bytes(input[offset..offset + 8].try_into().unwrap()); + Ok(Self { + identity: FileIdentity::from_fixed_words( + u64::from_le_bytes(input[8..16].try_into().unwrap()), + u64::from_le_bytes(input[16..24].try_into().unwrap()), + ), + kind, + len: u64::from_le_bytes(input[32..40].try_into().unwrap()), + links, + modified: [time(48), time(56)], + changed: [time(64), time(72)], + }) + } +} +impl From for FixedControlError { + fn from(problem: FixedControlProblem) -> Self { + Self::Invalid(problem) + } +} diff --git a/src/storage/maintenance.rs b/src/storage/maintenance.rs new file mode 100644 index 00000000..8e6854a9 --- /dev/null +++ b/src/storage/maintenance.rs @@ -0,0 +1,1295 @@ +//! Pure advisory selection and cursor codec. No filesystem effects, admission, +//! provenance proofs, retirement API or deletion authority live here. +use sha2::{Digest, Sha256}; + +const CATEGORIES: usize = 8; +const ADDRESS_BYTES: usize = 128; +const BODY_BYTES: usize = 8 + 32 + 1 + CATEGORIES * (1 + ADDRESS_BYTES); +pub(crate) const CURSOR_BYTES: usize = BODY_BYTES + 32; +const MAGIC: &[u8; 8] = b"CGMC0002"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub(crate) enum WorkCategory { + Canonical, + TemporaryOrBackup, + Run, + Phase, + Generation, + LegacyNative, + LegacySnapshot, + DefaultDirect, +} +impl WorkCategory { + fn from_index(index: usize) -> Self { + match index { + 0 => Self::Canonical, + 1 => Self::TemporaryOrBackup, + 2 => Self::Run, + 3 => Self::Phase, + 4 => Self::Generation, + 5 => Self::LegacyNative, + 6 => Self::LegacySnapshot, + 7 => Self::DefaultDirect, + _ => unreachable!("validated category"), + } + } +} + +/// Caller supplies a lossless fixed-width logical address, not a truncated or +/// hashed path. This module neither resolves it nor certifies its association. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct CursorAddress(pub(crate) [u8; ADDRESS_BYTES]); +impl CursorAddress { + pub(crate) fn from_short(short: [u8; 64]) -> Self { + let mut address = [0; ADDRESS_BYTES]; + address[..64].copy_from_slice(&short); + Self(address) + } + pub(crate) fn as_short(self) -> Option<[u8; 64]> { + if self.0[64..].iter().any(|byte| *byte != 0) { + return None; + } + let mut short = [0; 64]; + short.copy_from_slice(&self.0[..64]); + Some(short) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum CursorFailure { + Corrupt, + DifferentInstallation, +} + +pub(crate) struct MaintenanceCursor { + installation: [u8; 32], + next_category: u8, + after: [Option; CATEGORIES], +} +impl MaintenanceCursor { + pub(crate) fn new(installation: [u8; 32]) -> Self { + Self { + installation, + next_category: 0, + after: [None; CATEGORIES], + } + } + pub(crate) fn next_category(&self) -> WorkCategory { + WorkCategory::from_index(self.next_category as usize) + } + pub(crate) fn advance_empty(&mut self, category: WorkCategory) { + if self.next_category() == category { + self.next_category = ((category as usize + 1) % CATEGORIES) as u8; + } + } + pub(crate) fn after(&self, category: WorkCategory) -> Option { + self.after[category as usize] + } + + pub(crate) fn encode(&self) -> [u8; CURSOR_BYTES] { + let mut bytes = [0u8; CURSOR_BYTES]; + bytes[..8].copy_from_slice(MAGIC); + bytes[8..40].copy_from_slice(&self.installation); + bytes[40] = self.next_category; + for (index, address) in self.after.iter().enumerate() { + if let Some(address) = address { + let offset = 41 + index * (1 + ADDRESS_BYTES); + bytes[offset] = 1; + bytes[offset + 1..offset + 1 + ADDRESS_BYTES].copy_from_slice(&address.0); + } + } + let hash = Sha256::digest(&bytes[..BODY_BYTES]); + bytes[BODY_BYTES..].copy_from_slice(&hash); + bytes + } + pub(crate) fn decode( + bytes: &[u8; CURSOR_BYTES], + installation: [u8; 32], + ) -> Result { + if &bytes[..8] != MAGIC + || bytes[40] as usize >= CATEGORIES + || bytes[BODY_BYTES..] != Sha256::digest(&bytes[..BODY_BYTES])[..] + { + return Err(CursorFailure::Corrupt); + } + if bytes[8..40] != installation { + return Err(CursorFailure::DifferentInstallation); + } + let mut result = Self::new(installation); + result.next_category = bytes[40]; + for index in 0..CATEGORIES { + let offset = 41 + index * (1 + ADDRESS_BYTES); + match bytes[offset] { + 0 if bytes[offset + 1..offset + 1 + ADDRESS_BYTES] + .iter() + .all(|byte| *byte == 0) => {} + 1 => { + let mut address = [0u8; ADDRESS_BYTES]; + address.copy_from_slice(&bytes[offset + 1..offset + 1 + ADDRESS_BYTES]); + result.after[index] = Some(CursorAddress(address)); + } + _ => return Err(CursorFailure::Corrupt), + } + } + Ok(result) + } + pub(crate) fn selection(&self) -> FairSelection { + FairSelection { + next_category: self.next_category, + after: self.after, + later: [None; CATEGORIES], + wrap: [None; CATEGORIES], + } + } + /// Advance for inspected blocked/unknown entries as well as eligible ones. + /// Persistence is a separately budgeted caller operation, never performed + /// by this facts-only module. + pub(crate) fn inspected(&mut self, category: WorkCategory, address: CursorAddress) { + self.after[category as usize] = Some(address); + self.next_category = ((category as usize + 1) % CATEGORIES) as u8; + } +} + +pub(crate) struct FairSelection { + next_category: u8, + after: [Option; CATEGORIES], + later: [Option; CATEGORIES], + wrap: [Option; CATEGORIES], +} +impl FairSelection { + pub(crate) fn observe(&mut self, category: WorkCategory, address: CursorAddress) { + let index = category as usize; + let selected = if self.after[index].is_none_or(|after| address > after) { + &mut self.later[index] + } else { + &mut self.wrap[index] + }; + if selected.is_none_or(|existing| address < existing) { + *selected = Some(address); + } + } + pub(crate) fn selected(&self) -> Option<(WorkCategory, CursorAddress)> { + for delta in 0..CATEGORIES { + let index = (self.next_category as usize + delta) % CATEGORIES; + if let Some(address) = self.later[index].or(self.wrap[index]) { + return Some((WorkCategory::from_index(index), address)); + } + } + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn direct_confirmation_ingress_does_not_pin_legacy_source_or_block_recovery() { + use super::super::inventory::DirectPair; + use super::super::layout::DirectLayout; + use super::super::maintenance_admission::{ + prepare_canonical, prepare_metadata_control, prepare_private_metadata, tests::Fixture, + with_private_metadata, METADATA_WRITE_CUT, + }; + use super::super::maintenance_ownership::{self, EpochObservation, ReferenceDomain}; + #[derive(Clone)] + struct Admission; + impl MaintenanceAdmission for Admission { + type Permit = (); + fn try_acquire(&self) -> Option<()> { + Some(()) + } + fn stop_admission(&self) {} + } + let mut f = Fixture::new(); + // These real legacy payloads would be pinned by request capture even + // though the selected destination is an unrelated modern Direct pair. + let legacy = super::super::inventory::default_direct(&f.target); + std::fs::write(&legacy.database, b"legacy database").unwrap(); + std::fs::write(&legacy.manifest, b"legacy manifest").unwrap(); + let pair = DirectPair { + database: f.target.state_root.join("direct").join("graph.ldb"), + manifest: f.target.state_root.join("direct").join("manifest.json"), + }; + let layout = DirectLayout::new(&pair.database, &pair.manifest); + let store = super::super::direct::DirectStore::new(layout.clone()).unwrap(); + drop(store.begin_write().unwrap()); + f.target.direct_pair = Some(pair); + let domain = ReferenceDomain::direct(&layout).unwrap(); + let EpochObservation::Verified(epoch) = + maintenance_ownership::observe_reference_epoch(&domain).unwrap() + else { + panic!("fresh Direct destination must have a tracked epoch"); + }; + let origin = prepare_canonical(&f.target, &domain, &epoch, &f.memory) + .unwrap() + .origin_scope(); + let interrupt = || { + let mut metadata = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + let mut cursor = MaintenanceCursor::new(origin.incarnation); + cursor.advance_empty(WorkCategory::Canonical); + let control = cursor.encode(); + let update = prepare_metadata_control( + &origin, + metadata.committed().unwrap(), + &control, + &f.memory, + ) + .unwrap(); + METADATA_WRITE_CUT.with(|value| *value.borrow_mut() = Some(1)); + assert!(std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = with_private_metadata( + &mut metadata, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + update.effects(), + &mut |mutation, grant| mutation.publish_control(&update, grant), + ); + })) + .is_err()); + update.next() + }; + let initial_commit = interrupt(); + let target = AccountedMaintenanceTarget::copy_from(&f.target, &f.memory).unwrap(); + assert!(!target.accepts_legacy_confirmation()); + let (mut runtime, client) = + start_controller(target, f.memory.clone(), Admission, true).unwrap(); + client.wait_for_first_attempt(); + assert_eq!( + prepare_private_metadata(&f.target, &origin, &f.memory) + .unwrap() + .committed(), + Some(initial_commit) + ); + let next_commit = interrupt(); + assert_eq!( + client.request(Trigger::Health, true), + RequestDisposition::Enqueued + ); + assert_eq!( + client.request(Trigger::Health, false), + RequestDisposition::Enqueued + ); + assert!(client.confirm_and_wait()); + assert_eq!( + prepare_private_metadata(&f.target, &origin, &f.memory) + .unwrap() + .committed(), + Some(next_commit) + ); + runtime.stop_and_join(); + assert_eq!(std::fs::read(&legacy.database).unwrap(), b"legacy database"); + assert_eq!(std::fs::read(&legacy.manifest).unwrap(), b"legacy manifest"); + } + + fn address(number: u64) -> CursorAddress { + let mut bytes = [0; ADDRESS_BYTES]; + bytes[..8].copy_from_slice(&number.to_be_bytes()); + CursorAddress(bytes) + } + #[test] + fn dc_cursor_restart_blocked_first_and_category_rotation() { + let mut cursor = MaintenanceCursor::new([42; 32]); + let mut seen = std::collections::BTreeSet::new(); + for _ in 0..510 { + cursor = MaintenanceCursor::decode(&cursor.encode(), [42; 32]).unwrap(); + let mut selection = cursor.selection(); + // Reverse order falsifies dependence on directory enumeration order. + for n in (0..250).rev() { + selection.observe(WorkCategory::Canonical, address(n)); + } + selection.observe(WorkCategory::Run, address(999)); + let (category, candidate) = selection.selected().unwrap(); + cursor.inspected(category, candidate); // candidate 0 remains blocked. + if category == WorkCategory::Canonical { + seen.insert(candidate); + } + } + // Alternating categories prevents the large canonical namespace from + // starving even one blocked run, while canonical resumes after blocked0. + assert_eq!(seen.len(), 250); + assert!(seen.contains(&address(249))); + } + #[test] + fn dc_cursor_wrap_and_all_categories() { + let mut cursor = MaintenanceCursor::new([1; 32]); + for _ in 0..2 { + for expected in 0..CATEGORIES { + let mut selection = cursor.selection(); + for index in (0..CATEGORIES).rev() { + selection.observe(WorkCategory::from_index(index), address(1)); + } + let (category, selected) = selection.selected().unwrap(); + assert_eq!(category as usize, expected); + cursor.inspected(category, selected); + } + } + assert!(MaintenanceCursor::new([1; 32]) + .selection() + .selected() + .is_none()); + } + #[test] + fn dc_cursor_full_generation_address_and_short_adapter() { + assert_eq!(CURSOR_BYTES, 1105); + let short = [7; 64]; + assert_eq!(CursorAddress::from_short(short).as_short(), Some(short)); + let long = CursorAddress([b'g'; ADDRESS_BYTES]); + assert_eq!(long.as_short(), None); + let mut cursor = MaintenanceCursor::new([3; 32]); + cursor.inspected(WorkCategory::Generation, long); + let decoded = MaintenanceCursor::decode(&cursor.encode(), [3; 32]).unwrap(); + assert_eq!(decoded.after(WorkCategory::Generation), Some(long)); + let mut old_version = cursor.encode(); + old_version[..8].copy_from_slice(b"CGMC0001"); + let hash = Sha256::digest(&old_version[..BODY_BYTES]); + old_version[BODY_BYTES..].copy_from_slice(&hash); + assert!(matches!( + MaintenanceCursor::decode(&old_version, [3; 32]), + Err(CursorFailure::Corrupt) + )); + } + #[test] + fn dc_measurement_foreign_json_is_unknown_not_retained_coordination() { + let root = std::env::temp_dir().join(format!( + "maintenance-unknown-json-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + std::fs::create_dir(&root).unwrap(); + let path = root.join("caller-owned.json"); + let bytes = b"unregistered caller bytes"; + std::fs::write(&path, bytes).unwrap(); + let target = MaintenanceTarget { + repository_root: root.clone(), + state_root: root.clone(), + storage_root: None, + direct_pair: None, + config_path: None, + }; + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let (_, _, complete, _, unknown, deferred) = + observe_bytes_for_test(&target, &memory, &|| false).unwrap(); + assert_eq!(unknown.logical_bytes, Some(bytes.len() as u64)); + assert_eq!(deferred.logical_bytes, None); + assert!(!complete); + assert_eq!(std::fs::read(path).unwrap(), bytes); + std::fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn dc_measurement_untraversed_subtree_has_unknown_sizes() { + let root = std::env::temp_dir().join(format!( + "maintenance-unknown-subtree-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + std::fs::create_dir_all(root.join("foreign-directory")).unwrap(); + let payload = root.join("foreign-directory/untouched.bin"); + std::fs::write(&payload, b"unregistered subtree bytes").unwrap(); + let target = MaintenanceTarget { + repository_root: root.clone(), + state_root: root.clone(), + storage_root: None, + direct_pair: None, + config_path: None, + }; + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let observation = measurement::observe(&target, &memory, &|| false).unwrap(); + assert!(!observation.complete); + assert_eq!(observation.total.logical, None); + assert_eq!(observation.total.allocated, None); + assert_eq!(observation.unknown.logical, None); + assert_eq!( + std::fs::read(payload).unwrap(), + b"unregistered subtree bytes" + ); + std::fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn dc_cursor_codec_corruption_and_recreated_root() { + let cursor = MaintenanceCursor::new([3; 32]); + let mut bytes = cursor.encode(); + assert!(matches!( + MaintenanceCursor::decode(&bytes, [4; 32]), + Err(CursorFailure::DifferentInstallation) + )); + bytes[100] ^= 1; + assert!(matches!( + MaintenanceCursor::decode(&bytes, [3; 32]), + Err(CursorFailure::Corrupt) + )); + } +} + +// Runtime orchestration keeps only fixed reports in its mailbox. Candidate +// payloads stay on the joined advisory thread and never cross the real permit. +use super::inventory::{measurement, MaintenanceTarget}; +use super::maintenance_budget::{CleanupMemory, MemoryReservation, MutationGrant, ResourceLimit}; +use super::maintenance_candidate::{ + advance_candidate, finish_candidate, prepare_candidate_with_state, CandidateAdvance, + CandidateError, CandidatePreparationState, CandidateReport, CandidateStatus, PreparedCandidate, +}; +use super::maintenance_scheduler::{ + AttemptOutcome, Cancellation, DriverError, MaintenanceDriver, TargetBinding, +}; +use std::alloc::Layout; +use std::sync::{Arc, Condvar, Mutex}; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// The fixed report is independent of the last driver attempt: preparation can +/// fail before any mutation is attempted, without relabelling older evidence. +#[derive(Clone, Copy)] +pub(crate) struct ControllerObservation { + pub(crate) report: CandidateReport, + binding: Option, + legacy_binding: bool, + measurement: Option, + pub(crate) attempt_report: Option, + mutation_attempted_entries: Option, + pub(crate) observed_at_unix_ms: Option, + pub(crate) attempt_started_at_unix_ms: Option, + pub(crate) attempt_finished_at_unix_ms: Option, + pub(crate) error: Option, +} +struct ObservationState { + current: Mutex, + _reservation: MemoryReservation, + _emergency_memory: MemoryReservation, + emergency_busy: Mutex, + emergency_wake: Condvar, +} +#[derive(Clone)] +pub(crate) struct MaintenanceObserver { + state: Arc, +} +impl std::fmt::Debug for MaintenanceObserver { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter + .debug_struct("MaintenanceObserver") + .finish_non_exhaustive() + } +} +impl MaintenanceObserver { + pub(crate) fn new(memory: &CleanupMemory) -> Result { + let layout = Layout::new::<[usize; 2]>() + .extend(Layout::new::()) + .map_err(|_| ResourceLimit::ArithmeticOverflow)? + .0 + .pad_to_align(); + let reservation = memory.reserve(layout.size())?; + let emergency_memory = memory.reserve(PROJECTION_BYTES)?; + Ok(Self { + state: Arc::new(ObservationState { + current: Mutex::new(ControllerObservation { + report: CandidateReport::default(), + binding: None, + legacy_binding: false, + measurement: None, + attempt_report: None, + mutation_attempted_entries: None, + observed_at_unix_ms: None, + attempt_started_at_unix_ms: None, + attempt_finished_at_unix_ms: None, + error: None, + }), + _reservation: reservation, + _emergency_memory: emergency_memory, + emergency_busy: Mutex::new(false), + emergency_wake: Condvar::new(), + }), + }) + } + pub(crate) fn snapshot(&self) -> ControllerObservation { + *self + .state + .current + .lock() + .unwrap_or_else(|error| error.into_inner()) + } + fn update(&self, update: impl FnOnce(&mut ControllerObservation)) { + update( + &mut self + .state + .current + .lock() + .unwrap_or_else(|error| error.into_inner()), + ); + } +} + +/// Reservation is established before cloning the selected paths. The original +/// selector/runtime is caller infrastructure; this owned copy is cleanup data. +pub(crate) struct AccountedMaintenanceTarget { + target: MaintenanceTarget, + _reservation: MemoryReservation, + _arc_reservation: Option, +} +impl AccountedMaintenanceTarget { + pub(crate) fn copy_from( + target: &MaintenanceTarget, + memory: &CleanupMemory, + ) -> Result { + let mut bytes = std::mem::size_of::(); + for path in [ + Some(target.repository_root.as_path()), + Some(target.state_root.as_path()), + target.storage_root.as_deref(), + target.config_path.as_deref(), + target + .direct_pair + .as_ref() + .map(|pair| pair.database.as_path()), + target + .direct_pair + .as_ref() + .map(|pair| pair.manifest.as_path()), + ] + .into_iter() + .flatten() + { + bytes = bytes + .checked_add(path.as_os_str().len()) + .ok_or(ResourceLimit::ArithmeticOverflow)?; + } + let reservation = memory.reserve(bytes)?; + Ok(Self { + target: target.clone(), + _reservation: reservation, + _arc_reservation: None, + }) + } + pub(crate) fn from_paths( + repository: &std::path::Path, + state: &std::path::Path, + storage: Option<&std::path::Path>, + direct: Option<(&std::path::Path, &std::path::Path)>, + config: Option<&std::path::Path>, + memory: &CleanupMemory, + ) -> Result { + let mut bytes = std::mem::size_of::(); + for path in [ + Some(repository), + Some(state), + storage, + config, + direct.map(|pair| pair.0), + direct.map(|pair| pair.1), + ] + .into_iter() + .flatten() + { + bytes = bytes + .checked_add(path.as_os_str().len()) + .ok_or(ResourceLimit::ArithmeticOverflow)?; + } + let reservation = memory.reserve(bytes)?; + Ok(Self { + target: MaintenanceTarget { + repository_root: repository.to_path_buf(), + state_root: state.to_path_buf(), + storage_root: storage.map(std::path::Path::to_path_buf), + direct_pair: direct.map(|(database, manifest)| super::inventory::DirectPair { + database: database.to_path_buf(), + manifest: manifest.to_path_buf(), + }), + config_path: config.map(std::path::Path::to_path_buf), + }, + _reservation: reservation, + _arc_reservation: None, + }) + } + + pub(crate) fn target(&self) -> &MaintenanceTarget { + &self.target + } + fn accepts_legacy_confirmation(&self) -> bool { + self.target.direct_pair.is_none() + } +} + +pub(crate) struct MaintenanceController { + target: AccountedMaintenanceTarget, + observer: MaintenanceObserver, + last_advance: Option, + _admission: A, + preparation: CandidatePreparationState, + binding: Option< + super::maintenance_budget::MemoryBox, + >, +} +#[inline(never)] +fn prepare_controller_binding( + target: &super::inventory::MaintenanceTarget, + memory: &CleanupMemory, +) -> Result< + super::maintenance_budget::MemoryBox, + super::locks::FixedControlError, +> { + let binding = super::maintenance_admission::prepare_target_binding(target, memory)?; + super::maintenance_budget::MemoryBox::new(binding, memory) + .map_err(super::locks::FixedControlError::Resource) +} + +impl MaintenanceController { + pub(crate) fn new( + target: AccountedMaintenanceTarget, + observer: MaintenanceObserver, + admission: A, + ) -> Self { + Self { + target, + observer, + last_advance: None, + _admission: admission, + preparation: CandidatePreparationState::default(), + binding: None, + } + } + fn failed(&self, error: CandidateError) -> DriverError { + self.observer.update(|observation| { + observation.error = Some(error); + observation.report.pending = true; + observation.report.observations_complete = false; + }); + match error { + CandidateError::ResourceLimit => DriverError::ResourceLimit, + _ => DriverError::Preparation, + } + } +} +impl MaintenanceDriver for MaintenanceController { + type Work = PreparedCandidate; + fn completed(&mut self, _outcome: AttemptOutcome) { + self.observer.update(|observation| { + observation.attempt_finished_at_unix_ms = Some(controller_unix_ms()); + }); + } + fn desired_binding(&self) -> Option { + self.binding + .as_ref() + .map(|binding| TargetBinding(binding.digest())) + } + fn prepare( + &mut self, + binding: TargetBinding, + cancellation: &Cancellation<'_>, + memory: &CleanupMemory, + ) -> Result, DriverError> { + self.last_advance = None; + if cancellation.is_cancelled() { + self.preparation.clear(); + return Err(self.failed(CandidateError::Cancelled)); + } + self.observer.update(|observation| { + observation.attempt_started_at_unix_ms = Some(controller_unix_ms()); + observation.attempt_finished_at_unix_ms = None; + observation.attempt_report = None; + observation.mutation_attempted_entries = None; + observation.error = None; + }); + let current = self + .binding + .as_mut() + .is_some_and(|binding| binding.is_current().unwrap_or(false)); + if !current { + self.preparation.clear(); + self.binding = None; + let witness = prepare_controller_binding(self.target.target(), memory) + .map_err(|_| self.failed(CandidateError::Stale))?; + self.binding = Some(witness); + self.observer.update(|observation| { + observation.binding = self.desired_binding(); + observation.legacy_binding = self + .binding + .as_ref() + .is_some_and(|binding| binding.is_legacy()); + observation.measurement = None; + observation.observed_at_unix_ms = None; + observation.report = CandidateReport::default(); + }); + } + if self.desired_binding() != Some(binding) { + return Err(self.failed(CandidateError::Stale)); + } + let measurement = measurement::observe(self.target.target(), memory, &|| { + cancellation.is_cancelled() + }) + .map_err(|_| self.failed(CandidateError::ResourceLimit))?; + self.observer.update(|observation| { + observation.measurement = Some(measurement); + observation.observed_at_unix_ms = Some(controller_unix_ms()); + }); + self.preparation + .set_legacy_interest(cancellation.legacy_confirmation_pending()); + self.preparation + .set_legacy_confirmation_metadata(cancellation.legacy_confirmation_metadata()); + let mut report = CandidateReport::default(); + let prepared = prepare_candidate_with_state( + self.target.target(), + self.binding + .as_ref() + .expect("retained current target witness"), + &mut self.preparation, + memory, + &|| cancellation.is_cancelled(), + &mut report, + ) + .map_err(|error| self.failed(error))?; + self.observer.update(|observation| { + observation.report = report; + observation.attempt_report = Some(report); + }); + Ok(prepared) + } + fn requires_legacy_confirmation(&self, work: &Self::Work) -> bool { + work.requires_legacy_confirmation() + } + fn source_payloads_parked(&self) -> bool { + self.preparation.has_parked_payloads() + } + fn mutate( + &mut self, + work: &Self::Work, + grant: &mut MutationGrant, + confirmation: Option< + &crate::storage::maintenance_scheduler::LegacyQuiescenceConfirmation<'_>, + >, + ) -> Result { + let starting_entries = grant.remaining_entries(); + let result = advance_candidate(work.borrow_hot_frame(), grant, confirmation); + self.observer.update(|observation| { + observation.mutation_attempted_entries = + Some(starting_entries.saturating_sub(grant.remaining_entries())); + }); + let advance = result.map_err(|error| { + let failure = self.failed(error); + if failure == DriverError::ResourceLimit { + failure + } else { + DriverError::Mutation + } + })?; + self.last_advance = Some(advance); + self.observer.update(|observation| { + observation.attempt_report = Some(advance.report); + }); + Ok(candidate_outcome(advance.status)) + } + fn finish( + &mut self, + work: &mut Self::Work, + outcome: AttemptOutcome, + cancellation: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result { + let Some(advance) = self.last_advance.take() else { + return Ok(outcome); + }; + let report = finish_candidate(work, advance, &mut self.preparation, &|| { + cancellation.is_cancelled() + }) + .map_err(|error| self.failed(error))?; + self.observer.update(|observation| { + observation.report = report; + observation.attempt_report = Some(report); + }); + if cancellation.is_cancelled() { + return Ok(AttemptOutcome::Cancelled); + } + if report.pending { + Ok(AttemptOutcome::Pending) + } else { + Ok(candidate_outcome(advance.status)) + } + } +} +fn candidate_outcome(status: CandidateStatus) -> AttemptOutcome { + match status { + CandidateStatus::Complete => AttemptOutcome::Complete, + CandidateStatus::Pending | CandidateStatus::NeedsDisposal => AttemptOutcome::Pending, + CandidateStatus::Deferred => AttemptOutcome::Deferred, + } +} +fn controller_unix_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() + .min(u128::from(u64::MAX)) as u64 +} + +use super::maintenance_scheduler::{ + DriverLiveness, MaintenanceAdmission, MaintenanceHandle, MaintenanceRuntime, + RequestDisposition, Trigger, +}; +use crate::protocol::{MaintenanceAttempt, MaintenanceAttemptStatus, MaintenanceSummary}; + +/// Cleanup has its own aggregate requested-layout ceiling, below the normal +/// native worker budget. It is shared by target copies, reports and candidates. +pub(crate) const MAINTENANCE_MEMORY_BYTES: usize = 8 * 1024 * 1024; + +pub(crate) struct MaintenanceClient { + handle: MaintenanceHandle, + observer: MaintenanceObserver, + memory: CleanupMemory, + target: Arc, +} +impl Clone for MaintenanceClient { + fn clone(&self) -> Self { + Self { + handle: self.handle.clone(), + observer: self.observer.clone(), + memory: self.memory.clone(), + target: Arc::clone(&self.target), + } + } +} +impl std::fmt::Debug for MaintenanceClient { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter + .debug_struct("MaintenanceClient") + .finish_non_exhaustive() + } +} +impl MaintenanceClient { + pub(crate) fn request(&self, trigger: Trigger, confirm: bool) -> RequestDisposition { + let confirm = confirm && self.target.accepts_legacy_confirmation(); + let Some(binding) = self.observer.snapshot().binding else { + return if confirm { + RequestDisposition::UnboundConfirmation + } else { + self.handle.request(trigger, None) + }; + }; + let _capture = if confirm { + match self.handle.begin_source_capture() { + Some(capture) => Some(capture), + None => return RequestDisposition::UnboundConfirmation, + } + } else { + None + }; + let source = if confirm { + let source = match super::maintenance_admission::prepare_legacy_confirmation_source( + self.target.target(), + &self.memory, + ) { + Ok(source) => source, + Err(_) => return RequestDisposition::UnboundConfirmation, + }; + match super::maintenance_budget::MemoryBox::new(source, &self.memory) { + Ok(source) => Some(source), + Err(_) => return RequestDisposition::UnboundConfirmation, + } + } else { + None + }; + self.handle.rebind(binding); + self.handle + .request_with_source(trigger, confirm.then_some(binding), source) + } + pub(crate) fn wait_for_first_attempt(&self) { + loop { + let snapshot = self.handle.wait_for_first_attempt(); + if matches!( + snapshot.liveness, + DriverLiveness::Stopped | DriverLiveness::Failed + ) || snapshot.last_attempt.is_some_and(|attempt| { + attempt.binding == snapshot.binding && attempt.generation == snapshot.generation + }) { + return; + } + // Initial legacy witness acquisition changes the placeholder + // binding. That preparation-only handoff is not the transient + // owner's promised advisory pass for the now-bound destination. + } + } + pub(crate) fn confirm_and_wait(&self) -> bool { + // A placeholder-generation handoff is not the bound target's startup + // pass. Wait for that pass and its payload disposal before reserving + // request-time source pins; ordinary busy/parked checks still apply. + self.wait_for_first_attempt(); + if !self.target.accepts_legacy_confirmation() { + let binding = self.observer.snapshot().binding; + let snapshot = self.handle.request_and_wait(Trigger::Health, None); + return binding == Some(snapshot.binding) + && !matches!( + snapshot.liveness, + DriverLiveness::Failed | DriverLiveness::Stopped + ); + } + if let Some(binding) = self.observer.snapshot().binding { + let Some(capture) = self.handle.begin_source_capture() else { + return false; + }; + let source = match super::maintenance_admission::prepare_legacy_confirmation_source( + self.target.target(), + &self.memory, + ) + .and_then(|source| { + super::maintenance_budget::MemoryBox::new(source, &self.memory) + .map_err(super::locks::FixedControlError::Resource) + }) { + Ok(source) => source, + Err(_) => return false, + }; + self.handle.rebind(binding); + let disposition = + self.handle + .request_with_source(Trigger::Health, Some(binding), Some(source)); + drop(capture); + if disposition != RequestDisposition::Enqueued { + return false; + } + let snapshot = self.handle.request_and_wait(Trigger::Health, None); + return snapshot.binding == binding + && !matches!(snapshot.liveness, DriverLiveness::Failed); + } + false + } + pub(crate) fn projection(&self) -> MaintenanceProjection { + match self.memory.reserve(PROJECTION_BYTES) { + Ok(reservation) => MaintenanceProjection { + summary: self.build_summary(), + _reservation: Some(reservation), + _emergency: None, + }, + Err(_) => { + // Real coordinator execution is serialized. The parked fallback + // also serializes unusual internal direct Core clones, retaining + // one startup-precharged minimal projection through handoff. + let mut busy = self + .observer + .state + .emergency_busy + .lock() + .unwrap_or_else(|error| error.into_inner()); + while *busy { + busy = self + .observer + .state + .emergency_wake + .wait(busy) + .unwrap_or_else(|error| error.into_inner()); + } + *busy = true; + drop(busy); + let emergency = EmergencyProjection { + state: Arc::clone(&self.observer.state), + }; + let mut summary = MaintenanceSummary { + requested: self.handle.snapshot().requested, + ..MaintenanceSummary::default() + }; + summary + .reasons + .push("maintenance_projection_resource_limit".into()); + summary + .errors + .push("maintenance memory ceiling reached while reporting".into()); + MaintenanceProjection { + summary, + _reservation: None, + _emergency: Some(emergency), + } + } + } + } + + fn build_summary(&self) -> MaintenanceSummary { + let driver = self.handle.snapshot(); + let observation = self.observer.snapshot(); + let mut summary = MaintenanceSummary { + requested: driver.requested, + running: driver.running, + pending: driver.pending + || observation.report.pending + || observation.report.unknown + || observation.observed_at_unix_ms.is_none() + || observation.error.is_some(), + observed_at_unix_ms: observation.observed_at_unix_ms, + observation_age_ms: observation + .observed_at_unix_ms + .map(|at| controller_unix_ms().saturating_sub(at)), + observation_complete: observation.observed_at_unix_ms.is_some() + && observation.report.observations_complete + && observation.report.checked_categories == u8::MAX, + measurement_complete: observation + .measurement + .is_some_and(|measurement| measurement.complete), + ..MaintenanceSummary::default() + }; + if let Some(measurement) = observation.measurement { + let bucket = |value: measurement::Bucket| crate::protocol::MaintenanceByteBucket { + logical_bytes: value.logical, + allocated_bytes: value.allocated, + }; + summary.bytes.total = bucket(measurement.total); + summary.bytes.retained = bucket(measurement.retained); + summary.bytes.deferred = bucket(measurement.deferred); + summary.bytes.unknown = bucket(measurement.unknown); + summary.bytes.by_category = crate::protocol::MaintenanceCategoryBytes { + active_graph: bucket(measurement.categories[0]), + active_cache: bucket(measurement.categories[1]), + leased_generation: bucket(measurement.categories[2]), + live_run: bucket(measurement.categories[3]), + recovery_evidence: bucket(measurement.categories[4]), + artifact_temporary: bucket(measurement.categories[5]), + legacy_staging: bucket(measurement.categories[6]), + retired_direct: bucket(measurement.categories[7]), + coordination: bucket(measurement.categories[8]), + unknown: bucket(measurement.categories[9]), + }; + } + summary.reasons.clear(); + if observation.legacy_binding { + summary.reasons.push("legacy_quiescence_required".into()); + } + if let Some(measurement) = observation.measurement { + if !measurement.complete { + summary + .reasons + .push("maintenance_measurement_partial_lower_bounds".into()); + } + if !measurement.references_complete { + summary + .reasons + .push("active_cache_references_incomplete".into()); + } + if measurement.resource_limited { + summary + .reasons + .push("maintenance_measurement_resource_limit".into()); + } + if measurement.allocation_unknown { + summary.reasons.push("allocated_bytes_unavailable".into()); + } + if measurement.unknown_entries { + summary.reasons.push("unknown_entries_retained".into()); + } + } + if observation.observed_at_unix_ms.is_none() { + summary.reasons.push("maintenance_not_observed".into()); + } + for bit in 0..64 { + if observation.report.reasons & (1u64 << bit) != 0 { + if let Some(reason) = super::maintenance_candidate::candidate_reason(bit) { + summary.reasons.push(reason.into()); + } + } + } + if observation.report.deferred { + summary + .reasons + .push("maintenance_candidate_deferred".into()); + } + if observation.report.unknown { + summary.reasons.push("maintenance_candidate_unknown".into()); + } + if !summary.observation_complete { + summary + .reasons + .push("maintenance_observation_incomplete".into()); + } + if let Some(error) = observation.error { + summary.errors.push( + match error { + CandidateError::ResourceLimit => "maintenance memory ceiling reached", + CandidateError::Io(_) => "maintenance filesystem observation failed", + CandidateError::Stale => "maintenance target changed during preparation", + CandidateError::InvalidEvidence => "maintenance evidence is invalid or unknown", + CandidateError::Cancelled => "maintenance cancelled", + } + .into(), + ); + } + if let Some(failure) = driver.failure { + summary + .errors + .push(format!("maintenance worker failed: {failure:?}")); + } + if driver.liveness == DriverLiveness::Stopped { + summary.reasons.push("maintenance_stopped".into()); + } + if let Some(started) = observation.attempt_started_at_unix_ms { + let finished = observation.attempt_finished_at_unix_ms; + summary.last_attempt = Some(MaintenanceAttempt { + started_at_unix_ms: started, + finished_at_unix_ms: finished, + status: finished.map(|_| { + if observation.error.is_some() { + MaintenanceAttemptStatus::Invalid + } else if observation.report.deferred { + MaintenanceAttemptStatus::Deferred + } else if observation.report.pending + || !observation.report.observations_complete + { + MaintenanceAttemptStatus::Partial + } else { + MaintenanceAttemptStatus::Complete + } + }), + attempted_entries: finished.and(observation.mutation_attempted_entries), + deleted_entries: finished + .and(observation.attempt_report) + .and_then(|report| usize::try_from(report.reclaimed_entries).ok()), + reclaimed_allocated_bytes: finished + .and(observation.attempt_report) + .and_then(|report| report.reclaimed_allocated_bytes), + continuation_phase: None, + }); + } + summary + } +} + +// Fixed source bounds: <=80 static reasons (each <=64 bytes), <=2 fixed-code +// errors (each <=128 bytes), 16 byte buckets and one fixed attempt. 256 KiB +// conservatively covers the DTO, Value/BTree nodes, structured wrapper and block +// rendering copies simultaneously. Normal graph/application payload is caller +// output, not maintenance working memory. The guard survives private formatting +// and serialization and ends at the public owned-response handoff. +const PROJECTION_BYTES: usize = 256 * 1024; +struct EmergencyProjection { + state: Arc, +} +impl Drop for EmergencyProjection { + fn drop(&mut self) { + *self + .state + .emergency_busy + .lock() + .unwrap_or_else(|error| error.into_inner()) = false; + self.state.emergency_wake.notify_all(); + } +} +pub(crate) struct MaintenanceProjection { + pub(crate) summary: MaintenanceSummary, + _reservation: Option, + _emergency: Option, +} +impl MaintenanceProjection { + pub(crate) fn unavailable(error: DriverError) -> Self { + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).ok(); + let reservation = memory + .as_ref() + .and_then(|memory| memory.reserve(PROJECTION_BYTES).ok()); + let mut summary = MaintenanceSummary::default(); + summary + .reasons + .push("maintenance_initialization_failed".into()); + summary + .errors + .push(format!("maintenance unavailable: {error:?}")); + Self { + summary, + _reservation: reservation, + _emergency: None, + } + } +} + +pub(crate) fn start_controller( + target: AccountedMaintenanceTarget, + memory: CleanupMemory, + admission: A, + confirmation: bool, +) -> Result<(MaintenanceRuntime, MaintenanceClient), DriverError> { + let binding = + super::maintenance_admission::observe_maintenance_binding(target.target(), &memory).ok(); + let initial_source = if confirmation + && target.accepts_legacy_confirmation() + && binding.is_some() + { + super::maintenance_admission::prepare_legacy_confirmation_source(target.target(), &memory) + .ok() + .and_then(|source| super::maintenance_budget::MemoryBox::new(source, &memory).ok()) + } else { + None + }; + let initial_confirmation = initial_source.is_some(); + let binding = binding.unwrap_or([0; 32]); + let observer = MaintenanceObserver::new(&memory).map_err(|_| DriverError::ResourceLimit)?; + let worker_target = AccountedMaintenanceTarget::copy_from(target.target(), &memory) + .map_err(|_| DriverError::ResourceLimit)?; + let client_target = target; + let arc_layout = Layout::new::<[usize; 2]>() + .extend(Layout::new::()) + .map_err(|_| DriverError::ResourceLimit)? + .0 + .pad_to_align(); + // Retain Arc's control words together with this target's owned buffers. + let mut client_target = client_target; + let arc_reservation = memory + .reserve(arc_layout.size()) + .map_err(|_| DriverError::ResourceLimit)?; + client_target._arc_reservation = Some(arc_reservation); + let target = Arc::new(client_target); + let worker_observer = observer.clone(); + let worker_admission = admission.clone(); + let factory = move |_: &CleanupMemory| { + Ok(MaintenanceController::new( + worker_target, + worker_observer, + worker_admission, + )) + }; + let runtime = if initial_confirmation { + MaintenanceRuntime::start_with_confirmation( + memory.clone(), + TargetBinding(binding), + admission, + factory, + true, + initial_source, + ) + } else { + MaintenanceRuntime::start(memory.clone(), TargetBinding(binding), admission, factory) + } + .map_err(|_| DriverError::ResourceLimit)?; + let client = MaintenanceClient { + handle: runtime.handle(), + observer, + memory, + target, + }; + Ok((runtime, client)) +} + +#[cfg(test)] +pub(crate) fn observe_bytes_for_test( + target: &MaintenanceTarget, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result< + ( + crate::protocol::MaintenanceByteBucket, + crate::protocol::MaintenanceByteBucket, + bool, + bool, + crate::protocol::MaintenanceByteBucket, + crate::protocol::MaintenanceByteBucket, + ), + ResourceLimit, +> { + let observation = measurement::observe(target, memory, cancel)?; + let bucket = |value: measurement::Bucket| crate::protocol::MaintenanceByteBucket { + logical_bytes: value.logical, + allocated_bytes: value.allocated, + }; + Ok(( + bucket(observation.categories[0]), + bucket(observation.categories[1]), + observation.complete, + observation.references_complete, + bucket(observation.unknown), + bucket(observation.deferred), + )) +} diff --git a/src/storage/maintenance_admission.rs b/src/storage/maintenance_admission.rs new file mode 100644 index 00000000..d364b322 --- /dev/null +++ b/src/storage/maintenance_admission.rs @@ -0,0 +1,2510 @@ +//! Bounded mutation admission. Prepared values own advisory facts/buffers only; +//! actual non-Send guards exist solely during a borrowed hot-frame callback. +use super::inventory::MaintenanceTarget; +use super::layout::{DirectLayout, ManagedLayout}; +use super::locks::{ + open_fixed_control, FixedControlError, FixedControlKind as Kind, + FixedControlProblem as Problem, FixedControlScratch, FixedFileStamp, FixedLockedFile, LockMode, + RootedDirectory, +}; +use super::maintenance_budget::{ + CleanupMemory, MemoryBox, MemoryReservation, MutationGrant, ReservedBytes, +}; +use super::maintenance_ownership::{ + self as ownership, CanonicalEpochAdvance, EpochLock, PreparedEpoch, ReferenceDomain, + ReferenceEpoch, +}; +use super::maintenance_projection::{project_binding, BoundConfiguration, ProjectionFailure}; +use std::path::{Path, PathBuf}; + +pub(crate) type AdmissionError = FixedControlError; +pub(crate) type EpochAdvance = CanonicalEpochAdvance; +fn invalid(problem: Problem) -> AdmissionError { + AdmissionError::Invalid(problem) +} +fn projection(error: ProjectionFailure) -> AdmissionError { + match error { + ProjectionFailure::Resource(error) => AdmissionError::Resource(error), + _ => invalid(Problem::InvalidRecord), + } +} +#[derive(Clone)] +struct Entry { + path: PathBuf, + kind: Kind, + stamp: Option, +} +impl Entry { + fn prepare( + path: PathBuf, + kind: Kind, + optional: bool, + scratch: &mut FixedControlScratch, + ) -> Result { + let stamp = match open_fixed_control(&path, kind, false, scratch) { + Ok(file) => Some(FixedFileStamp::capture(&file, kind)?), + Err(AdmissionError::Io(error)) + if optional && error.kind() == std::io::ErrorKind::NotFound => + { + None + } + Err(error) => return Err(error), + }; + Ok(Self { path, kind, stamp }) + } + fn check( + &self, + scratch: &mut FixedControlScratch, + grant: &mut MutationGrant, + ) -> Result<(), AdmissionError> { + check_entry(&self.path, self.kind, self.stamp, scratch, grant) + } + fn lock( + &self, + scratch: &mut FixedControlScratch, + grant: &mut MutationGrant, + ) -> Result { + grant.take_entry()?; + let file = open_fixed_control(&self.path, Kind::File, true, scratch)?; + self.stamp + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .recheck(&file)?; + FixedLockedFile::try_lock(file, LockMode::Exclusive)?.ok_or_else(|| invalid(Problem::Busy)) + } +} +fn check_entry( + path: &Path, + kind: Kind, + stamp: Option, + scratch: &mut FixedControlScratch, + grant: &mut MutationGrant, +) -> Result<(), AdmissionError> { + grant.take_entry()?; + check_entry_identity(path, kind, stamp, scratch) +} +fn check_entry_identity( + path: &Path, + kind: Kind, + stamp: Option, + scratch: &mut FixedControlScratch, +) -> Result<(), AdmissionError> { + match (open_fixed_control(path, kind, false, scratch), stamp) { + (Ok(file), Some(stamp)) => stamp.recheck(&file), + (Err(AdmissionError::Io(error)), None) if error.kind() == std::io::ErrorKind::NotFound => { + Ok(()) + } + (Err(error), _) => Err(error), + _ => Err(invalid(Problem::Changed)), + } +} + +/// OWNED and movable preparation. No self references or storage authority. +pub(crate) struct PreparedCanonical { + epoch: PreparedEpoch, + binding: BoundConfiguration, + repository: Entry, + state: Entry, + locks: [Option; 10], + selector_index: usize, + root: Entry, + destination: Entry, + publisher_binding: Entry, + origin: RootOriginBinding, + workers: Entry, + phases: Entry, + worker_state: Entry, + direct: bool, + read: ReservedBytes, + scratch: FixedControlScratch, + directory_scratch: [u8; 4096], + _floor: MemoryReservation, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct RootOriginBinding { + pub(crate) root: super::locks::FileIdentity, + pub(crate) coordination: super::locks::FileIdentity, + pub(crate) incarnation: [u8; 32], + pub(crate) cohort: super::locks::FileIdentity, + pub(crate) publication_binding: super::locks::FileIdentity, +} +pub(crate) struct CanonicalMutation<'a> { + directory: &'a RootedDirectory, + root_use: &'a FixedLockedFile, + selector: &'a FixedLockedFile, + origin: RootOriginBinding, + advance: EpochAdvance, + _same_thread: std::marker::PhantomData>, +} +impl CanonicalMutation<'_> { + pub(crate) fn directory(&self) -> &RootedDirectory { + self.directory + } + pub(crate) fn origin_scope(&self) -> RootOriginBinding { + self.origin + } + pub(crate) fn epoch(&self) -> EpochAdvance { + self.advance + } +} + +/// Capture E BEFORE complete reference/origin preparation. The caller owns that +/// complete reference result; this seal binds its same immutable E to current +/// configuration, physical namespace and constant-count lock/registry checks. +pub(crate) fn prepare_canonical( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + memory: &CleanupMemory, +) -> Result { + let binding = project_binding(target, memory).map_err(projection)?; + let floor = memory + .reserve( + super::maintenance_projection::path_charge( + [ + Some(target.repository_root.as_path()), + Some(target.state_root.as_path()), + Some(binding.storage.as_path()), + Some(binding.config_path.as_path()), + Some(domain.artifact_root()), + binding.direct.as_ref().map(|p| p.database.as_path()), + binding.direct.as_ref().map(|p| p.manifest.as_path()), + ] + .into_iter() + .flatten(), + 32, + std::mem::size_of::(), + ) + .map_err(AdmissionError::Resource)?, + ) + .map_err(AdmissionError::Resource)?; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(AdmissionError::Resource)?, + )?; + let mut read = memory + .reserve(2048) + .map_err(AdmissionError::Resource)? + .bytes(2048) + .map_err(AdmissionError::Resource)?; + let (actual, worker, worker_state, workers, phases, state_lock, direct) = + if let Some(pair) = &binding.direct { + let layout = DirectLayout::new(&pair.database, &pair.manifest); + ( + ReferenceDomain::direct(&layout), + layout.worker_lock_path(), + layout.worker_state_path(), + layout.worker_workspace_root_path(), + layout.phase_workspace_root(), + None, + true, + ) + } else { + let layout = ManagedLayout::new(&binding.storage); + ( + ReferenceDomain::managed(&layout), + layout.worker_lock_path(), + layout.worker_state_path(), + binding.storage.join("workers"), + layout.phase_workspace_root(), + Some(layout.state_lock_path()), + false, + ) + }; + let actual = actual.map_err(|_| invalid(Problem::InvalidRecord))?; + if !actual.same_scope(domain) { + return Err(invalid(Problem::Changed)); + } + let epoch = PreparedEpoch::prepare(domain, expected, memory, &mut read, &mut scratch)?; + let frame = epoch.frame(); + let mut locks: [Option; 10] = std::array::from_fn(|_| None); + locks[0] = Some(Entry::prepare( + frame.paths.writer.to_owned(), + Kind::File, + false, + &mut scratch, + )?); + locks[1] = Some(Entry::prepare( + frame.paths.root_use.to_owned(), + Kind::File, + false, + &mut scratch, + )?); + locks[2] = Some(Entry::prepare(worker, Kind::File, false, &mut scratch)?); + let mut count = 3; + if let Some(path) = state_lock { + locks[count] = Some(Entry::prepare(path, Kind::File, false, &mut scratch)?); + count += 1; + } + if let Some(pair) = &binding.direct { + for resource in [&pair.database, &pair.manifest] { + let (parent, basename) = ownership::database_lock_path(resource, false) + .map_err(|_| invalid(Problem::InvalidPath))?; + for path in [parent.join(".database-use-family.lock"), basename] { + let entry = Entry::prepare(path, Kind::File, false, &mut scratch)?; + if locks + .iter() + .flatten() + .any(|old| old.stamp.map(|s| s.identity()) == entry.stamp.map(|s| s.identity())) + { + continue; + } + locks[count] = Some(entry); + count += 1; + } + } + } + let destination = Entry::prepare( + frame.paths.destination.to_owned(), + Kind::Directory, + false, + &mut scratch, + )?; + let publisher_binding = Entry::prepare( + frame.paths.root.join(".publication.binding"), + Kind::File, + false, + &mut scratch, + )?; + let (root_id, coordination, incarnation, cohort) = epoch.root_facts(); + let mut binding_file = + open_fixed_control(&publisher_binding.path, Kind::File, false, &mut scratch)?; + let binding_stamp = publisher_binding.stamp.unwrap(); + let len = super::locks::read_fixed_control(&mut binding_file, &binding_stamp, &mut read)?; + let publisher = super::maintenance_controls::PublisherBinding::decode(&read.as_slice()[..len]) + .map_err(|_| invalid(Problem::InvalidRecord))?; + if publisher.root != root_id + || publisher.coordination != coordination + || publisher.incarnation != incarnation + { + return Err(invalid(Problem::Changed)); + } + let selector = Entry::prepare( + frame.paths.root.join(".publication.selector"), + Kind::File, + false, + &mut scratch, + )?; + if selector.stamp.unwrap().identity() != publisher.selector { + return Err(invalid(Problem::Changed)); + } + locks[count] = Some(selector); + let origin = RootOriginBinding { + root: root_id, + coordination, + incarnation, + cohort, + publication_binding: binding_stamp.identity(), + }; + let root = Entry::prepare( + frame.paths.root.to_owned(), + Kind::Directory, + false, + &mut scratch, + )?; + let repository = Entry::prepare( + target.repository_root.clone(), + Kind::Directory, + false, + &mut scratch, + )?; + let state = Entry::prepare( + target.state_root.clone(), + Kind::Directory, + false, + &mut scratch, + )?; + let workers = Entry::prepare(workers, Kind::Directory, true, &mut scratch)?; + let phases = Entry::prepare(phases, Kind::Directory, true, &mut scratch)?; + let worker_state = Entry::prepare(worker_state, Kind::File, true, &mut scratch)?; + // A descriptor is process authority, not an inert file. Until its bounded + // projection/kernel proof is supplied, retain it rather than infer idle. + if worker_state.stamp.is_some() { + return Err(invalid(Problem::Busy)); + } + Ok(PreparedCanonical { + epoch, + binding, + repository, + state, + locks, + selector_index: count, + root, + destination, + publisher_binding, + origin, + workers, + phases, + worker_state, + direct, + read, + scratch, + directory_scratch: [0; 4096], + _floor: floor, + }) +} + +/// Fresh grant starts in the real dispatcher BEFORE this call. `effect_entries` +/// reserves the ENTIRE known callback effect cost before E changes. Each effect +/// uses check_time; it must not try to renew the grant or park this authority. +pub(crate) fn with_prepared_canonical( + prepared: &mut PreparedCanonical, + grant: &mut MutationGrant, + effect_entries: usize, + apply: &mut impl FnMut(&CanonicalMutation<'_>, &mut MutationGrant) -> Result<(), AdmissionError>, +) -> Result { + grant.check()?; + let PreparedCanonical { + epoch, + binding, + repository, + state, + locks, + selector_index, + root, + destination, + publisher_binding, + origin, + workers, + phases, + worker_state, + direct, + read, + scratch, + directory_scratch, + .. + } = prepared; + let mut held: [Option; 10] = std::array::from_fn(|_| None); + for (index, entry) in locks.iter().enumerate() { + if let Some(entry) = entry { + held[index] = Some(entry.lock(scratch, grant)?); + } + } + let root_identity = root + .stamp + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .identity(); + grant.take_entry()?; + let directory = RootedDirectory::open_fixed(&root.path, root_identity, true, scratch)?; + grant.take_entry()?; + let _destination_directory = RootedDirectory::open_fixed( + &destination.path, + destination.stamp.unwrap().identity(), + true, + scratch, + )?; + let frame = epoch.frame(); + ownership::with_canonical_epoch_handles( + &frame, + EpochLock::Fixed(held[0].as_ref().unwrap()), + EpochLock::Fixed(held[1].as_ref().unwrap()), + read, + scratch, + grant, + effect_entries, + &mut |scratch, grant| { + repository.check(scratch, grant)?; + state.check(scratch, grant)?; + if repository.stamp.map(|s| s.identity()) != Some(binding.repository) + || state.stamp.map(|s| s.identity()) != Some(binding.state) + { + return Err(invalid(Problem::Changed)); + } + check_entry( + &binding.config_path, + Kind::File, + binding.config_stamp, + scratch, + grant, + )?; + publisher_binding.check(scratch, grant)?; + worker_state.check(scratch, grant)?; + for entry in locks.iter().flatten() { + entry.check(scratch, grant)?; + } + workers.check(scratch, grant)?; + phases.check(scratch, grant)?; + if let Some(stamp) = workers.stamp { + grant.take_entry()?; + let worker_directory = + RootedDirectory::open_fixed(&workers.path, stamp.identity(), false, scratch)?; + if !worker_directory.contains_only( + if *direct && phases.stamp.is_some() { + Some(b"phases") + } else { + None + }, + directory_scratch, + grant, + )? { + return Err(invalid(Problem::Busy)); + } + } + if let Some(stamp) = phases.stamp { + grant.take_entry()?; + let phase_directory = + RootedDirectory::open_fixed(&phases.path, stamp.identity(), false, scratch)?; + if !phase_directory.contains_only(None, directory_scratch, grant)? { + return Err(invalid(Problem::Busy)); + } + } + Ok(()) + }, + &mut |advance, grant| { + apply( + &CanonicalMutation { + directory: &directory, + root_use: held[1].as_ref().unwrap(), + selector: held[*selector_index].as_ref().unwrap(), + origin: *origin, + advance, + _same_thread: std::marker::PhantomData, + }, + grant, + ) + }, + ) +} + +pub(crate) fn observe_maintenance_binding( + target: &MaintenanceTarget, + memory: &CleanupMemory, +) -> Result<[u8; 32], AdmissionError> { + let mut binding = prepare_target_binding(target, memory)?; + if binding.is_legacy() { + return Err(invalid(Problem::InvalidRecord)); + } + binding.is_current()?; + Ok(binding.digest()) +} + +#[cfg(test)] +pub(crate) mod tests { + #[test] + fn private_metadata_owner_keeps_preparation_images_in_charged_boxes() { + // The former inline scratch/epoch/config owner exceeded this fixed + // boundary; the shipping boxed factory must keep its result compact. + assert!(std::mem::size_of::() <= 2048); + let _boxed_fields = |value: &super::PreparedPrivateMetadata| { + let _: &super::MemoryBox = &value.scratch; + let _: &super::MemoryBox = &value._epoch; + let _: &super::MemoryBox = &value._binding; + }; + } + use super::super::layout::{create_fresh_reference_root, managed_generation_id}; + use super::super::locks::{open_locked, try_open_existing_locked}; + use super::super::maintenance_ownership::{ + initialize_fresh_reference_domain, observe_reference_epoch, EpochObservation, + ReferenceMutation, + }; + use super::*; + use std::fs; + pub(crate) struct Fixture { + root: PathBuf, + pub(crate) target: MaintenanceTarget, + pub(crate) layout: ManagedLayout, + pub(crate) domain: ReferenceDomain, + pub(crate) memory: CleanupMemory, + } + impl Fixture { + pub(crate) fn new() -> Self { + let root = std::env::temp_dir().join(format!("admit-{}", managed_generation_id())); + fs::create_dir(&root).unwrap(); + let root = fs::canonicalize(root).unwrap(); + let state = root.join(".codebaseGraph"); + fs::create_dir(&state).unwrap(); + fs::write(state.join("config.json"), br#"{"schema_version":3}"#).unwrap(); + let layout = ManagedLayout::new(state.join("storage")); + let fresh = create_fresh_reference_root(layout.storage_root()) + .unwrap() + .unwrap(); + layout.ensure_roots().unwrap(); + fs::create_dir(layout.storage_root().join("workers")).unwrap(); + fs::create_dir(layout.phase_workspace_root()).unwrap(); + for path in [ + layout.writer_lock_path(), + layout.worker_lock_path(), + layout.state_lock_path(), + ] { + drop(open_locked(path, LockMode::Exclusive).unwrap()); + } + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + drop(writer); + Self { + target: MaintenanceTarget { + repository_root: root.clone(), + state_root: state, + storage_root: None, + direct_pair: None, + config_path: None, + }, + root, + layout, + domain, + memory: CleanupMemory::new(8 * 1024 * 1024).unwrap(), + } + } + pub(crate) fn epoch(&self) -> ReferenceEpoch { + match observe_reference_epoch(&self.domain).unwrap() { + EpochObservation::Verified(epoch) => epoch, + other => panic!("{other:?}"), + } + } + pub(crate) fn prepared(&self) -> PreparedCanonical { + prepare_canonical(&self.target, &self.domain, &self.epoch(), &self.memory).unwrap() + } + pub(crate) fn unlocked(&self) { + for path in [ + self.layout.writer_lock_path(), + self.layout.worker_lock_path(), + self.layout.state_lock_path(), + ] { + assert!(try_open_existing_locked(&path, LockMode::Exclusive) + .unwrap() + .is_some()); + } + } + } + impl Drop for Fixture { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.root); + } + } + + #[test] + fn simultaneous_preparations_fit_one_account_without_maximum_path_overcharges() { + let f = Fixture::new(); + let before = f.memory.used(); + let witness = prepare_target_binding(&f.target, &f.memory).unwrap(); + let duplicate = witness.duplicate(&f.memory).unwrap(); + let first = f.prepared(); + let second = f.prepared(); + let metadata = + prepare_private_metadata(&f.target, &first.origin_scope(), &f.memory).unwrap(); + assert!(f.memory.used() <= f.memory.limit()); + drop((metadata, second, first, duplicate, witness)); + assert_eq!(f.memory.used(), before); + } + #[test] + fn legacy_request_source_replacement_invalidates_confirmation_without_rebinding_runtime() { + let f = Fixture::new(); + let pair = super::super::inventory::default_direct(&f.target); + fs::write(&pair.database, b"old database").unwrap(); + fs::write(&pair.manifest, b"old manifest").unwrap(); + fs::create_dir(f.target.state_root.join("artifacts")).unwrap(); + let target_before = prepare_target_binding(&f.target, &f.memory) + .unwrap() + .digest(); + let requested = prepare_legacy_confirmation_source(&f.target, &f.memory).unwrap(); + assert_eq!( + requested.facts(), + capture_legacy_confirmation_source(&f.target, &f.memory).unwrap() + ); + let replacement = f.target.state_root.join("new-graph"); + fs::write(&replacement, b"new database").unwrap(); + fs::rename(replacement, &pair.database).unwrap(); + let mut scratch = FixedControlScratch::new( + f.memory + .reserve(FixedControlScratch::required_bytes()) + .unwrap(), + ) + .unwrap(); + assert!(requested + .recheck( + &mut scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(2)) + ) + .is_err()); + assert_ne!( + requested.facts(), + capture_legacy_confirmation_source(&f.target, &f.memory).unwrap() + ); + // An explicit Direct rebuild need not change the Managed runtime. The + // independently queued source witness still rejects the new source. + assert_eq!( + target_before, + prepare_target_binding(&f.target, &f.memory) + .unwrap() + .digest() + ); + assert_ne!( + super::super::locks::file_identity(requested.pins[0].as_ref().unwrap()).unwrap(), + super::super::locks::path_identity(&pair.database).unwrap() + ); + } + #[test] + fn legacy_request_source_missing_cache_is_noncreating_and_cannot_approve_later_birth() { + let f = Fixture::new(); + let pair = super::super::inventory::default_direct(&f.target); + let cache = f.target.state_root.join("artifacts"); + let requested = prepare_legacy_confirmation_source(&f.target, &f.memory).unwrap(); + assert!(!pair.database.exists()); + assert!(!pair.manifest.exists()); + assert!(!cache.exists()); + assert_eq!(requested.facts().database, None); + assert_eq!(requested.facts().caches[0], None); + fs::create_dir(&cache).unwrap(); + let mut scratch = FixedControlScratch::new( + f.memory + .reserve(FixedControlScratch::required_bytes()) + .unwrap(), + ) + .unwrap(); + assert!(requested + .recheck( + &mut scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(2)) + ) + .is_err()); + assert_ne!( + requested.facts(), + capture_legacy_confirmation_source(&f.target, &f.memory).unwrap() + ); + } + #[test] + fn private_metadata_composes_actual_canonical_root_and_selector_without_reacquisition() { + let f = Fixture::new(); + let mut canonical = f.prepared(); + let origin = canonical.origin_scope(); + let mut metadata = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + let update = prepare_metadata_control( + &origin, + metadata.committed().unwrap(), + b"canonical-composed", + &f.memory, + ) + .unwrap(); + let mut called = false; + with_prepared_canonical( + &mut canonical, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(2)), + update.effects(), + &mut |authority, grant| { + assert_eq!(authority.root_use.mode(), LockMode::Exclusive); + assert_eq!(authority.selector.mode(), LockMode::Exclusive); + with_reserved_canonical_private_metadata( + &mut metadata, + authority, + grant, + update.effects(), + &mut |mutation, grant| { + called = true; + mutation.publish_control(&update, grant) + }, + ) + }, + ) + .unwrap(); + assert!(called); + f.unlocked(); + let current = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + assert_eq!(current.payload(), Some(&b"canonical-composed"[..])); + } + + #[test] + fn canonical_metadata_composition_rejects_another_root_before_callback() { + let f = Fixture::new(); + let other = Fixture::new(); + let mut canonical = f.prepared(); + let other_origin = other.prepared().origin_scope(); + let mut metadata = + prepare_private_metadata(&other.target, &other_origin, &other.memory).unwrap(); + let before = metadata.committed(); + let mut called = false; + let result = with_prepared_canonical( + &mut canonical, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(2)), + 0, + &mut |authority, grant| { + with_reserved_canonical_private_metadata( + &mut metadata, + authority, + grant, + 0, + &mut |_, _| { + called = true; + Ok(()) + }, + ) + }, + ); + assert!(result.is_err()); + assert!(!called); + assert_eq!(metadata.committed(), before); + assert_eq!(other.epoch().counter(), 0); + f.unlocked(); + other.unlocked(); + } + + #[test] + fn private_metadata_composes_actual_owned_root_without_reacquisition() { + let f = Fixture::new(); + let origin = f.prepared().origin_scope(); + let mut prepared = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + let path = prepared.root_use.path.clone(); + let before = prepared.committed().unwrap(); + let update = prepare_metadata_control(&origin, before, b"owned-root", &f.memory).unwrap(); + let shared_file = + open_fixed_control(&path, Kind::File, false, &mut prepared.scratch).unwrap(); + let shared = FixedLockedFile::try_lock(shared_file, LockMode::Shared) + .unwrap() + .unwrap(); + let mut called = false; + assert!(with_reserved_private_metadata_owned_root( + &mut prepared, + &shared, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(2)), + update.effects(), + &mut |_, _| { + called = true; + Ok(()) + } + ) + .is_err()); + assert!(!called); + drop(shared); + let file = open_fixed_control(&path, Kind::File, false, &mut prepared.scratch).unwrap(); + let exclusive = FixedLockedFile::try_lock(file, LockMode::Exclusive) + .unwrap() + .unwrap(); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + grant.take_entries(update.effects()).unwrap(); + with_reserved_private_metadata_owned_root( + &mut prepared, + &exclusive, + &mut grant, + update.effects(), + &mut |mutation, grant| mutation.publish_control(&update, grant), + ) + .unwrap(); + // The callback borrows the actual lease; it remains owned afterward. + assert_eq!(exclusive.mode(), LockMode::Exclusive); + assert!(try_open_existing_locked(&path, LockMode::Exclusive) + .unwrap() + .is_none()); + drop(exclusive); + let refreshed = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + assert_eq!(refreshed.committed(), Some(update.next())); + assert_eq!(f.epoch().counter(), 0); + } + #[test] + fn private_metadata_stages_over_one_hundred_frames_without_advancing_source_epoch() { + let f = Fixture::new(); + let origin = f.prepared().origin_scope(); + let producer = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + let source_epoch = 1; + for index in 0u64..101 { + let mut prepared = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + let before = prepared.committed().unwrap(); + let offset = prepared.stream_len(); + let intent = + prepare_metadata_control(&origin, before, &index.to_le_bytes(), &f.memory).unwrap(); + let ready = + prepare_metadata_control(&origin, intent.next(), &index.to_le_bytes(), &f.memory) + .unwrap(); + with_private_metadata( + &mut prepared, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(2)), + 14, + &mut |mutation, grant| { + mutation.publish_control(&intent, grant)?; + mutation.append_frame(offset, &index.to_le_bytes(), grant)?; + mutation.publish_control(&ready, grant) + }, + ) + .unwrap(); + } + drop(producer); + assert_eq!(f.epoch().counter(), source_epoch); + let prepared = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + assert_eq!(prepared.stream_len(), 808); + assert_eq!(prepared.committed().unwrap().generation, 202); + assert_eq!( + std::fs::read(f.layout.artifacts_root().join(".publication.stream")) + .unwrap() + .len(), + 808 + ); + } + #[test] + fn private_metadata_resumes_each_durable_control_cut_from_same_bound_files() { + for cut in 1..=3 { + let f = Fixture::new(); + let origin = f.prepared().origin_scope(); + let mut prepared = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + let update = prepare_metadata_control( + &origin, + prepared.committed().unwrap(), + b"exact intended metadata", + &f.memory, + ) + .unwrap(); + METADATA_WRITE_CUT.with(|value| *value.borrow_mut() = Some(cut)); + let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = with_private_metadata( + &mut prepared, + &mut MutationGrant::start(), + 6, + &mut |mutation, grant| mutation.publish_control(&update, grant), + ); + })); + assert!(outcome.is_err()); + f.unlocked(); + let mut prepared = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + if let Some(resume) = prepare_metadata_recovery(&prepared, &f.memory).unwrap() { + with_private_metadata( + &mut prepared, + &mut MutationGrant::start(), + resume.effects(), + &mut |mutation, grant| mutation.publish_control(&resume, grant), + ) + .unwrap(); + } + let prepared = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + assert_eq!(prepared.committed(), Some(update.next())); + assert_eq!( + prepared.payload(), + Some(b"exact intended metadata".as_slice()) + ); + assert_eq!(f.epoch().counter(), 0); + } + } + #[test] + fn private_metadata_partial_frame_and_reset_preserve_unknown_bytes() { + use std::io::Write; + let f = Fixture::new(); + let origin = f.prepared().origin_scope(); + let path = f.layout.artifacts_root().join(".publication.stream"); + let frame = [7u8; 512]; + let mut file = std::fs::OpenOptions::new().write(true).open(&path).unwrap(); + file.write_all(&frame[..39]).unwrap(); + file.sync_all().unwrap(); + drop(file); + let mut prepared = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + with_private_metadata( + &mut prepared, + &mut MutationGrant::start(), + 3, + &mut |mutation, grant| mutation.append_frame(0, &frame, grant), + ) + .unwrap(); + assert_eq!(std::fs::read(&path).unwrap(), frame); + let mut prepared = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + let mut file = std::fs::OpenOptions::new() + .append(true) + .open(&path) + .unwrap(); + file.write_all(b"unknown tail").unwrap(); + file.sync_all().unwrap(); + drop(file); + assert!(with_private_metadata( + &mut prepared, + &mut MutationGrant::start(), + 2, + &mut |mutation, grant| mutation.reset_stream(512, grant) + ) + .is_err()); + assert_eq!(std::fs::metadata(&path).unwrap().len(), 524); + // Unknown bytes remain untouched. Only a separate fully verified + // owned prefix is eligible for S3's durable ResetIntent. + let clean = Fixture::new(); + let clean_origin = clean.prepared().origin_scope(); + let clean_path = clean.layout.artifacts_root().join(".publication.stream"); + std::fs::write(&clean_path, frame).unwrap(); + let mut clean_prepared = + prepare_private_metadata(&clean.target, &clean_origin, &clean.memory).unwrap(); + with_private_metadata( + &mut clean_prepared, + &mut MutationGrant::start(), + 2, + &mut |mutation, grant| mutation.reset_stream(512, grant), + ) + .unwrap(); + assert_eq!(std::fs::metadata(clean_path).unwrap().len(), 0); + assert_eq!(f.epoch().counter(), 0); + assert_eq!(std::fs::metadata(path).unwrap().len(), 524); + } + #[test] + fn target_binding_duplicate_retains_actual_legacy_objects_and_same_challenge() { + let f = Fixture::new(); + let root = f.layout.artifacts_root(); + let coordination = root.join(".use.lock"); + std::fs::write(&coordination, b"").unwrap(); + let mut witness = prepare_target_binding(&f.target, &f.memory).unwrap(); + assert!(witness.is_legacy()); + let mut duplicate = witness.duplicate(&f.memory).unwrap(); + assert_eq!(witness.digest(), duplicate.digest()); + assert!(witness.is_current().unwrap()); + std::fs::write(&coordination, b"different generation").unwrap(); + assert!(witness.is_current().is_err()); + assert!(duplicate.is_current().is_err()); + drop(witness); + assert!(duplicate.is_current().is_err()); + } + + #[test] + fn local_epoch_invalidates_current_counter_while_unrelated_shared_producer_stays_live() { + let f = Fixture::new(); + let before = f.epoch().counter(); + let producer = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + let mut scratch = FixedControlScratch::new( + f.memory + .reserve(FixedControlScratch::required_bytes()) + .unwrap(), + ) + .unwrap(); + let mut read = f.memory.reserve(2048).unwrap().bytes(2048).unwrap(); + let prepared = + PreparedEpoch::prepare_local(&f.domain, &f.memory, &mut read, &mut scratch).unwrap(); + let advance = prepared + .with_local_invalidation( + &mut read, + &mut scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + 0, + &mut |advance, _| { + assert_eq!(advance.before, before + 1); + assert_eq!(advance.after, before + 2); + assert!(matches!( + observe_reference_epoch(&f.domain).unwrap(), + EpochObservation::Pending(_) + )); + assert!(producer + .artifact_root_producer_capability() + .unwrap() + .is_some()); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(advance.after, before + 2); + drop(producer); + assert_eq!(f.epoch().counter(), before + 2); + } + + #[test] + fn canonical_ready_holds_actual_guards_and_compares_before_own_epoch() { + let f = Fixture::new(); + let before = f.epoch().counter(); + let mut prepared = f.prepared(); + let mut called = false; + let result = with_prepared_canonical( + &mut prepared, + &mut MutationGrant::start(), + 0, + &mut |authority, _| { + called = true; + assert_eq!(authority.epoch().before, before); + assert_eq!(authority.epoch().after, before + 1); + assert_eq!( + authority.directory().identity(), + super::super::locks::path_identity(&f.layout.artifacts_root()).unwrap() + ); + assert!(try_open_existing_locked( + &f.layout.writer_lock_path(), + LockMode::Exclusive + ) + .unwrap() + .is_none()); + Ok(()) + }, + ) + .unwrap(); + assert!(called); + assert_eq!(result.after, before + 1); + f.unlocked(); + assert_eq!(f.epoch().counter(), before + 1); + } + #[test] + fn canonical_stale_epoch_and_changed_config_never_enter_callback() { + let f = Fixture::new(); + let mut prepared = f.prepared(); + drop(ReferenceMutation::begin_in_domain(&f.domain).unwrap()); + let current = f.epoch().counter(); + assert!(with_prepared_canonical( + &mut prepared, + &mut MutationGrant::start(), + 0, + &mut |_, _| panic!("stale callback") + ) + .is_err()); + assert_eq!(f.epoch().counter(), current); + f.unlocked(); + let mut prepared = f.prepared(); + fs::write( + f.target.state_root.join("config.json"), + br#"{"schema_version":3,"ignored":true}"#, + ) + .unwrap(); + assert!(with_prepared_canonical( + &mut prepared, + &mut MutationGrant::start(), + 0, + &mut |_, _| panic!("changed callback") + ) + .is_err()); + assert_eq!(f.epoch().counter(), current); + f.unlocked(); + } + #[test] + fn canonical_large_registry_is_bounded_nonmutating_and_unknown_siblings_survive() { + let f = Fixture::new(); + let workers = f.layout.storage_root().join("workers"); + fs::create_dir_all(&workers).unwrap(); + for index in 0..1000 { + fs::write(workers.join(format!("unknown-{index}")), b"foreign").unwrap(); + } + let current = f.epoch().counter(); + let mut prepared = f.prepared(); + assert!(with_prepared_canonical( + &mut prepared, + &mut MutationGrant::start(), + 0, + &mut |_, _| panic!("nonempty callback") + ) + .is_err()); + assert_eq!(f.epoch().counter(), current); + assert_eq!(fs::read_dir(workers).unwrap().count(), 1000); + f.unlocked(); + } + #[test] + fn canonical_small_grant_and_panic_drop_actual_authority_before_work() { + let f = Fixture::new(); + let current = f.epoch().counter(); + let mut prepared = f.prepared(); + assert!(with_prepared_canonical( + &mut prepared, + &mut MutationGrant::for_test(1, std::time::Duration::from_secs(1)), + 1, + &mut |_, _| panic!("small callback") + ) + .is_err()); + assert_eq!(f.epoch().counter(), current); + f.unlocked(); + let mut prepared = f.prepared(); + let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = with_prepared_canonical( + &mut prepared, + &mut MutationGrant::start(), + 0, + &mut |_, _| panic!("fixture callback unwind"), + ); + })); + assert!(outcome.is_err()); + f.unlocked(); + assert_eq!(f.epoch().counter(), current + 1); + // Bulk prepared work is still owned here, AFTER authority unwound. + drop(prepared); + } + #[test] + fn canonical_live_producer_is_nonblocking_and_preparation_does_not_spend_grant() { + let f = Fixture::new(); + let mut prepared = f.prepared(); + let producer = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + assert!(with_prepared_canonical( + &mut prepared, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + 0, + &mut |_, _| panic!("live callback") + ) + .is_err()); + drop(producer); + f.unlocked(); + let mut prepared = f.prepared(); + // Outlive the production window, then start a fresh fixture grant. + std::thread::sleep(std::time::Duration::from_millis(110)); + assert!(with_prepared_canonical( + &mut prepared, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + 0, + &mut |_, _| Ok(()) + ) + .is_ok()); + } +} + +/// Fixed request-time source facts, separate from runtime target identity. +/// Equality binds the exact default pair, both recognized cache incarnations, +/// and the complete native DB/manifest stamps. It is no mutation authority. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct LegacyConfirmationVersion { + state: super::locks::FileIdentity, + gate: super::locks::FileIdentity, + nonce: [u8; 32], + version: u64, + job: [u8; 32], + phase: super::maintenance_legacy_guard::LegacyPhase, +} +pub(super) fn legacy_confirmation_version_from_records( + binding: &super::maintenance_legacy_guard::Binding, + record: &super::maintenance_legacy_guard::Record, +) -> LegacyConfirmationVersion { + LegacyConfirmationVersion { + state: binding.state, + gate: binding.gate, + nonce: binding.nonce, + version: record.version, + job: record.job, + phase: record.phase, + } +} +fn legacy_confirmation_version( + target: &MaintenanceTarget, +) -> Result, AdmissionError> { + use super::maintenance_legacy_guard as legacy; + let root = RootedDirectory::open_readonly_root( + &target.state_root, + super::locks::path_identity(&target.state_root).map_err(|_| invalid(Problem::Changed))?, + )?; + let state = match root.open_readonly_file(legacy::STATE) { + Ok(file) => file, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + let gate = root.open_readonly_file(legacy::GATE)?; + let observed = legacy::observe_files(&root, &gate, &state)?; + Ok(Some(legacy_confirmation_version_from_records( + &observed.binding, + &observed.record, + ))) +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct LegacyConfirmationSource { + pub(crate) database: Option, + pub(crate) manifest: Option, + pub(crate) caches: [Option; 2], + pub(crate) version: Option, + pair_binding: [u8; 32], +} +impl LegacyConfirmationSource { + pub(crate) fn encode_checkpoint(&self, out: &mut [u8; 512]) { + use sha2::{Digest, Sha256}; + out.fill(0); + out[..8].copy_from_slice(b"CGSCON01"); + for (i, stamp) in [self.database, self.manifest].into_iter().enumerate() { + if let Some(stamp) = stamp { + out[8 + i] = 1; + stamp.encode_checkpoint((&mut out[16 + i * 80..96 + i * 80]).try_into().unwrap()); + } + } + for (i, id) in self.caches.into_iter().enumerate() { + if let Some(id) = id { + out[10 + i] = 1; + let (a, b) = id.fixed_words(); + out[176 + i * 16..184 + i * 16].copy_from_slice(&a.to_le_bytes()); + out[184 + i * 16..192 + i * 16].copy_from_slice(&b.to_le_bytes()); + } + } + out[208..240].copy_from_slice(&self.pair_binding); + if let Some(version) = self.version { + out[12] = 1; + for (i, id) in [version.state, version.gate].into_iter().enumerate() { + let (a, b) = id.fixed_words(); + out[240 + i * 16..248 + i * 16].copy_from_slice(&a.to_le_bytes()); + out[248 + i * 16..256 + i * 16].copy_from_slice(&b.to_le_bytes()); + } + out[272..304].copy_from_slice(&version.nonce); + out[304..312].copy_from_slice(&version.version.to_le_bytes()); + out[312..344].copy_from_slice(&version.job); + out[344] = match version.phase { + super::maintenance_legacy_guard::LegacyPhase::Active => 0, + super::maintenance_legacy_guard::LegacyPhase::Preparing => 1, + super::maintenance_legacy_guard::LegacyPhase::Retiring => 2, + super::maintenance_legacy_guard::LegacyPhase::Removed => 3, + }; + } + let digest = Sha256::digest(&out[..480]); + out[480..].copy_from_slice(&digest); + } + pub(crate) fn decode_checkpoint(input: &[u8; 512]) -> Result { + use sha2::{Digest, Sha256}; + if &input[..8] != b"CGSCON01" + || input[8..13].iter().any(|v| *v > 1) + || input[13..16] + .iter() + .chain(&input[345..480]) + .any(|v| *v != 0) + || Sha256::digest(&input[..480]).as_slice() != &input[480..] + { + return Err(invalid(Problem::InvalidRecord)); + } + let stamp = |i: usize| -> Result, AdmissionError> { + let bytes = &input[16 + i * 80..96 + i * 80]; + if input[8 + i] == 1 { + Ok(Some(FixedFileStamp::decode_checkpoint( + bytes.try_into().unwrap(), + )?)) + } else if bytes.iter().all(|v| *v == 0) { + Ok(None) + } else { + Err(invalid(Problem::InvalidRecord)) + } + }; + let id = |offset: usize| { + super::locks::FileIdentity::from_fixed_words( + u64::from_le_bytes(input[offset..offset + 8].try_into().unwrap()), + u64::from_le_bytes(input[offset + 8..offset + 16].try_into().unwrap()), + ) + }; + let cache = |i: usize| -> Result, AdmissionError> { + if input[10 + i] == 1 { + Ok(Some(id(176 + i * 16))) + } else if input[176 + i * 16..192 + i * 16].iter().all(|v| *v == 0) { + Ok(None) + } else { + Err(invalid(Problem::InvalidRecord)) + } + }; + let version = if input[12] == 1 { + Some(LegacyConfirmationVersion { + state: id(240), + gate: id(256), + nonce: input[272..304].try_into().unwrap(), + version: u64::from_le_bytes(input[304..312].try_into().unwrap()), + job: input[312..344].try_into().unwrap(), + phase: match input[344] { + 0 => super::maintenance_legacy_guard::LegacyPhase::Active, + 1 => super::maintenance_legacy_guard::LegacyPhase::Preparing, + 2 => super::maintenance_legacy_guard::LegacyPhase::Retiring, + 3 => super::maintenance_legacy_guard::LegacyPhase::Removed, + _ => return Err(invalid(Problem::InvalidRecord)), + }, + }) + } else if input[240..345].iter().all(|v| *v == 0) { + None + } else { + return Err(invalid(Problem::InvalidRecord)); + }; + Ok(Self { + database: stamp(0)?, + manifest: stamp(1)?, + caches: [cache(0)?, cache(1)?], + pair_binding: input[208..240].try_into().unwrap(), + version, + }) + } +} +/// Observational source pins prevent inode ABA while a true demand is queued. +/// No lock is held and this owner must be disposed outside execution authority. +pub(crate) struct PreparedLegacyConfirmationSource { + entries: [Entry; 5], + pins: [Option; 5], + facts: LegacyConfirmationSource, + _memory: MemoryReservation, +} +impl PreparedLegacyConfirmationSource { + pub(crate) fn facts(&self) -> LegacyConfirmationSource { + self.facts + } + /// Actual writer/native/cache exclusions belong to the caller. This only + /// repeats bounded exact source observations; no hash, codec or allocation. + pub(crate) fn recheck( + &self, + scratch: &mut FixedControlScratch, + grant: &mut MutationGrant, + ) -> Result<(), AdmissionError> { + for (index, entry) in self.entries.iter().enumerate() { + grant.take_entry()?; + match ( + open_fixed_control(&entry.path, entry.kind, false, scratch), + entry.stamp, + &self.pins[index], + ) { + (Ok(file), Some(stamp), Some(pin)) => { + if super::locks::file_identity(pin).map_err(|_| invalid(Problem::Changed))? + != stamp.identity() + { + return Err(invalid(Problem::Changed)); + } + if index < 2 || index == 4 { + stamp.recheck(&file)?; + } else if FixedFileStamp::capture(&file, Kind::Directory)?.identity() + != stamp.identity() + { + return Err(invalid(Problem::Changed)); + } + } + (Err(AdmissionError::Io(error)), None, None) + if error.kind() == std::io::ErrorKind::NotFound => {} + (Err(error), _, _) => return Err(error), + _ => return Err(invalid(Problem::Changed)), + } + } + Ok(()) + } +} +fn legacy_confirmation_charge(target: &MaintenanceTarget) -> Result { + target + .state_root + .as_os_str() + .as_encoded_bytes() + .len() + .checked_mul(16) + .and_then(|x| x.checked_add(16384)) + .and_then(|x| x.checked_add(std::mem::size_of::())) + .ok_or(AdmissionError::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + )) +} +fn legacy_confirmation_entries( + target: &MaintenanceTarget, + scratch: &mut FixedControlScratch, +) -> Result<[Entry; 5], AdmissionError> { + let pair = super::inventory::default_direct(target); + let cache = DirectLayout::new(&pair.database, &pair.manifest).artifact_root_path(); + Ok([ + Entry::prepare(pair.database, Kind::File, true, scratch)?, + Entry::prepare(pair.manifest, Kind::File, true, scratch)?, + Entry::prepare( + target.state_root.join("artifacts"), + Kind::Directory, + true, + scratch, + )?, + Entry::prepare(cache, Kind::Directory, true, scratch)?, + Entry::prepare( + target + .state_root + .join(std::str::from_utf8(super::maintenance_legacy_guard::STATE).unwrap()), + Kind::File, + true, + scratch, + )?, + ]) +} +fn legacy_confirmation_facts( + entries: &[Entry; 5], + version: Option, +) -> LegacyConfirmationSource { + use sha2::{Digest, Sha256}; + let mut hash = Sha256::new(); + hash.update(b"CGDEFAULTSOURCE01"); + for entry in entries { + let path = entry.path.as_os_str().as_encoded_bytes(); + hash.update((path.len() as u64).to_le_bytes()); + hash.update(path); + } + LegacyConfirmationSource { + database: entries[0].stamp, + manifest: entries[1].stamp, + caches: [ + entries[2].stamp.map(|s| s.identity()), + entries[3].stamp.map(|s| s.identity()), + ], + version, + pair_binding: hash.finalize().into(), + } +} +/// Facts-only advisory preparation probes one payload at a time, retaining no +/// DB/manifest descriptors alongside the request's two existing source pins. +/// The first actual enrollment must recheck them under its real source guards. +#[inline(never)] +pub(crate) fn capture_legacy_confirmation_source( + target: &MaintenanceTarget, + memory: &CleanupMemory, +) -> Result { + let _reservation = memory + .reserve(legacy_confirmation_charge(target)?) + .map_err(AdmissionError::Resource)?; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(AdmissionError::Resource)?, + )?; + let entries = legacy_confirmation_entries(target, &mut scratch)?; + let version = legacy_confirmation_version(target)?; + Ok(legacy_confirmation_facts(&entries, version)) +} +/// Noncreating request capture. Missing entries are explicit facts, never +/// approval to enroll a replacement which appears after the request. +#[inline(never)] +pub(crate) fn prepare_legacy_confirmation_source( + target: &MaintenanceTarget, + memory: &CleanupMemory, +) -> Result { + let reservation = memory + .reserve(legacy_confirmation_charge(target)?) + .map_err(AdmissionError::Resource)?; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(AdmissionError::Resource)?, + )?; + let entries = legacy_confirmation_entries(target, &mut scratch)?; + let mut pins = [None, None, None, None, None]; + for (index, entry) in entries.iter().enumerate() { + if let Some(stamp) = entry.stamp { + let file = open_fixed_control(&entry.path, entry.kind, false, &mut scratch)?; + if index < 2 || index == 4 { + stamp.recheck(&file)?; + } else if FixedFileStamp::capture(&file, Kind::Directory)?.identity() + != stamp.identity() + { + return Err(invalid(Problem::Changed)); + } + pins[index] = Some(file); + } + } + let version = legacy_confirmation_version(target)?; + let facts = legacy_confirmation_facts(&entries, version); + let owner = PreparedLegacyConfirmationSource { + entries, + pins, + facts, + _memory: reservation, + }; + owner.recheck(&mut scratch, &mut MutationGrant::start())?; + Ok(owner) +} + +/// Exact-incarnation confirmation witness. Retained non-authority descriptors +/// prevent root/control inode ABA even for legacy roots lacking a nonce. This +/// is preparation, never a lock or enrollment/deletion capability. +pub(crate) struct PreparedTargetBinding { + digest: [u8; 32], + root: Entry, + _root_file: std::fs::File, + coordination: Entry, + _coordination_file: Option, + binding: BoundConfiguration, + repository: Entry, + state: Entry, + _repository_file: std::fs::File, + _state_file: std::fs::File, + scratch: FixedControlScratch, + legacy: bool, + _floor: MemoryReservation, +} +impl PreparedTargetBinding { + pub(crate) fn duplicate(&self, memory: &CleanupMemory) -> Result { + let floor = memory + .reserve(self._floor.remaining()) + .map_err(AdmissionError::Resource)?; + let binding = self + .binding + .duplicate(memory) + .map_err(AdmissionError::Resource)?; + let scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(AdmissionError::Resource)?, + )?; + Ok(Self { + digest: self.digest, + root: self.root.clone(), + _root_file: self._root_file.try_clone()?, + coordination: self.coordination.clone(), + _coordination_file: self + ._coordination_file + .as_ref() + .map(std::fs::File::try_clone) + .transpose()?, + binding, + repository: self.repository.clone(), + state: self.state.clone(), + _repository_file: self._repository_file.try_clone()?, + _state_file: self._state_file.try_clone()?, + scratch, + legacy: self.legacy, + _floor: floor, + }) + } + pub(crate) fn digest(&self) -> [u8; 32] { + self.digest + } + pub(crate) fn is_legacy(&self) -> bool { + self.legacy + } + pub(crate) fn is_current(&mut self) -> Result { + // Advisory fixed recheck; no mutation authority or user confirmation is + // implied by this convenience. The real admission repeats these checks. + self.recheck_identity(&mut || Ok(())).map(|_| true) + } + pub(super) fn recheck(&mut self, grant: &mut MutationGrant) -> Result<(), AdmissionError> { + self.recheck_identity(&mut || { + grant.take_entry()?; + Ok(()) + }) + } + fn recheck_identity( + &mut self, + charge: &mut impl FnMut() -> Result<(), AdmissionError>, + ) -> Result<(), AdmissionError> { + charge()?; + let current = + open_fixed_control(&self.root.path, Kind::Directory, false, &mut self.scratch)?; + if FixedFileStamp::capture(¤t, Kind::Directory)?.identity() + != self.root.stamp.unwrap().identity() + { + return Err(invalid(Problem::Changed)); + } + charge()?; + check_entry_identity( + &self.coordination.path, + self.coordination.kind, + self.coordination.stamp, + &mut self.scratch, + )?; + charge()?; + check_entry_identity( + &self.binding.config_path, + Kind::File, + self.binding.config_stamp, + &mut self.scratch, + )?; + for (entry, pin) in [ + (&self.repository, &self._repository_file), + (&self.state, &self._state_file), + ] { + charge()?; + let current = + open_fixed_control(&entry.path, Kind::Directory, false, &mut self.scratch)?; + let expected = entry + .stamp + .ok_or_else(|| invalid(Problem::Changed))? + .identity(); + if FixedFileStamp::capture(¤t, Kind::Directory)?.identity() != expected + || super::locks::file_identity(pin).map_err(|_| invalid(Problem::Changed))? + != expected + { + return Err(invalid(Problem::Changed)); + } + } + Ok(()) + } +} +pub(crate) fn prepare_target_binding( + target: &MaintenanceTarget, + memory: &CleanupMemory, +) -> Result { + use sha2::{Digest, Sha256}; + let binding = project_binding(target, memory).map_err(projection)?; + let floor = memory + .reserve(128 * 1024 + std::mem::size_of::()) + .map_err(AdmissionError::Resource)?; + let root_path = if let Some(pair) = &binding.direct { + DirectLayout::new(&pair.database, &pair.manifest).artifact_root_path() + } else { + ManagedLayout::new(&binding.storage).artifacts_root() + }; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(AdmissionError::Resource)?, + )?; + let root = Entry::prepare(root_path, Kind::Directory, false, &mut scratch)?; + let root_file = open_fixed_control(&root.path, Kind::Directory, false, &mut scratch)?; + root.stamp.unwrap().recheck(&root_file)?; + let coordination = Entry::prepare(root.path.join(".use.lock"), Kind::File, true, &mut scratch)?; + let coordination_file = if let Some(stamp) = coordination.stamp { + let file = open_fixed_control(&coordination.path, Kind::File, false, &mut scratch)?; + stamp.recheck(&file)?; + Some(file) + } else { + None + }; + let incarnation = coordination_file.as_ref().and_then(|file| { + super::locks::read_coordination_incarnation(file) + .ok() + .flatten() + }); + let legacy = incarnation.is_none(); + let mut nonce = incarnation.unwrap_or([0; 32]); + if legacy { + getrandom::fill(&mut nonce).map_err(|_| invalid(Problem::InvalidRecord))?; + } + let repository = Entry::prepare( + target.repository_root.clone(), + Kind::Directory, + false, + &mut scratch, + )?; + let state = Entry::prepare( + target.state_root.clone(), + Kind::Directory, + false, + &mut scratch, + )?; + let mut hash = Sha256::new(); + hash.update(b"CGTARGET02"); + hash.update([u8::from(legacy)]); + hash.update(nonce); + for entry in [&root, &coordination, &repository, &state] { + hash.update(entry.path.as_os_str().as_encoded_bytes()); + if let Some(stamp) = entry.stamp { + let (a, b) = stamp.identity().fixed_words(); + hash.update([1]); + hash.update(a.to_le_bytes()); + hash.update(b.to_le_bytes()); + } else { + hash.update([0]); + } + } + hash.update(binding.config_path.as_os_str().as_encoded_bytes()); + if let Some(stamp) = binding.config_stamp { + hash.update(stamp.binding_digest()); + } + if let Some(pair) = &binding.direct { + hash.update(pair.database.as_os_str().as_encoded_bytes()); + hash.update([0]); + hash.update(pair.manifest.as_os_str().as_encoded_bytes()); + } else { + hash.update(binding.storage.as_os_str().as_encoded_bytes()); + } + Ok(PreparedTargetBinding { + digest: hash.finalize().into(), + root, + _root_file: root_file, + coordination, + _coordination_file: coordination_file, + binding, + _repository_file: open_fixed_control( + &repository.path, + Kind::Directory, + false, + &mut scratch, + )?, + _state_file: open_fixed_control(&state.path, Kind::Directory, false, &mut scratch)?, + repository, + state, + scratch, + legacy, + _floor: floor, + }) +} + +impl PreparedCanonical { + /// Advisory origin facts for offslot receipt/journal preparation; this + /// getter does not transfer any current namespace authority. + pub(crate) fn origin_scope(&self) -> RootOriginBinding { + self.origin + } +} + +pub(crate) struct PreparedPrivateMetadata { + origin: RootOriginBinding, + coordination_header: [u8; super::locks::COORDINATION_HEADER_BYTES], + root: Entry, + root_use: Entry, + cohort: Entry, + binding: Entry, + selector: Entry, + data: [Entry; 2], + data_files: [std::fs::File; 2], + stream: Entry, + stream_file: std::fs::File, + images: ReservedBytes, + read: ReservedBytes, + scratch: MemoryBox, + committed: Option, + recovery: super::maintenance_controls::RecoveryStep, + _epoch: MemoryBox, + _binding: MemoryBox, + _floor: MemoryReservation, +} +impl PreparedPrivateMetadata { + pub(crate) fn origin_scope(&self) -> RootOriginBinding { + self.origin + } + pub(crate) fn committed(&self) -> Option { + self.committed + } + pub(crate) fn recovery(&self) -> super::maintenance_controls::RecoveryStep { + self.recovery + } + pub(crate) fn stream_len(&self) -> u64 { + self.stream.stamp.unwrap().len() + } + pub(crate) fn payload(&self) -> Option<&[u8]> { + let commit = self.committed?; + let begin = commit.active * super::maintenance_controls::SLOT_BYTES; + let slot = &self.images.as_slice()[begin..begin + super::maintenance_controls::SLOT_BYTES]; + let len = u64::from_le_bytes(slot[48..56].try_into().ok()?) as usize; + slot.get(56..56 + len) + } +} + +/// Compatibility value API; shipping stream callers retain the charged owner. +#[inline(never)] +pub(crate) fn prepare_private_metadata( + target: &MaintenanceTarget, + origin: &RootOriginBinding, + memory: &CleanupMemory, +) -> Result { + prepare_private_metadata_boxed(target, origin, memory).map(MemoryBox::into_inner) +} + +// Isolate fallible by-value constructors from the retained metadata frame. +#[inline(never)] +fn private_metadata_binding( + memory: &CleanupMemory, + target: &MaintenanceTarget, +) -> Result, AdmissionError> { + let binding = project_binding(target, memory).map_err(projection)?; + MemoryBox::new(binding, memory).map_err(AdmissionError::Resource) +} +#[inline(never)] +fn private_metadata_scratch( + memory: &CleanupMemory, +) -> Result, AdmissionError> { + let scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(AdmissionError::Resource)?, + )?; + MemoryBox::new(scratch, memory).map_err(AdmissionError::Resource) +} +#[inline(never)] +fn private_metadata_epoch( + domain: &ReferenceDomain, + memory: &CleanupMemory, + read: &mut ReservedBytes, + scratch: &mut FixedControlScratch, +) -> Result, AdmissionError> { + let epoch = PreparedEpoch::prepare_local(domain, memory, read, scratch)?; + MemoryBox::new(epoch, memory).map_err(AdmissionError::Resource) +} + +#[inline(never)] +pub(crate) fn prepare_private_metadata_boxed( + target: &MaintenanceTarget, + origin: &RootOriginBinding, + memory: &CleanupMemory, +) -> Result, AdmissionError> { + use super::maintenance_controls as c; + let binding = private_metadata_binding(memory, target)?; + let floor = memory + .reserve(256 * 1024 + std::mem::size_of::()) + .map_err(AdmissionError::Resource)?; + let domain = if let Some(pair) = &binding.direct { + ReferenceDomain::direct(&DirectLayout::new(&pair.database, &pair.manifest)) + } else { + ReferenceDomain::managed(&ManagedLayout::new(&binding.storage)) + } + .map_err(|_| invalid(Problem::InvalidRecord))?; + let mut scratch = private_metadata_scratch(memory)?; + let mut read = memory + .reserve(4096) + .map_err(AdmissionError::Resource)? + .bytes(4096) + .map_err(AdmissionError::Resource)?; + let epoch = private_metadata_epoch(&domain, memory, &mut read, &mut scratch)?; + let facts = epoch.root_facts(); + if facts + != ( + origin.root, + origin.coordination, + origin.incarnation, + origin.cohort, + ) + { + return Err(invalid(Problem::Changed)); + } + let path = domain.artifact_root(); + let root = Entry::prepare(path.to_owned(), Kind::Directory, false, &mut scratch)?; + let root_use = Entry::prepare(path.join(".use.lock"), Kind::File, false, &mut scratch)?; + let cohort = Entry::prepare( + path.join(".use-cohort.json"), + Kind::File, + false, + &mut scratch, + )?; + let binding_entry = Entry::prepare( + path.join(".publication.binding"), + Kind::File, + false, + &mut scratch, + )?; + if binding_entry.stamp.unwrap().identity() != origin.publication_binding { + return Err(invalid(Problem::Changed)); + } + let mut file = open_fixed_control(&binding_entry.path, Kind::File, false, &mut scratch)?; + let len = + super::locks::read_fixed_control(&mut file, &binding_entry.stamp.unwrap(), &mut read)?; + let publisher = c::PublisherBinding::decode(&read.as_slice()[..len]) + .map_err(|_| invalid(Problem::InvalidRecord))?; + if ( + publisher.root, + publisher.coordination, + publisher.incarnation, + ) != (origin.root, origin.coordination, origin.incarnation) + { + return Err(invalid(Problem::Changed)); + } + let selector = Entry::prepare( + path.join(".publication.selector"), + Kind::File, + false, + &mut scratch, + )?; + let data = [ + Entry::prepare( + path.join(".publication.data0"), + Kind::File, + false, + &mut scratch, + )?, + Entry::prepare( + path.join(".publication.data1"), + Kind::File, + false, + &mut scratch, + )?, + ]; + let data_files = [ + open_fixed_control(&data[0].path, Kind::File, true, &mut scratch)?, + open_fixed_control(&data[1].path, Kind::File, true, &mut scratch)?, + ]; + if selector.stamp.unwrap().identity() != publisher.selector + || data[0].stamp.unwrap().identity() != publisher.data[0] + || data[1].stamp.unwrap().identity() != publisher.data[1] + { + return Err(invalid(Problem::Changed)); + } + let total = 2 * c::SLOT_BYTES + c::SELECTOR_BYTES; + let mut images = memory + .reserve(total) + .map_err(AdmissionError::Resource)? + .bytes(total) + .map_err(AdmissionError::Resource)?; + for index in 0..2 { + data[index].stamp.unwrap().recheck(&data_files[index])?; + if data[index].stamp.unwrap().len() != c::SLOT_BYTES as u64 { + return Err(invalid(Problem::InvalidRecord)); + } + super::locks::read_fixed_range( + &data_files[index], + 0, + &mut images.as_mut_slice()[index * c::SLOT_BYTES..(index + 1) * c::SLOT_BYTES], + )?; + data[index].stamp.unwrap().recheck(&data_files[index])?; + } + let selector_file = open_fixed_control(&selector.path, Kind::File, false, &mut scratch)?; + if selector.stamp.unwrap().len() != c::SELECTOR_BYTES as u64 { + return Err(invalid(Problem::InvalidRecord)); + } + super::locks::read_fixed_range( + &selector_file, + 0, + &mut images.as_mut_slice()[2 * c::SLOT_BYTES..], + )?; + selector.stamp.unwrap().recheck(&selector_file)?; + let slots = [ + c::decode_slot(&images.as_slice()[..c::SLOT_BYTES], &origin.incarnation).ok(), + c::decode_slot( + &images.as_slice()[c::SLOT_BYTES..2 * c::SLOT_BYTES], + &origin.incarnation, + ) + .ok(), + ]; + let selector_bytes = &images.as_slice()[2 * c::SLOT_BYTES..]; + let committed = c::decode_commit(&selector_bytes[..c::COMMIT_BYTES], &origin.incarnation).ok(); + let intent = c::decode_intent(&selector_bytes[c::COMMIT_BYTES..], &origin.incarnation).ok(); + let recovery = c::recovery_step(committed, intent, slots); + if recovery == c::RecoveryStep::Unknown { + return Err(invalid(Problem::InvalidRecord)); + } + let stream = Entry::prepare( + path.join(".publication.stream"), + Kind::File, + false, + &mut scratch, + )?; + if Some(stream.stamp.unwrap().identity()) != publisher.stream { + return Err(invalid(Problem::Changed)); + } + let stream_file = open_fixed_control(&stream.path, Kind::File, true, &mut scratch)?; + stream.stamp.unwrap().recheck(&stream_file)?; + MemoryBox::new( + PreparedPrivateMetadata { + origin: *origin, + coordination_header: super::locks::encode_coordination_incarnation(origin.incarnation), + root, + root_use, + cohort, + binding: binding_entry, + selector, + data, + data_files, + stream, + stream_file, + images, + read, + scratch, + committed, + recovery, + _epoch: epoch, + _binding: binding, + _floor: floor, + }, + memory, + ) + .map_err(AdmissionError::Resource) +} + +/// Only the prebound metadata stream/slots are reachable through this facade. +/// No payload directory/path/handle or destination writer authority is exposed. +pub(crate) struct PrivateMetadataMutation<'a> { + origin: RootOriginBinding, + selector: &'a FixedLockedFile, + data: [&'a std::fs::File; 2], + stream: &'a std::fs::File, + stream_len: u64, + read: &'a mut [u8], + committed: Option, + remaining_effects: usize, + usable: bool, + recovery_pending: bool, +} +impl PrivateMetadataMutation<'_> { + fn spend(&mut self, count: usize) -> Result<(), AdmissionError> { + self.remaining_effects = + self.remaining_effects + .checked_sub(count) + .ok_or(AdmissionError::Grant( + super::maintenance_budget::GrantExhausted::Entries, + ))?; + Ok(()) + } + fn range( + &mut self, + file: &std::fs::File, + offset: u64, + bytes: &[u8], + grant: &mut MutationGrant, + ) -> Result<(), AdmissionError> { + if bytes.len() > self.read.len() { + return Err(invalid(Problem::BufferTooSmall)); + } + grant.check_time()?; + super::locks::write_fixed_range(file, offset, bytes, grant)?; + #[cfg(test)] + METADATA_WRITE_CUT.with(|cut| { + let mut cut = cut.borrow_mut(); + if let Some(remaining) = cut.as_mut() { + *remaining -= 1; + if *remaining == 0 { + *cut = None; + panic!("fixture durable metadata write cut"); + } + } + }); + grant.check_time()?; + super::locks::read_fixed_range(file, offset, &mut self.read[..bytes.len()])?; + if self.read[..bytes.len()] != *bytes { + return Err(invalid(Problem::Changed)); + } + grant.check_time()?; + Ok(()) + } + /// Exact bounded inert append. A durable S3 AppendIntent must already bind + /// these bytes/offset/sourceE/G. Partial own prefixes can be rewritten only + /// when they match; unknown tails are retained, never truncated/adopted. + pub(crate) fn append_frame( + &mut self, + offset: u64, + bytes: &[u8], + grant: &mut MutationGrant, + ) -> Result<(), AdmissionError> { + if !self.usable || bytes.is_empty() || bytes.len() > 4096 || bytes.len() > self.read.len() { + return Err(invalid(Problem::BufferTooSmall)); + } + let end = offset + .checked_add(bytes.len() as u64) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + if self.stream_len < offset || self.stream_len > end { + return Err(invalid(Problem::Changed)); + } + let partial = (self.stream_len - offset) as usize; + self.spend(if partial == 0 { 2 } else { 3 })?; + if partial > 0 { + grant.check_time()?; + super::locks::read_fixed_range(self.stream, offset, &mut self.read[..partial])?; + if self.read[..partial] != bytes[..partial] { + return Err(invalid(Problem::Changed)); + } + } + self.usable = false; + let stream = self.stream; + self.range(stream, offset, bytes, grant)?; + if stream.metadata()?.len() != end { + return Err(invalid(Problem::Changed)); + } + self.stream_len = end; + self.usable = true; + Ok(()) + } +} + +pub(crate) fn with_private_metadata( + prepared: &mut PreparedPrivateMetadata, + grant: &mut MutationGrant, + effect_entries: usize, + apply: &mut impl FnMut( + &mut PrivateMetadataMutation<'_>, + &mut MutationGrant, + ) -> Result<(), AdmissionError>, +) -> Result<(), AdmissionError> { + with_private_metadata_inner(prepared, None, None, grant, effect_entries, false, apply) +} +/// Composition-only entry point: caller is inside an actual scoped-role +/// callback and included these effects in that role's atomic reservation. +/// Fixed existing-only metadata admission observations still spend previously +/// UNRESERVED entries. No canonical writer/state/reference admission is nested. +pub(crate) fn with_reserved_private_metadata( + prepared: &mut PreparedPrivateMetadata, + grant: &mut MutationGrant, + effect_entries: usize, + apply: &mut impl FnMut( + &mut PrivateMetadataMutation<'_>, + &mut MutationGrant, + ) -> Result<(), AdmissionError>, +) -> Result<(), AdmissionError> { + with_private_metadata_inner(prepared, None, None, grant, effect_entries, true, apply) +} +/// Compose with an already held actual Exclusive artifact-root lease. No +/// reference/writer admission is repeated and no namespace marker substitutes +/// for the borrowed kernel guard. Effects belong to the outer reservation. +pub(crate) fn with_reserved_private_metadata_owned_root( + prepared: &mut PreparedPrivateMetadata, + root_use: &FixedLockedFile, + grant: &mut MutationGrant, + effect_entries: usize, + apply: &mut impl FnMut( + &mut PrivateMetadataMutation<'_>, + &mut MutationGrant, + ) -> Result<(), AdmissionError>, +) -> Result<(), AdmissionError> { + with_private_metadata_inner( + prepared, + Some(root_use), + None, + grant, + effect_entries, + true, + apply, + ) +} +/// Compose only inside an actual canonical callback. Borrow both real +/// Exclusive guards, keeping all metadata identity/image/stream checks while +/// avoiding self-contention on the already held root and selector locks. +pub(crate) fn with_reserved_canonical_private_metadata( + prepared: &mut PreparedPrivateMetadata, + authority: &CanonicalMutation<'_>, + grant: &mut MutationGrant, + effect_entries: usize, + apply: &mut impl FnMut( + &mut PrivateMetadataMutation<'_>, + &mut MutationGrant, + ) -> Result<(), AdmissionError>, +) -> Result<(), AdmissionError> { + if prepared.origin != authority.origin { + return Err(invalid(Problem::Changed)); + } + with_private_metadata_inner( + prepared, + Some(authority.root_use), + Some(authority.selector), + grant, + effect_entries, + true, + apply, + ) +} +fn with_private_metadata_inner( + prepared: &mut PreparedPrivateMetadata, + borrowed_root: Option<&FixedLockedFile>, + borrowed_selector: Option<&FixedLockedFile>, + grant: &mut MutationGrant, + effect_entries: usize, + already_reserved: bool, + apply: &mut impl FnMut( + &mut PrivateMetadataMutation<'_>, + &mut MutationGrant, + ) -> Result<(), AdmissionError>, +) -> Result<(), AdmissionError> { + grant.check()?; + let acquired_root = if borrowed_root.is_none() { + grant.take_entry()?; + let root_file = open_fixed_control( + &prepared.root_use.path, + Kind::File, + false, + &mut prepared.scratch, + )?; + Some( + FixedLockedFile::try_lock(root_file, LockMode::Shared)? + .ok_or_else(|| invalid(Problem::Busy))?, + ) + } else { + None + }; + let root_use = borrowed_root + .or(acquired_root.as_ref()) + .ok_or_else(|| invalid(Problem::Busy))?; + if borrowed_root.is_some() && root_use.mode() != LockMode::Exclusive { + return Err(invalid(Problem::Busy)); + } + prepared + .root_use + .stamp + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .recheck(root_use.file())?; + if !root_use.matches_fixed_header(&prepared.coordination_header)? { + return Err(invalid(Problem::Changed)); + } + let acquired_selector = if borrowed_selector.is_none() { + Some(prepared.selector.lock(&mut prepared.scratch, grant)?) + } else { + None + }; + let selector = borrowed_selector + .or(acquired_selector.as_ref()) + .ok_or_else(|| invalid(Problem::Busy))?; + let selector_stamp = prepared + .selector + .stamp + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + if !selector.matches_fixed_current(&selector_stamp, LockMode::Exclusive)? { + return Err(invalid(Problem::Changed)); + } + selector_stamp.recheck(selector.file())?; + for entry in [ + &prepared.root, + &prepared.root_use, + &prepared.cohort, + &prepared.binding, + &prepared.selector, + &prepared.data[0], + &prepared.data[1], + &prepared.stream, + ] { + entry.check(&mut prepared.scratch, grant)?; + } + for index in 0..2 { + prepared.data[index] + .stamp + .unwrap() + .recheck(&prepared.data_files[index])?; + grant.take_entry()?; + let begin = index * super::maintenance_controls::SLOT_BYTES; + let len = super::maintenance_controls::SLOT_BYTES; + super::locks::read_fixed_range( + &prepared.data_files[index], + 0, + &mut prepared.read.as_mut_slice()[..len], + )?; + if prepared.read.as_slice()[..len] != prepared.images.as_slice()[begin..begin + len] { + return Err(invalid(Problem::Changed)); + } + } + let len = super::maintenance_controls::SELECTOR_BYTES; + grant.take_entry()?; + super::locks::read_fixed_range(selector.file(), 0, &mut prepared.read.as_mut_slice()[..len])?; + if prepared.read.as_slice()[..len] + != prepared.images.as_slice()[2 * super::maintenance_controls::SLOT_BYTES..] + { + return Err(invalid(Problem::Changed)); + } + prepared + .stream + .stamp + .unwrap() + .recheck(&prepared.stream_file)?; + if !already_reserved { + grant.take_entries(effect_entries)?; + } + { + let mut mutation = PrivateMetadataMutation { + origin: prepared.origin, + selector, + data: [&prepared.data_files[0], &prepared.data_files[1]], + stream: &prepared.stream_file, + stream_len: prepared.stream.stamp.unwrap().len(), + read: prepared.read.as_mut_slice(), + committed: prepared.committed, + remaining_effects: effect_entries, + usable: true, + recovery_pending: !matches!( + prepared.recovery, + super::maintenance_controls::RecoveryStep::Committed(_) + ), + }; + apply(&mut mutation, grant)?; + grant.check_time()?; + } + drop(acquired_selector); + drop(acquired_root); + Ok(()) +} + +pub(crate) struct PreparedControlUpdate { + incarnation: [u8; 32], + before: Option, + next: super::maintenance_controls::CommitFact, + images: ReservedBytes, + effects: usize, + recovery: bool, +} +impl PreparedControlUpdate { + pub(crate) fn effects(&self) -> usize { + self.effects + } + #[cfg(test)] + pub(crate) fn next(&self) -> super::maintenance_controls::CommitFact { + self.next + } +} +pub(crate) fn prepare_metadata_control( + origin: &RootOriginBinding, + before: super::maintenance_controls::CommitFact, + payload: &[u8], + memory: &CleanupMemory, +) -> Result { + use super::maintenance_controls as c; + let size = c::SLOT_BYTES + c::INTENT_BYTES + c::COMMIT_BYTES; + let mut images = memory + .reserve(size + std::mem::size_of::()) + .map_err(AdmissionError::Resource)? + .bytes(size) + .map_err(AdmissionError::Resource)?; + let (slot, rest) = images.as_mut_slice().split_at_mut(c::SLOT_BYTES); + let (intent, commit) = rest.split_at_mut(c::INTENT_BYTES); + let generation = before + .generation + .checked_add(1) + .ok_or_else(|| invalid(Problem::EpochExhausted))?; + let fact = c::encode_slot(slot, &origin.incarnation, generation, payload) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let intended = c::encode_intent(intent, &origin.incarnation, before, slot) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let next = c::CommitFact { + generation, + active: intended.target, + slot_digest: fact.digest, + }; + c::encode_commit(commit, &origin.incarnation, next) + .map_err(|_| invalid(Problem::InvalidRecord))?; + Ok(PreparedControlUpdate { + incarnation: origin.incarnation, + before: Some(before), + next, + images, + effects: 6, + recovery: false, + }) +} +pub(crate) fn prepare_metadata_recovery( + prepared: &PreparedPrivateMetadata, + memory: &CleanupMemory, +) -> Result, AdmissionError> { + use super::maintenance_controls as c; + let (intent, effects) = match prepared.recovery { + c::RecoveryStep::Committed(_) => return Ok(None), + c::RecoveryStep::RewriteIntendedSlot(intent) => (intent, 4), + c::RecoveryStep::CommitIntendedSlot(intent) => (intent, 2), + c::RecoveryStep::Unknown => return Err(invalid(Problem::InvalidRecord)), + }; + let size = c::SLOT_BYTES + c::INTENT_BYTES + c::COMMIT_BYTES; + let mut images = memory + .reserve(size + std::mem::size_of::()) + .map_err(AdmissionError::Resource)? + .bytes(size) + .map_err(AdmissionError::Resource)?; + let source = &prepared.images.as_slice()[2 * c::SLOT_BYTES + c::COMMIT_BYTES..]; + let (slot, rest) = images.as_mut_slice().split_at_mut(c::SLOT_BYTES); + let (intent_bytes, commit) = rest.split_at_mut(c::INTENT_BYTES); + slot.copy_from_slice(&source[96..96 + c::SLOT_BYTES]); + intent_bytes.copy_from_slice(source); + let next = c::CommitFact { + generation: intent.after, + active: intent.target, + slot_digest: intent.slot_digest, + }; + c::encode_commit(commit, &prepared.origin.incarnation, next) + .map_err(|_| invalid(Problem::InvalidRecord))?; + Ok(Some(PreparedControlUpdate { + incarnation: prepared.origin.incarnation, + before: prepared.committed, + next, + images, + effects, + recovery: true, + })) +} +impl PrivateMetadataMutation<'_> { + pub(crate) fn publish_control( + &mut self, + update: &PreparedControlUpdate, + grant: &mut MutationGrant, + ) -> Result<(), AdmissionError> { + use super::maintenance_controls as c; + if !self.usable + || self.recovery_pending != update.recovery + || self.origin.incarnation != update.incarnation + || self.committed != update.before + { + return Err(invalid(Problem::Changed)); + } + self.spend(update.effects)?; + self.usable = false; + let selector = self.selector.file(); + let data = self.data[update.next.active]; + let (slot, rest) = update.images.as_slice().split_at(c::SLOT_BYTES); + let (intent, commit) = rest.split_at(c::INTENT_BYTES); + if !update.recovery { + self.range(selector, c::COMMIT_BYTES as u64, intent, grant)?; + } + if update.effects >= 4 { + self.range(data, 0, slot, grant)?; + } + self.range(selector, 0, commit, grant)?; + self.committed = Some(update.next); + self.recovery_pending = false; + self.usable = true; + Ok(()) + } +} + +impl PrivateMetadataMutation<'_> { + /// S3 must first durably publish its verified RESET_INTENT after Done and + /// verify the complete old prefix offslot. Only this same bound metadata + /// stream can be truncated; no payload/namespace handle is available. + pub(crate) fn reset_stream( + &mut self, + expected_len: u64, + grant: &mut MutationGrant, + ) -> Result<(), AdmissionError> { + if !self.usable || self.recovery_pending || self.stream_len != expected_len { + return Err(invalid(Problem::Changed)); + } + self.spend(2)?; + self.usable = false; + grant.check_time()?; + self.stream.set_len(0)?; + grant.check_time()?; + self.stream.sync_all()?; + grant.check_time()?; + if self.stream.metadata()?.len() != 0 { + return Err(invalid(Problem::Changed)); + } + self.stream_len = 0; + self.usable = true; + Ok(()) + } +} + +#[cfg(test)] +thread_local! {pub(crate) static METADATA_WRITE_CUT:std::cell::RefCell>=const {std::cell::RefCell::new(None)};} + +/// Global invalidation component for independently proven scoped roles. It +/// grants NO generation/workspace/payload ownership itself. The role module +/// retains its actual fixed local leases and immutable retirement/source proof. +pub(crate) struct PreparedScopedInvalidation { + epoch: PreparedEpoch, + read: ReservedBytes, + scratch: FixedControlScratch, +} +impl PreparedScopedInvalidation { + pub(crate) fn root_facts( + &self, + ) -> ( + super::locks::FileIdentity, + super::locks::FileIdentity, + [u8; 32], + super::locks::FileIdentity, + ) { + self.epoch.root_facts() + } +} +pub(crate) fn prepare_scoped_invalidation( + domain: &ReferenceDomain, + memory: &CleanupMemory, +) -> Result { + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(AdmissionError::Resource)?, + )?; + let mut read = memory + .reserve(2048) + .map_err(AdmissionError::Resource)? + .bytes(2048) + .map_err(AdmissionError::Resource)?; + let epoch = PreparedEpoch::prepare_local(domain, memory, &mut read, &mut scratch)?; + Ok(PreparedScopedInvalidation { + epoch, + read, + scratch, + }) +} +pub(crate) fn with_scoped_reference_invalidation( + prepared: &mut PreparedScopedInvalidation, + grant: &mut MutationGrant, + effect_entries: usize, + validate: &mut impl FnMut( + &mut FixedControlScratch, + &mut MutationGrant, + ) -> Result<(), AdmissionError>, + apply: &mut impl FnMut(EpochAdvance, &mut MutationGrant) -> Result<(), AdmissionError>, +) -> Result { + prepared.epoch.with_local_validated( + &mut prepared.read, + &mut prepared.scratch, + grant, + effect_entries, + validate, + apply, + ) +} + +/// Existing actual Exclusive root ownership is borrowed, never reacquired or +/// replaced by a marker. Error/unwind leaves the caller's real lease intact. +pub(crate) fn with_scoped_reference_invalidation_owned_root( + prepared: &mut PreparedScopedInvalidation, + root_use: &FixedLockedFile, + grant: &mut MutationGrant, + effect_entries: usize, + validate: &mut impl FnMut( + &mut FixedControlScratch, + &mut MutationGrant, + ) -> Result<(), AdmissionError>, + apply: &mut impl FnMut(EpochAdvance, &mut MutationGrant) -> Result<(), AdmissionError>, +) -> Result { + prepared.epoch.with_local_validated_owned_root( + Some(root_use), + &mut prepared.read, + &mut prepared.scratch, + grant, + effect_entries, + validate, + apply, + ) +} + +impl PreparedPrivateMetadata { + /// Offslot bounded advisory read of this exact captured metadata stream. + /// Callers reprepare after any write; no path/handle/authority is exported. + pub(crate) fn read_stream_exact( + &self, + offset: u64, + out: &mut [u8], + ) -> Result<(), AdmissionError> { + if out.len() > 4096 + || offset + .checked_add(out.len() as u64) + .is_none_or(|end| end > self.stream.stamp.unwrap().len()) + { + return Err(invalid(Problem::InvalidRecord)); + } + let stamp = self + .stream + .stamp + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + stamp.recheck(&self.stream_file)?; + super::locks::read_fixed_range(&self.stream_file, offset, out)?; + stamp.recheck(&self.stream_file) + } +} diff --git a/src/storage/maintenance_automatic_direct.rs b/src/storage/maintenance_automatic_direct.rs new file mode 100644 index 00000000..f6310530 --- /dev/null +++ b/src/storage/maintenance_automatic_direct.rs @@ -0,0 +1,137 @@ +//! Exact upgraded-protocol coverage for a Direct root born through the genuine +//! first-creation hook. It never authorizes adopting an older destination. +use super::*; +use crate::storage::locks::{ + FixedControlError as Error, FixedControlKind as Kind, FixedControlProblem as Problem, + FixedFileStamp, FixedLockedFile, RootedDirectory, +}; +use crate::storage::maintenance_budget::{ + CleanupMemory, MemoryBox, MemoryReservation, MutationGrant, +}; +fn bad() -> Error { + Error::Invalid(Problem::Changed) +} +fn native(e: NativeError) -> Error { + match e { + NativeError::Io(e) => e.into(), + _ => bad(), + } +} +pub(crate) struct PreparedAutomaticDirect { + destination: FileIdentity, + artifact: FileIdentity, + writer: FileIdentity, + use_lock: FileIdentity, + header: [u8; 72], + epoch_stamp: FixedFileStamp, + cohort_stamp: FixedFileStamp, + epoch: [u8; REFERENCE_EPOCH_SIZE], + read: [u8; REFERENCE_EPOCH_SIZE], + _floor: MemoryReservation, +} +/// Facts alone cannot create coverage: observe_reference_epoch validates the +/// actual factory-born fixed epoch, coordination incarnation and physical root. +pub(crate) fn prepare_automatic_direct( + layout: &DirectLayout, + memory: &CleanupMemory, +) -> Result>, Error> { + let floor = memory + .reserve(std::mem::size_of::() + 16384) + .map_err(Error::Resource)?; + let domain = ReferenceDomain::direct(layout).map_err(native)?; + let epoch = match observe_reference_epoch(&domain).map_err(native)? { + EpochObservation::Verified(epoch) => epoch, + EpochObservation::Pending(_) => return Ok(None), + }; + if epoch.record.scope.kind != 2 || !epoch.record.covered { + return Ok(None); + } + let root = RootedDirectory::open_readonly_root(&domain.artifact_root, epoch.record.root)?; + let cohort_file = root.open_readonly_file(b".use-cohort.json")?; + let cohort_stamp = FixedFileStamp::capture(&cohort_file, Kind::File)?; + if cohort_stamp.len() > 1024 { + return Err(bad()); + } + let mut body = [0; 1024]; + super::super::locks::read_fixed_range( + &cohort_file, + 0, + &mut body[..cohort_stamp.len() as usize], + )?; + let cohort: ArtifactCohortRecord = + serde_json::from_slice(&body[..cohort_stamp.len() as usize]).map_err(|_| bad())?; + if cohort.origin != CohortOrigin::GuardedCreation + || cohort_stamp.identity() != epoch.record.cohort + || cohort.root != epoch.record.root + || cohort.coordination != epoch.record.use_lock + || cohort.coordination_incarnation != epoch.record.use_incarnation + { + return Ok(None); + } + cohort_stamp.recheck(&cohort_file)?; + let file = root.open_readonly_file(REFERENCE_EPOCH_FILE.as_bytes())?; + let stamp = FixedFileStamp::capture(&file, Kind::File)?; + if stamp.identity() != epoch.file || stamp.len() != REFERENCE_EPOCH_SIZE as u64 { + return Err(bad()); + } + let mut image = [0; REFERENCE_EPOCH_SIZE]; + super::super::locks::read_fixed_range(&file, 0, &mut image)?; + stamp.recheck(&file)?; + if image != encode_reference_epoch(&epoch.record).map_err(native)? { + return Err(bad()); + } + MemoryBox::new( + PreparedAutomaticDirect { + destination: epoch.record.scope.destination, + artifact: epoch.record.root, + writer: epoch.record.scope.writer, + use_lock: epoch.record.use_lock, + header: super::super::locks::encode_coordination_incarnation( + epoch.record.use_incarnation, + ), + epoch_stamp: stamp, + cohort_stamp, + epoch: image, + read: [0; REFERENCE_EPOCH_SIZE], + _floor: floor, + }, + memory, + ) + .map(Some) + .map_err(Error::Resource) +} +impl PreparedAutomaticDirect { + /// Caller retains its actual writer, native family/resource and root-use + /// exclusion. This only compares already prepared bytes and named identities. + pub(crate) fn verify( + &mut self, + destination: &RootedDirectory, + artifact: &RootedDirectory, + writer: &FixedLockedFile, + root_use: &FixedLockedFile, + grant: &mut MutationGrant, + ) -> Result<(), Error> { + grant.check()?; + if destination.identity() != self.destination + || artifact.identity() != self.artifact + || writer.mode() != LockMode::Exclusive + || root_use.mode() != LockMode::Exclusive + || super::super::locks::file_identity(writer.file()).map_err(native)? != self.writer + || super::super::locks::file_identity(root_use.file()).map_err(native)? != self.use_lock + || !root_use.matches_fixed_header(&self.header)? + { + return Err(bad()); + } + grant.take_entries(2)?; + let cohort = artifact.open_readonly_file(b".use-cohort.json")?; + self.cohort_stamp.recheck(&cohort)?; + let epoch = artifact.open_readonly_file(REFERENCE_EPOCH_FILE.as_bytes())?; + self.epoch_stamp.recheck(&epoch)?; + super::super::locks::read_fixed_range(&epoch, 0, &mut self.read)?; + if self.read != self.epoch { + return Err(bad()); + } + grant.check_time()?; + Ok(()) + } +} diff --git a/src/storage/maintenance_backup.rs b/src/storage/maintenance_backup.rs new file mode 100644 index 00000000..b3eb4795 --- /dev/null +++ b/src/storage/maintenance_backup.rs @@ -0,0 +1,2014 @@ +//! Replacement-backup recovery facts and single-leaf effects. Journal ownership +//! and durable intent belong to the candidate engine, never to this adapter. +use super::locks::{FileIdentity, FixedControlKind, FixedFileStamp, RootedDirectory}; +use super::maintenance_budget::{CleanupMemory, MemoryReservation, MutationGrant}; +use super::maintenance_legacy_association::LegacyAssociation; +use super::maintenance_ownership::{expected_reference_current, ReferenceDomain, ReferenceEpoch}; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::io::{self, Read}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum BackupIssue { + Unknown, + Changed, + ResourceLimit, + Cancelled, + Io(io::ErrorKind), +} +impl From for BackupIssue { + fn from(error: io::Error) -> Self { + Self::Io(error.kind()) + } +} + +/// Only the exact historical producer spelling is recognized. A name is an +/// address, never origin, association or loader usability evidence. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct BackupAddress { + key: [u8; 32], + name: [u8; 255], + len: u8, +} +impl BackupAddress { + pub(crate) fn parse(name: &[u8]) -> Option { + if !name.starts_with(b".replaced-") || name.get(74) != Some(&b'-') { + return None; + } + let digits = name.get(75..)?; + if digits.is_empty() || (digits.len() > 1 && digits[0] == b'0') { + return None; + } + let mut timestamp = 0u128; + for byte in digits { + if !byte.is_ascii_digit() { + return None; + } + timestamp = timestamp + .checked_mul(10)? + .checked_add(u128::from(*byte - b'0'))?; + } + let mut key = [0; 32]; + for (index, pair) in name.get(10..74)?.chunks_exact(2).enumerate() { + fn nibble(byte: u8) -> Option { + match byte { + b'0'..=b'9' => Some(byte - b'0'), + b'a'..=b'f' => Some(byte - b'a' + 10), + _ => None, + } + } + key[index] = nibble(pair[0])? * 16 + nibble(pair[1])?; + } + let mut owned = [0; 255]; + owned.get_mut(..name.len())?.copy_from_slice(name); + Some(Self { + key, + name: owned, + len: name.len() as u8, + }) + } + pub(crate) fn key(&self) -> [u8; 32] { + self.key + } + pub(crate) fn name(&self) -> &[u8] { + &self.name[..usize::from(self.len)] + } + fn canonical(&self) -> [u8; 64] { + let mut bytes = [0; 64]; + for (index, byte) in self.key.iter().enumerate() { + bytes[index * 2] = b"0123456789abcdef"[usize::from(byte >> 4)]; + bytes[index * 2 + 1] = b"0123456789abcdef"[usize::from(byte & 15)]; + } + bytes + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct BackupPlan { + pub(crate) address: BackupAddress, + pub(crate) root: FileIdentity, + pub(crate) shard: FileIdentity, + pub(crate) source: FileIdentity, + pub(crate) payload: FixedFileStamp, + pub(crate) payload_sha256: [u8; 32], + pub(crate) source_epoch: u64, +} + +/// Entirely advisory, thread-local and accounted. The completed association is +/// the opaque full native/manifest/cache join, including selected LoaderUsable; +/// it cannot be replaced with a schema boolean or a backup-name inference. +/// Its one selected-payload validation already happened in the association job. +pub(crate) struct PreparedBackup { + association: LegacyAssociation, + _database: File, + _manifest: File, + _root: RootedDirectory, + _shard: RootedDirectory, + _source: RootedDirectory, + _payload: File, + plan: BackupPlan, + _floor: MemoryReservation, +} + +// Keep the owned source files, sealed association, epoch and budget explicit at verification. +#[allow(clippy::too_many_arguments)] +pub(crate) fn prepare_backup( + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + address: BackupAddress, + association: LegacyAssociation, + database: File, + manifest: File, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + // Reserve before constructing any retained or transient handles/buffers. + let floor = memory + .reserve(std::mem::size_of::() + 8192) + .map_err(|_| BackupIssue::ResourceLimit)?; + if cancel() { + return Err(BackupIssue::Cancelled); + } + if association.source_epoch() != expected + || !expected_reference_current(domain, expected, memory) + .map_err(|_| BackupIssue::Changed)? + { + return Err(BackupIssue::Changed); + } + let root = RootedDirectory::open_readonly_root( + domain.artifact_root(), + association.artifact_root_identity(), + )?; + if !association.matches(&database, &manifest, &root) { + return Err(BackupIssue::Changed); + } + let canonical = address.canonical(); + let shard = root.open_directory_readonly(&canonical[..2])?; + match shard.open_directory_readonly(&canonical) { + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Ok(_) => return Err(BackupIssue::Unknown), + Err(error) => return Err(error.into()), + } + let source = shard.open_directory_readonly(address.name())?; + let mut payload = source.open_readonly_file(b"partition.json")?; + let stamp = FixedFileStamp::capture(&payload, FixedControlKind::File) + .map_err(|_| BackupIssue::Changed)?; + if association.selected_backup_loader_usable() != Some(true) + || !association.selected_backup_matches(address.key(), source.identity(), stamp) + { + return Err(BackupIssue::Unknown); + } + // This is a digest pass over the SAME certified selected payload, not a + // second payload validation, and never runs with dispatcher authority. + let mut hash = Sha256::new(); + let mut buffer = [0; 4096]; + loop { + if cancel() { + return Err(BackupIssue::Cancelled); + } + let count = payload.read(&mut buffer)?; + if count == 0 { + break; + } + hash.update(&buffer[..count]); + } + stamp.recheck(&payload).map_err(|_| BackupIssue::Changed)?; + // Keep DB + manifest + one cache payload as the aggregate maximum. The + // replacement handle is retained only after its exact stamp is rechecked. + drop(payload); + let payload = source.open_readonly_file(b"partition.json")?; + stamp.recheck(&payload).map_err(|_| BackupIssue::Changed)?; + if cancel() { + return Err(BackupIssue::Cancelled); + } + if shard.open_directory_readonly(address.name())?.identity() != source.identity() + || !association.matches(&database, &manifest, &root) + || !expected_reference_current(domain, expected, memory) + .map_err(|_| BackupIssue::Changed)? + { + return Err(BackupIssue::Changed); + } + let plan = BackupPlan { + address, + root: root.identity(), + shard: shard.identity(), + source: source.identity(), + payload: stamp, + payload_sha256: hash.finalize().into(), + source_epoch: expected.counter(), + }; + Ok(PreparedBackup { + association, + _database: database, + _manifest: manifest, + _root: root, + _shard: shard, + _source: source, + _payload: payload, + plan, + _floor: floor, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn historical_backup_address_is_exact_and_lossless() { + let name = format!(".replaced-{}-{}", "ab".repeat(32), u128::MAX); + let address = BackupAddress::parse(name.as_bytes()).unwrap(); + assert_eq!(address.key(), [0xab; 32]); + assert_eq!(address.name(), name.as_bytes()); + assert_eq!(address.canonical(), b"ab".repeat(32).as_slice()); + } + #[test] + fn backup_names_do_not_supply_origin_or_guess_other_formats() { + let key = "ab".repeat(32); + for name in [ + format!(".replaced-{key}-"), + format!(".replaced-{key}-01"), + format!(".replaced-{key}-+1"), + format!(".replaced-{key}-{}0", u128::MAX), + format!(".replaced-{key}-1/child"), + format!(".replaced-{}-1", key.to_uppercase()), + format!("{key}.bak"), + format!(".replaced-{key}-1\0"), + ] { + assert!(BackupAddress::parse(name.as_bytes()).is_none(), "{name}"); + } + } +} + +/// Resolve the selected CURRENT source without invoking repair, snapshots or +/// loader admission. The caller has already captured the exact target binding. +/// Only these two source handles survive association preparation. +pub(crate) fn open_current_source( + target: &super::inventory::MaintenanceTarget, + memory: &CleanupMemory, +) -> Result<(File, File), BackupIssue> { + use super::locks::{open_fixed_control, FixedControlScratch}; + let path_bytes = target + .repository_root + .as_os_str() + .len() + .checked_add(target.state_root.as_os_str().len()) + .and_then(|n| { + n.checked_add( + target + .storage_root + .as_ref() + .map_or(0, |p| p.as_os_str().len()), + ) + }) + .and_then(|n| { + n.checked_add(target.direct_pair.as_ref().map_or(0, |p| { + p.database.as_os_str().len() + p.manifest.as_os_str().len() + })) + }) + .and_then(|n| n.checked_mul(8)) + .and_then(|n| n.checked_add(8192)) + .ok_or(BackupIssue::ResourceLimit)?; + let _paths = memory + .reserve(path_bytes) + .map_err(|_| BackupIssue::ResourceLimit)?; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(|_| BackupIssue::ResourceLimit)?, + ) + .map_err(|_| BackupIssue::Unknown)?; + let (database, manifest) = if let Some(pair) = &target.direct_pair { + (pair.database.clone(), pair.manifest.clone()) + } else { + let storage = target.storage_root.as_ref().ok_or(BackupIssue::Unknown)?; + let root = RootedDirectory::open_readonly_root( + storage, + super::locks::path_identity(storage).map_err(|_| BackupIssue::Changed)?, + )?; + let mut pointer = root.open_readonly_file(b"active.json")?; + let before = FixedFileStamp::capture(&pointer, FixedControlKind::File) + .map_err(|_| BackupIssue::Changed)?; + if before.len() > 1024 { + return Err(BackupIssue::Unknown); + } + let mut wire = [0u8; 1024]; + let mut filled = 0; + loop { + let n = pointer.read(&mut wire[filled..])?; + if n == 0 { + break; + } + filled += n; + if filled == wire.len() { + break; + } + } + before.recheck(&pointer).map_err(|_| BackupIssue::Changed)?; + before + .recheck(&root.open_readonly_file(b"active.json")?) + .map_err(|_| BackupIssue::Changed)?; + let active: super::managed::ActiveGeneration = + serde_json::from_slice(&wire[..filled]).map_err(|_| BackupIssue::Unknown)?; + if active.schema_version != 2 { + return Err(BackupIssue::Unknown); + } + super::layout::validate_generation_id(&active.generation_id) + .map_err(|_| BackupIssue::Unknown)?; + let generation = super::layout::ManagedLayout::new(storage) + .generation(&active.generation_id) + .map_err(|_| BackupIssue::Unknown)?; + (generation.db_path(), generation.manifest_path()) + }; + let database = open_fixed_control(&database, FixedControlKind::File, false, &mut scratch) + .map_err(|_| BackupIssue::Changed)?; + let manifest = open_fixed_control(&manifest, FixedControlKind::File, false, &mut scratch) + .map_err(|_| BackupIssue::Changed)?; + Ok((database, manifest)) +} + +use super::inventory::MaintenanceTarget; +use super::locks::{FixedControlError, FixedControlProblem}; +use super::maintenance_admission::{ + self as admission, PreparedCanonical, PreparedControlUpdate, PreparedPrivateMetadata, + PreparedTargetBinding, +}; +use super::maintenance_budget::MemoryBox; +use super::maintenance_candidate::CandidateReport; +use super::maintenance_journal::BackupRestoreRecord; +const BACKUP_CONTROL_BYTES: usize = 2048; +const SAVED_CURSOR_BYTES: usize = super::maintenance::CURSOR_BYTES; +const MARKER: &[u8] = b".restore-origin"; +fn changed() -> FixedControlError { + FixedControlError::Invalid(FixedControlProblem::Changed) +} +fn fixed_issue(e: BackupIssue) -> FixedControlError { + match e { + BackupIssue::ResourceLimit => { + FixedControlError::Resource(super::maintenance_budget::ResourceLimit::CeilingExceeded) + } + BackupIssue::Io(kind) => std::io::Error::from(kind).into(), + _ => changed(), + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct RetainedAssociationPrefix { + len: u64, + digest: [u8; 32], +} +#[derive(Clone, Copy)] +struct RestoreControl { + cursor: [u8; SAVED_CURSOR_BYTES], + record: BackupRestoreRecord, + database: FixedFileStamp, + manifest: FixedFileStamp, + association: [u8; 32], + epoch: u64, + source_receipt: Option, + prefix: Option, +} +impl RestoreControl { + fn encode(&self) -> [u8; BACKUP_CONTROL_BYTES] { + let mut out = [0; BACKUP_CONTROL_BYTES]; + out[..8].copy_from_slice(b"CGBACK01"); + out[8..8 + SAVED_CURSOR_BYTES].copy_from_slice(&self.cursor); + out[1120..1632].copy_from_slice(&self.record.encode()); + self.database + .encode_checkpoint((&mut out[1632..1712]).try_into().unwrap()); + self.manifest + .encode_checkpoint((&mut out[1712..1792]).try_into().unwrap()); + out[1792..1824].copy_from_slice(&self.association); + out[1824..1832].copy_from_slice(&self.epoch.to_le_bytes()); + if let Some(receipt) = self.source_receipt { + out[1832] = 1; + receipt.encode_checkpoint((&mut out[1840..1920]).try_into().unwrap()); + } + if let Some(prefix) = self.prefix { + out[1920..1928].copy_from_slice(&prefix.len.to_le_bytes()); + out[1928..1960].copy_from_slice(&prefix.digest); + } + let digest = Sha256::digest(&out[..2016]); + out[2016..].copy_from_slice(&digest); + out + } + fn decode(wire: &[u8]) -> Result { + if wire.len() != BACKUP_CONTROL_BYTES + || &wire[..8] != b"CGBACK01" + || wire[1113..1120] + .iter() + .chain(wire[1833..1840].iter()) + .chain(wire[1960..2016].iter()) + .any(|b| *b != 0) + || (wire[1920..1928] == [0; 8] && wire[1928..1960].iter().any(|b| *b != 0)) + || wire[1832] > 1 + || (wire[1832] == 0 && wire[1840..1920].iter().any(|b| *b != 0)) + || Sha256::digest(&wire[..2016])[..] != wire[2016..] + { + return Err(changed()); + } + Ok(Self { + cursor: wire[8..1113].try_into().unwrap(), + record: BackupRestoreRecord::decode(wire[1120..1632].try_into().unwrap()) + .map_err(|_| changed())?, + database: FixedFileStamp::decode_checkpoint(wire[1632..1712].try_into().unwrap())?, + manifest: FixedFileStamp::decode_checkpoint(wire[1712..1792].try_into().unwrap())?, + association: wire[1792..1824].try_into().unwrap(), + epoch: u64::from_le_bytes(wire[1824..1832].try_into().unwrap()), + source_receipt: if wire[1832] == 1 { + Some(FixedFileStamp::decode_checkpoint( + wire[1840..1920].try_into().unwrap(), + )?) + } else { + None + }, + prefix: if wire[1920..1928] == [0; 8] { + None + } else { + Some(RetainedAssociationPrefix { + len: u64::from_le_bytes(wire[1920..1928].try_into().unwrap()), + digest: wire[1928..1960].try_into().unwrap(), + }) + }, + }) + } +} +pub(crate) fn is_backup_payload(payload: &[u8]) -> bool { + payload.starts_with(b"CGBACK01") + || super::maintenance_legacy_stream::LegacyStreamHeader::decode(payload).is_ok_and( + |header| header.kind() == super::maintenance_legacy_stream::LegacyStreamKind::Backup, + ) +} + +/// Warm state is advisory. Durable control and the actual fresh creator marker +/// determine restart behavior; dropping this state cannot authorize adoption. +#[derive(Default)] +pub(crate) struct BackupPreparationState { + certified: Option>, + created: Option, + selected: super::maintenance_legacy_association::SelectedBackupAssociationState, + address: Option, +} +impl BackupPreparationState { + pub(crate) fn has_parked_payloads(&self) -> bool { + self.certified.is_some() || self.selected.job.is_some() || self.selected.transport.is_some() + } + pub(crate) fn clear(&mut self) { + self.certified = None; + self.created = None; + self.selected.clear(); + self.address = None; + } + pub(crate) fn accept_certified( + &mut self, + backup: PreparedBackup, + memory: &CleanupMemory, + ) -> Result<(), BackupIssue> { + self.certified = + Some(MemoryBox::new(backup, memory).map_err(|_| BackupIssue::ResourceLimit)?); + Ok(()) + } +} +struct CreatedDestination { + directory: FileIdentity, + epoch: u64, + marker: FixedFileStamp, +} +enum BackupAction { + PreparationPending, + Association { + step: MemoryBox, + committed: MemoryBox>, + }, + Control { + metadata: MemoryBox, + update: MemoryBox, + reset_stream: Option, + }, + Create { + ready: MemoryBox, + metadata: MemoryBox, + control: MemoryBox, + marker_wire: [u8; 512], + created: Option, + }, + Restore { + ready: MemoryBox, + metadata: MemoryBox, + control: MemoryBox, + database: File, + manifest: File, + source: RootedDirectory, + payload: File, + marker_wire: [u8; 512], + }, + CleanupSource { + ready: MemoryBox, + metadata: MemoryBox, + control: MemoryBox, + restored: FixedFileStamp, + source: RootedDirectory, + receipt: Option, + scratch: MemoryBox<[u8; 4096]>, + }, +} +pub(crate) struct BackupWork { + action: BackupAction, + unknown: bool, +} +fn control_work( + metadata: MemoryBox, + control: &[u8], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, FixedControlError> { + let reset_stream = if control.len() == SAVED_CURSOR_BYTES { + let current = RestoreControl::decode(metadata.payload().ok_or_else(changed)?)?; + if current.record.stage != 4 { + return Err(changed()); + } + verify_backup_prefix(&metadata, ¤t, memory, cancel)? + .then(|| current.prefix.unwrap().len) + } else { + None + }; + let update = boxed_backup_control(&metadata, control, memory)?; + MemoryBox::new( + BackupWork { + unknown: false, + action: BackupAction::Control { + metadata, + update, + reset_stream, + }, + }, + memory, + ) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +fn association_prefix_digest( + metadata: &PreparedPrivateMetadata, + len: u64, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result<[u8; 32], FixedControlError> { + let _floor = memory.reserve(8192).map_err(FixedControlError::Resource)?; + if metadata.stream_len() != len { + return Err(changed()); + } + let mut hash = Sha256::new(); + let mut bytes = [0; 4096]; + let mut offset = 0u64; + while offset < len { + if cancel() { + return Err(changed()); + } + let n = (len - offset).min(bytes.len() as u64) as usize; + metadata.read_stream_exact(offset, &mut bytes[..n])?; + hash.update(&bytes[..n]); + offset += n as u64; + } + Ok(hash.finalize().into()) +} +fn retained_prefix_len( + prefix: Option, + stage: u8, + stream_len: u64, +) -> Result, FixedControlError> { + let Some(prefix) = prefix else { + return if stream_len == 0 { + Ok(None) + } else { + Err(changed()) + }; + }; + if stage == 4 && stream_len == 0 { + return Ok(None); + } + if stream_len != prefix.len { + return Err(changed()); + } + Ok(Some(prefix.len)) +} +fn verify_backup_prefix( + metadata: &PreparedPrivateMetadata, + control: &RestoreControl, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + let Some(len) = + retained_prefix_len(control.prefix, control.record.stage, metadata.stream_len())? + else { + return Ok(false); + }; + if association_prefix_digest(metadata, len, memory, cancel)? != control.prefix.unwrap().digest { + return Err(changed()); + } + Ok(true) +} + +fn only_origin(directory: &RootedDirectory, scratch: &mut [u8; 4096]) -> std::io::Result { + let mut known = true; + match directory.visit_entries(scratch, &mut |name| { + if name == super::maintenance_origin::ORIGIN_NAME { + Ok(()) + } else { + known = false; + Err(io::ErrorKind::Interrupted.into()) + } + }) { + Ok(()) => Ok(true), + Err(error) if !known && error.kind() == io::ErrorKind::Interrupted => Ok(false), + Err(error) => Err(error), + } +} +fn marker_wire(record: BackupRestoreRecord) -> [u8; 512] { + // The pre-mkdir intent remains the immutable creation marker even when + // later control records carry the actual new destination identity. + let mut intent = record; + intent.stage = 1; + intent.destination = FileIdentity::from_fixed_words(0, 0); + intent.encode() +} +fn creator( + directory: &RootedDirectory, + record: BackupRestoreRecord, + expected_wire: &[u8; 512], +) -> Result { + if super::locks::created_file_nonce(&directory.pin_handle()?)? != Some(record.operation) { + return Err(changed()); + } + let file = directory.open_readonly_file(MARKER)?; + let stamp = FixedFileStamp::capture(&file, FixedControlKind::File)?; + if stamp.len() != 512 || super::locks::created_file_nonce(&file)? != Some(record.operation) { + return Err(changed()); + } + let mut wire = [0; 512]; + super::locks::read_fixed_range(&file, 0, &mut wire)?; + if wire != *expected_wire { + return Err(changed()); + } + stamp.recheck(&file)?; + stamp.recheck(&directory.open_readonly_file(MARKER)?)?; + Ok(stamp) +} +// Construct charged retained values before entering the backup state-machine +// frames; large by-value admission Results never overlap their downstream work. +#[inline(never)] +fn boxed_backup_canonical( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + memory: &CleanupMemory, + required: bool, +) -> Result>, FixedControlError> { + let ready = match admission::prepare_canonical(target, domain, expected, memory) { + Ok(ready) => ready, + Err(_) if !required => return Ok(None), + Err(error) => return Err(error), + }; + MemoryBox::new(ready, memory) + .map(Some) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +fn boxed_backup_metadata( + target: &MaintenanceTarget, + scope: &admission::RootOriginBinding, + memory: &CleanupMemory, + required: bool, +) -> Result>, FixedControlError> { + let metadata = match admission::prepare_private_metadata(target, scope, memory) { + Ok(metadata) => metadata, + Err(_) if !required => return Ok(None), + Err(error) => return Err(error), + }; + MemoryBox::new(metadata, memory) + .map(Some) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +fn boxed_backup_control( + metadata: &PreparedPrivateMetadata, + control: &[u8], + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let update = admission::prepare_metadata_control( + &metadata.origin_scope(), + metadata.committed().ok_or_else(changed)?, + control, + memory, + )?; + MemoryBox::new(update, memory).map_err(FixedControlError::Resource) +} + +/// All source association and checksums are prepared off the mutation permit. +/// The record is recoverable intent, never an origin receipt for old bytes. +#[inline(never)] +pub(crate) fn prepare_work( + target: &MaintenanceTarget, + _binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut BackupPreparationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + if cancel() { + return Err(changed()); + } + let Some(ready) = + boxed_backup_canonical(target, domain, expected, memory, state.certified.is_some())? + else { + return Ok(None); + }; + let Some(metadata) = boxed_backup_metadata( + target, + &ready.origin_scope(), + memory, + state.certified.is_some(), + )? + else { + return Ok(None); + }; + if !matches!( + metadata.recovery(), + super::maintenance_controls::RecoveryStep::Committed(_) + ) { + return Ok(None); + } + let Some(payload) = metadata.payload() else { + return Ok(None); + }; + if !payload.starts_with(b"CGBACK01") { + prepare_selection( + target, _binding, domain, expected, state, ready, metadata, memory, cancel, + ) + } else { + prepare_restore( + target, domain, expected, state, ready, metadata, memory, cancel, + ) + } +} +#[inline(never)] +// Keep borrowed domain/epoch separate from owned canonical/metadata capabilities and budget. +#[allow(clippy::too_many_arguments)] +fn prepare_selection( + target: &MaintenanceTarget, + _binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut BackupPreparationState, + ready: MemoryBox, + metadata: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + if state.certified.is_none() { + return prepare_proof( + target, _binding, domain, expected, state, ready, metadata, memory, cancel, + ); + } + + prepare_certified_selection(expected, state, ready, metadata, memory, cancel) +} + +#[inline(never)] +fn prepare_certified_selection( + expected: &ReferenceEpoch, + state: &mut BackupPreparationState, + ready: MemoryBox, + metadata: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let payload = metadata.payload().ok_or_else(changed)?; + let backup = state.certified.take().ok_or_else(changed)?; + if backup.association.source_epoch() != expected + || backup.plan.root != ready.origin_scope().root + { + return Err(changed()); + } + let (mut cursor, prefix) = if let Some(cursor) = + super::maintenance_candidate::idle_control_cursor(payload, ready.origin_scope()) + .map_err(|_| changed())? + { + if metadata.stream_len() != 0 { + return Err(changed()); + } + (cursor, None) + } else { + let header = super::maintenance_legacy_stream::LegacyStreamHeader::decode(payload)?; + if header.kind() != super::maintenance_legacy_stream::LegacyStreamKind::Backup + || !header.done() + || metadata.stream_len() != header.committed_end() + || header.committed_end() == 0 + { + return Err(changed()); + } + let prefix = RetainedAssociationPrefix { + len: header.committed_end(), + digest: association_prefix_digest(&metadata, header.committed_end(), memory, cancel)?, + }; + ( + super::maintenance::MaintenanceCursor::decode( + header.cursor(), + ready.origin_scope().incarnation, + ) + .map_err(|_| changed())?, + Some(prefix), + ) + }; + let mut address_bytes = [0; 128]; + if backup.plan.address.name().len() > address_bytes.len() { + return Err(changed()); + } + address_bytes[..backup.plan.address.name().len()].copy_from_slice(backup.plan.address.name()); + cursor.inspected( + super::maintenance::WorkCategory::TemporaryOrBackup, + super::maintenance::CursorAddress(address_bytes), + ); + let cursor = cursor.encode(); + let mut operation = [0; 32]; + getrandom::fill(&mut operation).map_err(|_| changed())?; + if operation == [0; 32] { + return Err(changed()); + } + let mut name = [0; 128]; + let address = backup.plan.address; + if address.name().len() > name.len() { + return Err(changed()); + } + name[..address.name().len()].copy_from_slice(address.name()); + let control = RestoreControl { + cursor, + record: BackupRestoreRecord { + stage: 1, + source_epoch: expected.counter(), + root: backup.plan.root, + shard: backup.plan.shard, + source: backup.plan.source, + payload: backup.plan.payload, + digest: backup.plan.payload_sha256, + destination: FileIdentity::from_fixed_words(0, 0), + operation, + name, + name_len: address.name().len() as u8, + }, + database: backup.association.database_stamp(), + manifest: backup.association.manifest_stamp(), + association: backup.association.cache_certificate(), + epoch: expected.counter(), + source_receipt: None, + prefix, + }; + control_work(metadata, &control.encode(), memory, cancel).map(Some) +} +#[inline(never)] +// Preserve distinct authority ownership and native-proof stage inputs without an aggregate context. +#[allow(clippy::too_many_arguments)] +fn prepare_proof( + target: &MaintenanceTarget, + _binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut BackupPreparationState, + ready: MemoryBox, + metadata: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let Some((root, address)) = + select_backup_proof_address(domain, state, &ready, &metadata, memory, cancel)? + else { + return Ok(None); + }; + drop(metadata); + drop(ready); + let progress = advance_backup_association( + target, domain, expected, state, root, address, memory, cancel, + )?; + finish_backup_association( + target, _binding, domain, expected, state, address, progress, memory, cancel, + ) +} + +// Cursor decoding and backup selection complete before native association +// begins. Neither cursor/control images nor certification outputs live above it. +#[inline(never)] +fn select_backup_proof_address( + domain: &ReferenceDomain, + state: &mut BackupPreparationState, + ready: &PreparedCanonical, + metadata: &PreparedPrivateMetadata, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, FixedControlError> { + let payload = metadata.payload().ok_or_else(changed)?; + + let cursor = if let Some(cursor) = + super::maintenance_candidate::idle_control_cursor(payload, ready.origin_scope()) + .map_err(|_| changed())? + { + cursor + } else if let Ok(header) = super::maintenance_legacy_stream::LegacyStreamHeader::decode(payload) + { + if header.kind() != super::maintenance_legacy_stream::LegacyStreamKind::Backup { + return Ok(None); + } + super::maintenance::MaintenanceCursor::decode( + header.cursor(), + ready.origin_scope().incarnation, + ) + .map_err(|_| changed())? + } else { + return Ok(None); + }; + let root = + RootedDirectory::open_readonly_root(domain.artifact_root(), ready.origin_scope().root)?; + let address = if let Some(address) = state.address { + address + } else { + let Some(address) = + select_missing_backup(&root, &cursor, memory, cancel).map_err(fixed_issue)? + else { + return Ok(None); + }; + state.address = Some(address); + address + }; + Ok(Some((root, address))) +} + +#[inline(never)] +// Keep actual native-source handles and epoch/budget controls explicit at this stack boundary. +#[allow(clippy::too_many_arguments)] +fn advance_backup_association( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut BackupPreparationState, + root: RootedDirectory, + address: BackupAddress, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + let progress = if state.selected.needs_source_handles() { + let (database, manifest) = open_current_source(target, memory).map_err(fixed_issue)?; + super::maintenance_legacy_stream::prepare_association_source( + target, + domain, + expected, + database, + manifest, + root, + domain.artifact_root(), + &target.repository_root, + None, + Some(address), + &mut state.selected, + memory, + cancel, + )? + } else { + drop(root); + super::maintenance_legacy_stream::prepare_parked_association_source( + target, + domain, + expected, + domain.artifact_root(), + &target.repository_root, + None, + Some(address), + &mut state.selected, + memory, + cancel, + )? + }; + Ok(progress) +} + +// Certification retains real handles only after the stream stage returned. +// Its recursive control preparation never retains a native association frame. +#[inline(never)] +// Separate owned source handles from certification state across the bounded recursion boundary. +#[allow(clippy::too_many_arguments)] +fn finish_backup_association( + target: &MaintenanceTarget, + _binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut BackupPreparationState, + address: BackupAddress, + progress: super::maintenance_legacy_stream::AssociationSourceProgress, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + match progress { + super::maintenance_legacy_stream::AssociationSourceProgress::Pending => { + pending_work(memory).map(Some) + } + super::maintenance_legacy_stream::AssociationSourceProgress::Step(step) => { + association_work(step, memory).map(Some) + } + super::maintenance_legacy_stream::AssociationSourceProgress::Complete { + association, + database, + manifest, + } => { + let prepared = prepare_backup( + domain, + expected, + address, + association, + database, + manifest, + memory, + cancel, + ) + .map_err(fixed_issue)?; + state.address = None; + state + .accept_certified(prepared, memory) + .map_err(fixed_issue)?; + prepare_work(target, _binding, domain, expected, state, memory, cancel) + } + } +} +#[inline(never)] +fn boxed_restore_control( + payload: &[u8], + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let control = RestoreControl::decode(payload)?; + MemoryBox::new(control, memory).map_err(FixedControlError::Resource) +} +#[inline(never)] +// Restore stages carry separate canonical/metadata capabilities and cooperative resource controls. +#[allow(clippy::too_many_arguments)] +fn prepare_restore( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut BackupPreparationState, + ready: MemoryBox, + metadata: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let payload = metadata.payload().ok_or_else(changed)?; + + let control = boxed_restore_control(payload, memory)?; + if control.record.root != ready.origin_scope().root || control.association == [0; 32] { + return Err(changed()); + } + verify_backup_prefix(&metadata, &control, memory, cancel)?; + let address = + BackupAddress::parse(&control.record.name[..usize::from(control.record.name_len)]) + .ok_or_else(changed)?; + let canonical = address.canonical(); + let root = RootedDirectory::open_readonly_root(domain.artifact_root(), control.record.root)?; + let shard = root.open_directory_readonly(&canonical[..2])?; + if shard.identity() != control.record.shard { + return Err(changed()); + } + let (database, manifest) = open_current_source(target, memory).map_err(fixed_issue)?; + control.database.recheck(&database)?; + control.manifest.recheck(&manifest)?; + if control.record.stage == 4 { + let destination = shard.open_directory_readonly(&canonical)?; + if destination.identity() != control.record.destination { + return Err(changed()); + } + creator(&destination, control.record, &marker_wire(control.record))?; + let restored = destination.open_readonly_file(b"partition.json")?; + if super::locks::file_identity(&restored).map_err(|_| changed())? + != control.record.payload.identity() + { + return Err(changed()); + } + verify_digest( + &restored, + control.record.payload.len(), + control.record.digest, + cancel, + )?; + } + let source = match shard.open_directory_readonly(address.name()) { + Ok(source) => source, + Err(error) + if error.kind() == io::ErrorKind::NotFound + && control.record.stage == 4 + && control.source_receipt.is_some() => + { + return control_work(metadata, &control.cursor, memory, cancel).map(Some); + } + Err(error) => return Err(error.into()), + }; + if source.identity() != control.record.source { + return Err(changed()); + } + if control.record.stage == 4 { + return prepare_cleanup( + ready, metadata, control, shard, source, database, manifest, canonical, expected, + memory, cancel, + ); + } + if control.record.stage == 1 { + return prepare_creation( + ready, metadata, control, shard, source, canonical, expected, state, memory, cancel, + ); + } + let destination = shard.open_directory_readonly(&canonical)?; + if destination.identity() != control.record.destination { + return Err(changed()); + } + creator(&destination, control.record, &marker_wire(control.record))?; + if control.record.stage == 3 { + return prepare_verified( + metadata, + control, + root, + source, + address, + destination, + expected, + memory, + cancel, + ); + } + prepare_move( + ready, + metadata, + control, + source, + database, + manifest, + destination, + expected, + memory, + cancel, + ) +} +#[inline(never)] +// Cleanup retains distinct rooted handles, payload files and sealed control without changing layout. +#[allow(clippy::too_many_arguments)] +fn prepare_cleanup( + ready: MemoryBox, + metadata: MemoryBox, + mut control: MemoryBox, + shard: RootedDirectory, + source: RootedDirectory, + database: File, + manifest: File, + canonical: [u8; 64], + expected: &ReferenceEpoch, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let Some(receipt_stamp) = control.source_receipt else { + let mut work = control_work(metadata, &control.cursor, memory, cancel)?; + work.unknown = true; + return Ok(Some(work)); + }; + let mut scratch = [0; 4096]; + if !only_origin(&source, &mut scratch)? { + let mut work = control_work(metadata, &control.cursor, memory, cancel)?; + work.unknown = true; + return Ok(Some(work)); + } + let receipt = match source.open_readonly_file(super::maintenance_origin::ORIGIN_NAME) { + Ok(receipt) => { + receipt_stamp.recheck(&receipt)?; + Some(receipt) + } + Err(error) if error.kind() == io::ErrorKind::NotFound => None, + Err(error) => return Err(error.into()), + }; + let destination = shard.open_directory_readonly(&canonical)?; + if destination.identity() != control.record.destination { + return Err(changed()); + } + creator(&destination, control.record, &marker_wire(control.record))?; + drop((database, manifest)); + let restored = destination.open_readonly_file(b"partition.json")?; + if super::locks::file_identity(&restored).map_err(|_| changed())? + != control.record.payload.identity() + { + return Err(changed()); + } + verify_digest( + &restored, + control.record.payload.len(), + control.record.digest, + cancel, + )?; + let restored = FixedFileStamp::capture(&restored, FixedControlKind::File)?; + control.epoch = expected.counter(); + MemoryBox::new( + BackupWork { + unknown: false, + action: BackupAction::CleanupSource { + ready, + metadata, + control, + restored, + source, + receipt, + scratch: MemoryBox::new(scratch, memory).map_err(FixedControlError::Resource)?, + }, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +// Creation keeps actual source/shard handles separate from mutable restore control and budget. +#[allow(clippy::too_many_arguments)] +fn prepare_creation( + ready: MemoryBox, + metadata: MemoryBox, + mut control: MemoryBox, + shard: RootedDirectory, + source: RootedDirectory, + canonical: [u8; 64], + expected: &ReferenceEpoch, + state: &mut BackupPreparationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + if let Some(created) = state.created.take() { + let directory = shard.open_directory_readonly(&canonical)?; + if directory.identity() != created.directory + || creator(&directory, control.record, &marker_wire(control.record))? != created.marker + || created.epoch != expected.counter() + { + return Err(changed()); + } + control.record.stage = 2; + control.record.destination = created.directory; + control.epoch = created.epoch; + return control_work(metadata, &control.encode(), memory, cancel).map(Some); + } + match shard.open_directory_readonly(&canonical) { + Ok(directory) => { + let _marker = creator(&directory, control.record, &marker_wire(control.record))?; + control.record.stage = 2; + control.record.destination = directory.identity(); + control.epoch = expected.counter(); + return control_work(metadata, &control.encode(), memory, cancel).map(Some); + } + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => return Err(error.into()), + } + if expected.counter() != control.epoch { + return Err(changed()); + } + control + .record + .payload + .recheck(&source.open_readonly_file(b"partition.json")?)?; + let marker_wire = marker_wire(control.record); + MemoryBox::new( + BackupWork { + unknown: false, + action: BackupAction::Create { + ready, + metadata, + control, + marker_wire, + created: None, + }, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +// Destination verification retains separate rooted capabilities, source epoch and bounded resources. +#[allow(clippy::too_many_arguments)] +fn prepare_verified( + metadata: MemoryBox, + mut control: MemoryBox, + root: RootedDirectory, + source: RootedDirectory, + address: BackupAddress, + destination: RootedDirectory, + expected: &ReferenceEpoch, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + // An actual destination observation, rather than a stored usable claim, + // confirms the exact old payload survived its no-replace move. + let payload = destination.open_readonly_file(b"partition.json")?; + if super::locks::file_identity(&payload).map_err(|_| changed())? + != control.record.payload.identity() + { + return Err(changed()); + } + verify_digest( + &payload, + control.record.payload.len(), + control.record.digest, + cancel, + )?; + let origin = super::maintenance_origin::observe_detached_artifact_origin( + &root, + &source, + &payload, + address.key(), + memory, + cancel, + ); + let mut scratch = [0; 4096]; + if let Ok(super::maintenance_origin::OriginObservation::Observed(facts)) = origin { + if facts.directory_created + && facts.directory == source.identity() + && facts.payload == control.record.payload.identity() + && facts.byte_len == control.record.payload.len() + && facts.payload_sha256 == control.record.digest + && only_origin(&source, &mut scratch)? + { + let receipt = source.open_readonly_file(super::maintenance_origin::ORIGIN_NAME)?; + let stamp = FixedFileStamp::capture(&receipt, FixedControlKind::File)?; + if stamp.identity() == facts.receipt { + control.source_receipt = Some(stamp); + } + } + } + control.record.stage = 4; + control.epoch = expected.counter(); + control_work(metadata, &control.encode(), memory, cancel).map(Some) +} +#[inline(never)] +// No-replace transport keeps source/destination handles explicit alongside control and epoch. +#[allow(clippy::too_many_arguments)] +fn prepare_move( + ready: MemoryBox, + metadata: MemoryBox, + mut control: MemoryBox, + source: RootedDirectory, + database: File, + manifest: File, + destination: RootedDirectory, + expected: &ReferenceEpoch, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + if control.record.stage != 2 { + return Err(changed()); + } + let payload = match source.open_readonly_file(b"partition.json") { + Ok(payload) => { + if expected.counter() != control.epoch { + return Err(changed()); + } + control.record.payload.recheck(&payload)?; + verify_digest( + &payload, + control.record.payload.len(), + control.record.digest, + cancel, + )?; + match destination.open_readonly_file(b"partition.json") { + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + _ => return Err(changed()), + } + payload + } + Err(error) if error.kind() == io::ErrorKind::NotFound => { + let payload = destination.open_readonly_file(b"partition.json")?; + if super::locks::file_identity(&payload).map_err(|_| changed())? + != control.record.payload.identity() + { + return Err(changed()); + } + verify_digest( + &payload, + control.record.payload.len(), + control.record.digest, + cancel, + )?; + control.record.stage = 3; + control.epoch = expected.counter(); + return control_work(metadata, &control.encode(), memory, cancel).map(Some); + } + Err(error) => return Err(error.into()), + }; + MemoryBox::new( + BackupWork { + unknown: false, + action: BackupAction::Restore { + ready, + metadata, + marker_wire: marker_wire(control.record), + control, + database, + manifest, + source, + payload, + }, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +fn pending_work(memory: &CleanupMemory) -> Result, FixedControlError> { + MemoryBox::new( + BackupWork { + unknown: false, + action: BackupAction::PreparationPending, + }, + memory, + ) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +fn association_work( + step: MemoryBox, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + MemoryBox::new( + BackupWork { + unknown: false, + action: BackupAction::Association { + step, + committed: MemoryBox::new(None, memory).map_err(FixedControlError::Resource)?, + }, + }, + memory, + ) + .map_err(FixedControlError::Resource) +} + +fn verify_digest( + file: &File, + len: u64, + expected: [u8; 32], + cancel: &dyn Fn() -> bool, +) -> Result<(), FixedControlError> { + let before = FixedFileStamp::capture(file, FixedControlKind::File)?; + if before.len() != len { + return Err(changed()); + } + let mut bytes = [0; 4096]; + let mut offset = 0; + let mut digest = Sha256::new(); + while offset < len { + if cancel() { + return Err(changed()); + } + let n = usize::try_from((len - offset).min(bytes.len() as u64)).map_err(|_| changed())?; + super::locks::read_fixed_range(file, offset, &mut bytes[..n])?; + digest.update(&bytes[..n]); + offset += n as u64; + } + before.recheck(file)?; + if digest.finalize()[..] != expected { + return Err(changed()); + } + Ok(()) +} +impl BackupWork { + pub(crate) fn advance( + &mut self, + grant: &mut MutationGrant, + _confirmation: Option<&super::maintenance_scheduler::LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + let mut report = CandidateReport { + pending: true, + unknown: self.unknown, + reasons: u64::from(self.unknown), + checked_categories: 1 << 1, + inspected_entries: 1, + ..Default::default() + }; + match &mut self.action { + BackupAction::PreparationPending => {} + BackupAction::Association { step, committed } => { + **committed = Some(step.advance(grant)?); + } + BackupAction::Control { + metadata, + update, + reset_stream, + } => { + admission::with_private_metadata( + metadata, + grant, + update.effects() + usize::from(reset_stream.is_some()) * 2, + &mut |mutation, grant| { + if let Some(len) = reset_stream { + mutation.reset_stream(*len, grant)?; + } + mutation.publish_control(update, grant) + }, + )?; + } + BackupAction::Create { + ready, + metadata, + control, + marker_wire, + created, + } => { + let address = BackupAddress::parse( + &control.record.name[..usize::from(control.record.name_len)], + ) + .ok_or_else(changed)?; + let canonical = address.canonical(); + let mut observed = None; + admission::with_prepared_canonical(ready, grant, 30, &mut |mutation, grant| { + if mutation.directory().identity() != control.record.root + || mutation.epoch().before != control.epoch + { + return Err(changed()); + } + admission::with_reserved_canonical_private_metadata( + metadata, + mutation, + grant, + 0, + &mut |_, _| Ok(()), + )?; + let shard = mutation + .directory() + .open_directory_readonly(&canonical[..2])?; + if shard.identity() != control.record.shard { + return Err(changed()); + } + let source = shard.open_directory_readonly(address.name())?; + if source.identity() != control.record.source { + return Err(changed()); + } + control + .record + .payload + .recheck(&source.open_readonly_file(b"partition.json")?)?; + grant.check_time()?; + let directory = shard.create_directory(&canonical)?; + super::locks::set_created_file_nonce( + &directory.pin_handle()?, + &control.record.operation, + )?; + let marker = directory.create_new(MARKER)?; + super::locks::set_created_file_nonce(&marker, &control.record.operation)?; + super::locks::write_fixed_range(&marker, 0, marker_wire, grant)?; + directory.sync_namespace()?; + shard.sync_namespace()?; + let stamp = creator(&directory, control.record, marker_wire)?; + #[cfg(all(test, debug_assertions))] + fault::cut(control.record.root, BackupFault::AfterCreatorSync); + observed = Some(CreatedDestination { + directory: directory.identity(), + epoch: mutation.epoch().after, + marker: stamp, + }); + Ok(()) + })?; + *created = observed; + report.mutated_entries = 1; + } + BackupAction::CleanupSource { + ready, + metadata, + control, + restored, + source, + receipt, + scratch, + } => { + let address = BackupAddress::parse( + &control.record.name[..usize::from(control.record.name_len)], + ) + .ok_or_else(changed)?; + let canonical = address.canonical(); + admission::with_prepared_canonical(ready, grant, 30, &mut |mutation, grant| { + if mutation.directory().identity() != control.record.root + || mutation.epoch().before != control.epoch + { + return Err(changed()); + } + admission::with_reserved_canonical_private_metadata( + metadata, + mutation, + grant, + 0, + &mut |_, _| Ok(()), + )?; + let shard = mutation + .directory() + .open_directory_readonly(&canonical[..2])?; + if shard.identity() != control.record.shard + || shard.open_directory_readonly(address.name())?.identity() + != control.record.source + { + return Err(changed()); + } + let destination = shard.open_directory_readonly(&canonical)?; + if destination.identity() != control.record.destination + || source.identity() != control.record.source + { + return Err(changed()); + } + restored.recheck(&destination.open_readonly_file(b"partition.json")?)?; + if !source.contains_only( + Some(super::maintenance_origin::ORIGIN_NAME), + scratch, + grant, + )? { + return Err(changed()); + } + let expected = control.source_receipt.ok_or_else(changed)?; + if let Some(file) = receipt { + expected.recheck(file)?; + expected.recheck( + &source.open_readonly_file(super::maintenance_origin::ORIGIN_NAME)?, + )?; + grant.check_time()?; + source.unlink_file( + super::maintenance_origin::ORIGIN_NAME, + expected.identity(), + )?; + source.sync_namespace()?; + } else { + match source.open_readonly_file(super::maintenance_origin::ORIGIN_NAME) { + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + _ => return Err(changed()), + } + } + grant.check_time()?; + shard.remove_empty_directory(address.name(), control.record.source)?; + shard.sync_namespace()?; + Ok(()) + })?; + report.mutated_entries = 1; + report.reclaimed_entries = 1; + } + BackupAction::Restore { + ready, + metadata, + control, + database, + manifest, + source, + payload, + marker_wire, + } => { + let address = BackupAddress::parse( + &control.record.name[..usize::from(control.record.name_len)], + ) + .ok_or_else(changed)?; + let canonical = address.canonical(); + admission::with_prepared_canonical(ready, grant, 32, &mut |mutation, grant| { + if mutation.directory().identity() != control.record.root + || mutation.epoch().before != control.epoch + { + return Err(changed()); + } + admission::with_reserved_canonical_private_metadata( + metadata, + mutation, + grant, + 0, + &mut |_, _| Ok(()), + )?; + control.database.recheck(database)?; + control.manifest.recheck(manifest)?; + let shard = mutation + .directory() + .open_directory_readonly(&canonical[..2])?; + if shard.identity() != control.record.shard + || shard.open_directory_readonly(address.name())?.identity() + != control.record.source + || source.identity() != control.record.source + { + return Err(changed()); + } + let destination = shard.open_directory_readonly(&canonical)?; + if destination.identity() != control.record.destination { + return Err(changed()); + } + creator(&destination, control.record, marker_wire)?; + control.record.payload.recheck(payload)?; + control + .record + .payload + .recheck(&source.open_readonly_file(b"partition.json")?)?; + grant.check_time()?; + source.rename_file_no_replace( + b"partition.json", + &destination, + b"partition.json", + control.record.payload.identity(), + )?; + let restored = destination.open_readonly_file(b"partition.json")?; + if super::locks::file_identity(&restored).map_err(|_| changed())? + != control.record.payload.identity() + { + return Err(changed()); + } + destination.sync_namespace()?; + source.sync_namespace()?; + shard.sync_namespace()?; + #[cfg(all(test, debug_assertions))] + fault::cut(control.record.root, BackupFault::AfterPayloadRestore); + Ok(()) + })?; + report.mutated_entries = 1; + } + } + Ok(report) + } +} +pub(crate) fn finish_work( + work: &mut BackupWork, + state: &mut BackupPreparationState, +) -> Result<(), FixedControlError> { + if let BackupAction::Association { committed, .. } = &mut work.action { + if let Some(committed) = committed.take() { + super::maintenance_legacy_stream::finish_association_source_step( + &mut state.selected, + committed, + )?; + } + } + if let BackupAction::CleanupSource { receipt, .. } = &mut work.action { + drop(receipt.take()); + } + if let BackupAction::Create { created, .. } = &mut work.action { + state.created = created.take(); + } + Ok(()) +} + +#[cfg(all(test, debug_assertions))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum BackupFault { + AfterCreatorSync, + AfterPayloadRestore, +} +#[cfg(all(test, debug_assertions))] +pub(crate) use fault::arm_backup_fault; +#[cfg(all(test, debug_assertions))] +mod fault { + use super::*; + use std::sync::{ + atomic::{AtomicU64, Ordering}, + Mutex, + }; + type FaultCut = (FileIdentity, BackupFault, u64, bool); + static CUTS: Mutex<[Option; 8]> = Mutex::new([None; 8]); + static NEXT: AtomicU64 = AtomicU64::new(1); + pub(crate) struct BackupFaultGuard { + slot: usize, + nonce: u64, + } + impl Drop for BackupFaultGuard { + fn drop(&mut self) { + let mut cuts = CUTS.lock().unwrap(); + if cuts[self.slot].is_some_and(|(_, _, nonce, _)| nonce == self.nonce) { + cuts[self.slot] = None; + } + } + } + impl BackupFaultGuard { + pub(crate) fn reached(&self) -> bool { + CUTS.lock().unwrap()[self.slot] + .is_some_and(|(_, _, nonce, hit)| nonce == self.nonce && hit) + } + } + pub(crate) fn arm_backup_fault(root: FileIdentity, point: BackupFault) -> BackupFaultGuard { + let nonce = NEXT.fetch_add(1, Ordering::Relaxed); + let mut cuts = CUTS.lock().unwrap(); + let slot = cuts + .iter() + .position(Option::is_none) + .expect("backup fixture fault slots"); + cuts[slot] = Some((root, point, nonce, false)); + BackupFaultGuard { slot, nonce } + } + pub(super) fn cut(root: FileIdentity, point: BackupFault) { + let hit = { + let mut cuts = CUTS.lock().unwrap(); + if let Some(slot) = cuts + .iter() + .position(|cut| cut.is_some_and(|(r, p, _, hit)| r == root && p == point && !hit)) + { + cuts[slot].as_mut().unwrap().3 = true; + true + } else { + false + } + }; + if hit { + panic!("fixture backup restore cut: {point:?}"); + } + } +} + +/// Noncreating qualification observation of the actual durable role control. +/// A released cursor is distinguishable from an uninitialized installation. +#[cfg(all(test, debug_assertions))] +pub(crate) fn backup_control_stage_for_test( + target: &MaintenanceTarget, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let domain = if let Some(pair) = &target.direct_pair { + ReferenceDomain::direct(&super::layout::DirectLayout::new( + &pair.database, + &pair.manifest, + )) + } else { + ReferenceDomain::managed(&super::layout::ManagedLayout::new( + target.storage_root.as_ref().ok_or_else(changed)?, + )) + } + .map_err(|_| changed())?; + let epoch = match super::maintenance_ownership::observe_reference_epoch(&domain) + .map_err(|_| changed())? + { + super::maintenance_ownership::EpochObservation::Verified(epoch) => epoch, + _ => return Err(changed()), + }; + let ready = admission::prepare_canonical(target, &domain, &epoch, memory)?; + let metadata = admission::prepare_private_metadata(target, &ready.origin_scope(), memory)?; + match metadata.payload() { + Some(payload) if payload.starts_with(b"CGBACK01") => { + Ok(Some(RestoreControl::decode(payload)?.record.stage)) + } + Some(payload) if payload.len() == SAVED_CURSOR_BYTES => { + super::maintenance::MaintenanceCursor::decode( + payload.try_into().unwrap(), + ready.origin_scope().incarnation, + ) + .map_err(|_| changed())?; + Ok(Some(0)) + } + _ => Ok(None), + } +} + +/// Bounded fair address discovery only. A historical-looking name or a missing +/// destination is never a substitute for selected native/cache association. +pub(crate) fn select_missing_backup( + root: &RootedDirectory, + cursor: &super::maintenance::MaintenanceCursor, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, BackupIssue> { + let _floor = memory + .reserve(16 * 1024) + .map_err(|_| BackupIssue::ResourceLimit)?; + let mut selection = cursor.selection(); + let mut root_scratch = [0; 4096]; + let mut shard_scratch = [0; 4096]; + root.visit_entries(&mut root_scratch, &mut |prefix| { + if cancel() { + return Err(io::ErrorKind::Interrupted.into()); + } + if prefix.len() != 2 + || !prefix + .iter() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(b)) + { + return Ok(()); + } + let shard = root.open_directory_readonly(prefix)?; + shard.visit_entries(&mut shard_scratch, &mut |name| { + if cancel() { + return Err(io::ErrorKind::Interrupted.into()); + } + let Some(address) = BackupAddress::parse(name) else { + return Ok(()); + }; + let canonical = address.canonical(); + if &canonical[..2] != prefix || address.name().len() > 128 { + return Ok(()); + } + match shard.open_directory_readonly(&canonical) { + Ok(_) => return Ok(()), + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(_) => return Ok(()), + } + let mut bytes = [0; 128]; + bytes[..address.name().len()].copy_from_slice(address.name()); + selection.observe( + super::maintenance::WorkCategory::TemporaryOrBackup, + super::maintenance::CursorAddress(bytes), + ); + Ok(()) + }) + }) + .map_err(|error| { + if error.kind() == io::ErrorKind::Interrupted { + BackupIssue::Cancelled + } else { + BackupIssue::Io(error.kind()) + } + })?; + Ok(selection.selected().and_then(|(_, address)| { + let len = address.0.iter().position(|byte| *byte == 0).unwrap_or(128); + BackupAddress::parse(&address.0[..len]) + })) +} + +#[cfg(test)] +mod retained_prefix_tests { + use super::*; + #[test] + fn dc_backup_restore_prefix_rejects_foreign_tail_and_only_stage4_recovers_truncation() { + let prefix = Some(RetainedAssociationPrefix { + len: 4096, + digest: [7; 32], + }); + assert_eq!(retained_prefix_len(None, 1, 0).unwrap(), None); + assert!(retained_prefix_len(None, 4, 1).is_err()); + for stage in 1..=4 { + assert_eq!( + retained_prefix_len(prefix, stage, 4096).unwrap(), + Some(4096) + ); + assert!(retained_prefix_len(prefix, stage, 4097).is_err()); + assert!(retained_prefix_len(prefix, stage, 4095).is_err()); + if stage < 4 { + assert!(retained_prefix_len(prefix, stage, 0).is_err()); + } + } + assert_eq!(retained_prefix_len(prefix, 4, 0).unwrap(), None); + } + #[test] + fn dc_backup_restore_control_preserves_sealed_prefix_and_legacy_zero_fields() { + let mut nonce = [0; 16]; + getrandom::fill(&mut nonce).unwrap(); + let path = std::env::temp_dir().join(format!( + "dc-backup-prefix-{:032x}", + u128::from_le_bytes(nonce) + )); + let file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .open(&path) + .unwrap(); + let stamp = FixedFileStamp::capture(&file, FixedControlKind::File).unwrap(); + let id = stamp.identity(); + let address = format!(".replaced-{}-1", "a".repeat(64)); + let mut name = [0; 128]; + name[..address.len()].copy_from_slice(address.as_bytes()); + let mut control = RestoreControl { + cursor: [0; SAVED_CURSOR_BYTES], + record: BackupRestoreRecord { + stage: 1, + source_epoch: 7, + root: id, + shard: id, + source: id, + payload: stamp, + digest: [3; 32], + destination: id, + operation: [4; 32], + name, + name_len: address.len() as u8, + }, + database: stamp, + manifest: stamp, + association: [5; 32], + epoch: 7, + source_receipt: None, + prefix: None, + }; + assert_eq!( + RestoreControl::decode(&control.encode()).unwrap().prefix, + None + ); + control.prefix = Some(RetainedAssociationPrefix { + len: 6144, + digest: [6; 32], + }); + let encoded = control.encode(); + assert_eq!( + RestoreControl::decode(&encoded).unwrap().prefix, + control.prefix + ); + assert!(encoded[1960..2016].iter().all(|byte| *byte == 0)); + let mut zero_len = encoded; + zero_len[1920..1928].fill(0); + let digest = Sha256::digest(&zero_len[..2016]); + zero_len[2016..].copy_from_slice(&digest); + assert!(RestoreControl::decode(&zero_len).is_err()); + let mut foreign_reserved = encoded; + foreign_reserved[1960] = 1; + let digest = Sha256::digest(&foreign_reserved[..2016]); + foreign_reserved[2016..].copy_from_slice(&digest); + assert!(RestoreControl::decode(&foreign_reserved).is_err()); + let mut corrupt = encoded; + corrupt[1928] ^= 1; + assert!(RestoreControl::decode(&corrupt).is_err()); + drop(file); + std::fs::remove_file(path).unwrap(); + } +} diff --git a/src/storage/maintenance_budget.rs b/src/storage/maintenance_budget.rs new file mode 100644 index 00000000..d0e577e9 --- /dev/null +++ b/src/storage/maintenance_budget.rs @@ -0,0 +1,613 @@ +//! Shared accounting for requested allocation layouts, not allocator slack/RSS. +//! These primitives carry no storage ownership or deletion authority. +use std::alloc::{alloc, alloc_zeroed, dealloc, Layout}; +use std::marker::PhantomData; +use std::ptr::NonNull; +use std::rc::Rc; +use std::sync::atomic::{fence, AtomicUsize, Ordering}; +use std::time::{Duration, Instant}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ResourceLimit { + ArithmeticOverflow, + CeilingExceeded, + AllocationFailed, +} + +struct Account { + references: AtomicUsize, + limit: usize, + used: AtomicUsize, + peak: AtomicUsize, +} + +/// One explicit, fallible, exactly laid-out shared allocation. Clones allocate +/// nothing; reservations retain this allocation until their last release. +pub(crate) struct CleanupMemory { + account: NonNull, +} +// SAFETY: the pointer remains alive through atomic reference counting, and all +// mutable shared fields are atomic. The limit never changes after construction. +unsafe impl Send for CleanupMemory {} +unsafe impl Sync for CleanupMemory {} + +impl CleanupMemory { + pub(crate) fn new(limit: usize) -> Result { + let layout = Layout::new::(); + if limit < layout.size() { + return Err(ResourceLimit::CeilingExceeded); + } + // SAFETY: valid nonzero layout; null is handled before initialization. + let account = NonNull::new(unsafe { alloc(layout) }.cast::()) + .ok_or(ResourceLimit::AllocationFailed)?; + // SAFETY: this allocation is uniquely owned and large/aligned enough. + unsafe { + account.as_ptr().write(Account { + references: AtomicUsize::new(1), + limit, + used: AtomicUsize::new(layout.size()), + peak: AtomicUsize::new(layout.size()), + }); + } + Ok(Self { account }) + } + + fn account(&self) -> &Account { + // SAFETY: every handle retains one live account reference. + unsafe { self.account.as_ref() } + } + + pub(crate) fn reserve(&self, bytes: usize) -> Result { + let account = self.account(); + let mut old = account.used.load(Ordering::Relaxed); + loop { + let new = old + .checked_add(bytes) + .ok_or(ResourceLimit::ArithmeticOverflow)?; + if new > account.limit { + return Err(ResourceLimit::CeilingExceeded); + } + match account + .used + .compare_exchange_weak(old, new, Ordering::AcqRel, Ordering::Relaxed) + { + Ok(_) => { + account.peak.fetch_max(new, Ordering::Relaxed); + return Ok(MemoryReservation { + memory: self.clone(), + bytes, + }); + } + Err(current) => old = current, + } + } + } + + #[cfg(test)] + pub(crate) fn used(&self) -> usize { + self.account().used.load(Ordering::Acquire) + } + #[cfg(test)] + pub(crate) fn peak(&self) -> usize { + self.account().peak.load(Ordering::Acquire) + } + #[cfg(test)] + pub(crate) fn limit(&self) -> usize { + self.account().limit + } +} + +impl Clone for CleanupMemory { + fn clone(&self) -> Self { + if self.account().references.fetch_add(1, Ordering::Relaxed) >= isize::MAX as usize { + std::process::abort(); + } + Self { + account: self.account, + } + } +} +impl Drop for CleanupMemory { + fn drop(&mut self) { + if self.account().references.fetch_sub(1, Ordering::Release) == 1 { + fence(Ordering::Acquire); + // SAFETY: the last reference owns the initialized allocation. + unsafe { + std::ptr::drop_in_place(self.account.as_ptr()); + dealloc(self.account.as_ptr().cast(), Layout::new::()); + } + } + } +} + +pub(crate) struct MemoryReservation { + memory: CleanupMemory, + bytes: usize, +} +impl MemoryReservation { + #[cfg(test)] + pub(crate) fn split(&mut self, bytes: usize) -> Result { + if bytes > self.bytes { + return Err(ResourceLimit::CeilingExceeded); + } + self.bytes -= bytes; + Ok(Self { + memory: self.memory.clone(), + bytes, + }) + } + pub(crate) fn remaining(&self) -> usize { + self.bytes + } + pub(crate) fn bytes(self, len: usize) -> Result { + if len > self.bytes { + return Err(ResourceLimit::CeilingExceeded); + } + let layout = Layout::array::(len).map_err(|_| ResourceLimit::ArithmeticOverflow)?; + let pointer = if len == 0 { + NonNull::dangling() + } else { + // SAFETY: valid layout, checked charge already retained by self. + NonNull::new(unsafe { alloc_zeroed(layout) }).ok_or(ResourceLimit::AllocationFailed)? + }; + Ok(ReservedBytes { + pointer, + len, + _reservation: self, + }) + } +} +impl Drop for MemoryReservation { + fn drop(&mut self) { + self.memory + .account() + .used + .fetch_sub(self.bytes, Ordering::AcqRel); + } +} + +pub(crate) struct ReservedBytes { + pointer: NonNull, + len: usize, + _reservation: MemoryReservation, +} +// SAFETY: uniquely owned bytes; sharing permits immutable access only. +unsafe impl Send for ReservedBytes {} +unsafe impl Sync for ReservedBytes {} +impl ReservedBytes { + pub(crate) fn as_slice(&self) -> &[u8] { + // SAFETY: live initialized allocation, including valid empty slice. + unsafe { std::slice::from_raw_parts(self.pointer.as_ptr(), self.len) } + } + pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] { + // SAFETY: exclusive borrow guarantees unique access to the allocation. + unsafe { std::slice::from_raw_parts_mut(self.pointer.as_ptr(), self.len) } + } + #[cfg(test)] + pub(crate) fn reserved_bytes(&self) -> usize { + self._reservation.remaining() + } +} +impl Drop for ReservedBytes { + fn drop(&mut self) { + if self.len != 0 { + // SAFETY: original pointer/layout; reservation drops AFTER this body. + unsafe { + dealloc( + self.pointer.as_ptr(), + Layout::array::(self.len).expect("validated byte layout"), + ); + } + } + } +} + +/// Fallible, exactly laid-out owned state. Its pointee drops before the shared +/// reservation is released. This keeps large prepared values out of repeatedly +/// copied Result/Option frames without using a second accounting pool. +pub(crate) struct MemoryBox { + pointer: NonNull, + _reservation: MemoryReservation, +} +// SAFETY: the allocation has one owner; borrowing follows ordinary T rules. +unsafe impl Send for MemoryBox {} +unsafe impl Sync for MemoryBox {} +impl MemoryBox { + /// Allocate charged storage without constructing a large value on stack. + pub(crate) fn new_uninit( + memory: &CleanupMemory, + ) -> Result>, ResourceLimit> { + let layout = Layout::new::(); + let reservation = memory.reserve(layout.size())?; + Self::new_uninit_reserved(reservation) + } + /// Reuse a caller's retained layout allowance without charging a second + /// pool or first constructing the pointee in a by-value stack frame. + pub(crate) fn new_uninit_reserved( + reservation: MemoryReservation, + ) -> Result>, ResourceLimit> { + let layout = Layout::new::(); + if reservation.remaining() < layout.size() { + return Err(ResourceLimit::CeilingExceeded); + } + let pointer = if layout.size() == 0 { + NonNull::dangling() + } else { + // SAFETY: exact nonzero allocation, null checked before ownership. + NonNull::new(unsafe { alloc(layout) }.cast::>()) + .ok_or(ResourceLimit::AllocationFailed)? + }; + Ok(MemoryBox { + pointer, + _reservation: reservation, + }) + } + pub(crate) fn new(value: T, memory: &CleanupMemory) -> Result { + let layout = Layout::new::(); + let reservation = memory.reserve(layout.size())?; + let pointer = if layout.size() == 0 { + NonNull::dangling() + } else { + // SAFETY: exact nonzero T layout. Null is handled before writing. + NonNull::new(unsafe { alloc(layout) }.cast::()) + .ok_or(ResourceLimit::AllocationFailed)? + }; + // SAFETY: correctly aligned unique storage, also valid for a ZST write. + unsafe { pointer.as_ptr().write(value) }; + Ok(Self { + pointer, + _reservation: reservation, + }) + } +} +impl MemoryBox { + /// Move the actual owned value into its next owner without cloning handles. + /// Call only off authority; a retained destination needs its own accounting. + #[inline(never)] + pub(crate) fn into_inner(self) -> T { + let owner = std::mem::ManuallyDrop::new(self); + // SAFETY: consuming the sole owner transfers the initialized T once; + // ManuallyDrop prevents the old pointee destructor from running. + let value = unsafe { owner.pointer.as_ptr().read() }; + let layout = Layout::new::(); + if layout.size() != 0 { + // SAFETY: the moved allocation uses its exact original layout. + unsafe { dealloc(owner.pointer.as_ptr().cast(), layout) }; + } + // SAFETY: this field has not been moved or dropped. Release it once, + // after deallocation, while T now belongs exclusively to the caller. + drop(unsafe { std::ptr::read(&owner._reservation) }); + value + } +} +impl MemoryBox> { + /// # Safety + /// Every field of T must be initialized with its valid unique owned value. + /// No alias may retain ownership of a field transferred into this storage. + pub(crate) unsafe fn assume_init(self) -> MemoryBox { + let owner = std::mem::ManuallyDrop::new(self); + MemoryBox { + pointer: owner.pointer.cast(), + // SAFETY: consumes the sole reservation without dropping it twice. + _reservation: unsafe { std::ptr::read(&owner._reservation) }, + } + } +} +impl std::ops::Deref for MemoryBox { + type Target = T; + fn deref(&self) -> &T { + // SAFETY: initialized pointee remains alive for this owner's lifetime. + unsafe { self.pointer.as_ref() } + } +} +impl std::ops::DerefMut for MemoryBox { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: unique owner and exclusive borrow prevent aliased mutation. + unsafe { self.pointer.as_mut() } + } +} +impl Drop for MemoryBox { + fn drop(&mut self) { + struct Allocation(NonNull); + impl Drop for Allocation { + fn drop(&mut self) { + let layout = Layout::new::(); + if layout.size() != 0 { + // SAFETY: original pointer/layout, freed exactly once. + unsafe { dealloc(self.0.as_ptr().cast(), layout) }; + } + } + } + // The local guard deallocates on both normal completion and unwinding, + // before the reservation field drops. T's destructor runs exactly once. + let _allocation = Allocation(self.pointer); + // SAFETY: this owner holds the live, uniquely owned initialized T. + unsafe { std::ptr::drop_in_place(self.pointer.as_ptr()) }; + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum GrantExhausted { + Entries, + Time, +} + +/// Cooperative limits. A syscall may exceed the deadline. No grant or authority +/// crosses threads, and no fresh grant may be substituted inside an admission. +pub(crate) struct MutationGrant { + started: Instant, + duration: Duration, + remaining: usize, + _same_thread: PhantomData>, +} +impl MutationGrant { + pub(crate) fn start() -> Self { + Self::with_limits(100, Duration::from_millis(100)) + } + #[cfg(test)] + pub(crate) fn for_test(entries: usize, duration: Duration) -> Self { + Self::with_limits(entries, duration) + } + fn with_limits(entries: usize, duration: Duration) -> Self { + Self { + started: Instant::now(), + duration, + remaining: entries, + _same_thread: PhantomData, + } + } + pub(crate) fn check(&self) -> Result<(), GrantExhausted> { + self.check_time()?; + if self.remaining == 0 { + Err(GrantExhausted::Entries) + } else { + Ok(()) + } + } + /// Check before each effect whose entry charge was already reserved. Unlike + /// check(), this permits performing the final successfully reserved entry. + pub(crate) fn check_time(&self) -> Result<(), GrantExhausted> { + let elapsed = self.started.elapsed(); + if elapsed >= self.duration { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "cleanup grant expired: elapsed_us={} remaining_entries={}", + elapsed.as_micros(), + self.remaining + ); + } + Err(GrantExhausted::Time) + } else { + Ok(()) + } + } + pub(crate) fn take_entry(&mut self) -> Result<(), GrantExhausted> { + self.take_entries(1) + } + pub(crate) fn take_entries(&mut self, count: usize) -> Result<(), GrantExhausted> { + self.check()?; + if count > self.remaining { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "cleanup grant entries exhausted: requested={count}, remaining={}", + self.remaining + ); + } + return Err(GrantExhausted::Entries); + } + self.remaining -= count; + Ok(()) + } + pub(crate) fn remaining_entries(&self) -> usize { + self.remaining + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn dc_memory_box_alignment_exact_charge_and_drop_order() { + #[repr(align(256))] + struct Aligned(u64); + let memory = CleanupMemory::new(8192).unwrap(); + let before = memory.used(); + let mut value = MemoryBox::new(Aligned(7), &memory).unwrap(); + assert_eq!((&*value as *const Aligned as usize) % 256, 0); + assert_eq!(memory.used() - before, std::mem::size_of::()); + value.0 = 9; + assert_eq!(value.0, 9); + drop(value); + assert_eq!(memory.used(), before); + struct Check<'a>(&'a CleanupMemory, usize); + impl Drop for Check<'_> { + fn drop(&mut self) { + assert!(self.0.used() >= self.1 + std::mem::size_of::()); + } + } + drop(MemoryBox::new(Check(&memory, before), &memory).unwrap()); + assert_eq!(memory.used(), before); + } + #[test] + fn dc_memory_box_fieldwise_large_initialization_is_charged_and_drops_once() { + let memory = CleanupMemory::new(128 * 1024).unwrap(); + let before = memory.used(); + let drops = std::cell::Cell::new(0); + struct Chunk<'a> { + bytes: [u8; 64 * 1024], + drops: &'a std::cell::Cell, + } + impl Drop for Chunk<'_> { + fn drop(&mut self) { + self.drops.set(self.drops.get() + 1); + } + } + let mut uninit = MemoryBox::>::new_uninit(&memory).unwrap(); + assert_eq!(memory.used() - before, std::mem::size_of::>()); + let pointer = uninit.as_mut_ptr(); + // SAFETY: each field is initialized directly; no reference to invalid T + // or temporary large aggregate is constructed before assume_init. + unsafe { + std::ptr::addr_of_mut!((*pointer).bytes) + .cast::() + .write_bytes(7, 64 * 1024); + std::ptr::addr_of_mut!((*pointer).drops).write(&drops); + } + let initialized = unsafe { uninit.assume_init() }; + assert_eq!(initialized.bytes[65535], 7); + assert_eq!(drops.get(), 0); + drop(initialized); + assert_eq!(drops.get(), 1); + assert_eq!(memory.used(), before); + drop(MemoryBox::>::new_uninit(&memory).unwrap()); + assert_eq!(drops.get(), 1); + assert_eq!(memory.used(), before); + } + #[test] + fn dc_memory_box_into_inner_transfers_drop_once_and_releases_exact_charge() { + let memory = CleanupMemory::new(4096).unwrap(); + let before = memory.used(); + let drops = std::cell::Cell::new(0); + struct Owned<'a>(&'a std::cell::Cell); + impl Drop for Owned<'_> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } + } + let value = MemoryBox::new(Owned(&drops), &memory).unwrap(); + assert_eq!(memory.used(), before + std::mem::size_of::>()); + let actual = value.into_inner(); + assert_eq!(memory.used(), before); + assert_eq!(drops.get(), 0); + drop(actual); + assert_eq!(drops.get(), 1); + let zst = MemoryBox::new((), &memory).unwrap(); + let () = zst.into_inner(); + assert_eq!(memory.used(), before); + } + #[test] + fn dc_memory_box_failed_reservation_and_panicking_pointee_release() { + let memory = CleanupMemory::new(128).unwrap(); + let before = memory.used(); + assert!(matches!( + MemoryBox::new([0u8; 256], &memory), + Err(ResourceLimit::CeilingExceeded) + )); + assert_eq!(memory.used(), before); + struct Panics(u8); + impl Drop for Panics { + fn drop(&mut self) { + let _ = self.0; + panic!("expected pointee panic"); + } + } + let owned = MemoryBox::new(Panics(1), &memory).unwrap(); + assert!(std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| drop(owned))).is_err()); + assert_eq!(memory.used(), before); + let zero = MemoryBox::new((), &memory).unwrap(); + assert_eq!(memory.used(), before); + drop(zero); + } + + #[test] + fn dc_memory_work_types_cross_threads() { + fn send_sync() {} + send_sync::(); + send_sync::(); + send_sync::(); + } + + #[test] + fn dc_memory_checks_layout_overflow_and_duplicate_buffers() { + assert!(matches!( + CleanupMemory::new(0), + Err(ResourceLimit::CeilingExceeded) + )); + let memory = CleanupMemory::new(usize::MAX).unwrap(); + assert_eq!(memory.limit(), usize::MAX); + let floor = memory.used(); + assert!(matches!( + memory.reserve(usize::MAX), + Err(ResourceLimit::ArithmeticOverflow) + )); + let a = memory.reserve(100).unwrap().bytes(100).unwrap(); + let b = memory.reserve(100).unwrap().bytes(100).unwrap(); + assert_eq!(memory.used(), floor + 200); + assert_eq!(a.reserved_bytes(), 100); + drop(a); + assert_eq!(memory.used(), floor + 100); + drop(b); + assert_eq!(memory.used(), floor); + let memory = CleanupMemory::new(floor + 20).unwrap(); + let _a = memory.reserve(11).unwrap().bytes(11).unwrap(); + assert!(matches!( + memory.reserve(10), + Err(ResourceLimit::CeilingExceeded) + )); + } + + #[test] + fn dc_memory_split_failure_and_account_lifetime() { + let memory = CleanupMemory::new(4096).unwrap(); + let floor = memory.used(); + let mut reservation = memory.reserve(20).unwrap(); + assert!(reservation.split(21).is_err()); + let child = reservation.split(8).unwrap(); + assert_eq!(reservation.remaining(), 12); + assert_eq!(memory.used(), floor + 20); + assert!(child.bytes(9).is_err()); + assert_eq!(memory.used(), floor + 12); + let mut bytes = reservation.bytes(12).unwrap(); + bytes.as_mut_slice()[11] = 42; + assert_eq!(bytes.as_slice()[11], 42); + drop(memory); + drop(bytes); // Buffer owns the last account reference. + } + + #[test] + fn dc_memory_simultaneous_reservations_share_one_ceiling() { + let memory = CleanupMemory::new(4096).unwrap(); + let floor = memory.used(); + let barrier = std::sync::Arc::new(std::sync::Barrier::new(3)); + std::thread::scope(|scope| { + for _ in 0..2 { + let account = memory.clone(); + let barrier = barrier.clone(); + scope.spawn(move || { + let _bytes = account.reserve(1024).unwrap().bytes(1024).unwrap(); + barrier.wait(); + barrier.wait(); + }); + } + barrier.wait(); + assert_eq!(memory.used(), floor + 2048); + assert!(memory.reserve(4096 - floor - 2048 + 1).is_err()); + barrier.wait(); + }); + assert_eq!(memory.used(), floor); + assert_eq!(memory.peak(), floor + 2048); + } + + #[test] + fn dc_grant_zero_entry101_deadline_and_atomic_transition() { + let mut zero = MutationGrant::for_test(0, Duration::from_secs(1)); + let mut effects = 0; + if zero.take_entry().is_ok() { + effects += 1; + } + assert_eq!(effects, 0); + let mut grant = MutationGrant::start(); + grant.take_entries(99).unwrap(); + assert_eq!(grant.take_entries(2), Err(GrantExhausted::Entries)); + assert_eq!(grant.remaining_entries(), 1); + grant.take_entry().unwrap(); + assert_eq!(grant.check_time(), Ok(())); + assert_eq!(grant.take_entry(), Err(GrantExhausted::Entries)); + let mut expired = MutationGrant::with_limits(100, Duration::ZERO); + assert_eq!(expired.take_entry(), Err(GrantExhausted::Time)); + assert_eq!(expired.remaining_entries(), 100); + } +} diff --git a/src/storage/maintenance_candidate.rs b/src/storage/maintenance_candidate.rs new file mode 100644 index 00000000..92bc9c38 --- /dev/null +++ b/src/storage/maintenance_candidate.rs @@ -0,0 +1,3503 @@ +//! Candidate preparation owns advisory data only. Publication of journal +//! metadata and mutation of payloads require distinct retained admission guards. +use super::inventory::MaintenanceTarget; +use super::layout::{DirectLayout, ManagedLayout}; +use super::locks::{self, FixedControlKind, FixedFileStamp, RootedDirectory}; +use super::maintenance_admission::PreparedTargetBinding; +use super::maintenance_budget::{CleanupMemory, MemoryBox, MemoryReservation, MutationGrant}; +use super::maintenance_origin::{self, OriginFacts, OriginObservation, OriginPhase}; +use super::maintenance_ownership::{self as ownership, EpochObservation, ReferenceDomain}; +use sha2::{Digest, Sha256}; +use std::cell::RefCell; +use std::io::Read; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub(crate) struct CandidateReport { + pub(crate) pending: bool, + pub(crate) deferred: bool, + pub(crate) unknown: bool, + pub(crate) observations_complete: bool, + pub(crate) checked_categories: u8, + pub(crate) inspected_entries: u64, + pub(crate) mutated_entries: u64, + pub(crate) reclaimed_entries: u64, + /// Logical length of reclaimed regular files; never an allocated-byte claim. + pub(crate) reclaimed_bytes: u64, + pub(crate) reclaimed_allocated_bytes: Option, + pub(crate) reasons: u64, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum CandidateError { + ResourceLimit, + Io(std::io::ErrorKind), + Stale, + InvalidEvidence, + Cancelled, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum CandidateStatus { + Complete, + Pending, + Deferred, + NeedsDisposal, +} +#[derive(Debug, Clone, Copy)] +pub(crate) struct CandidateAdvance { + pub(crate) status: CandidateStatus, + pub(crate) report: CandidateReport, +} +pub(crate) fn candidate_reason(bit: u8) -> Option<&'static str> { + Some(match bit { + 0 => "unknown_program_origin", + 1 => "reference_in_use", + 2 => "maintenance_admission_busy", + 3 => "legacy_quiescence_required", + 4 => "needs_recovery", + 5 => "source_changed", + 6 => "resource_limit", + 7 => "retirement_in_progress", + 8 => "permission_denied", + 9 => "unsupported_layout", + 10 => "new_direct_active", + 11 => "cancelled", + 12 => "cleanup_io_error", + _ => return None, + }) +} +fn io(error: std::io::Error) -> CandidateError { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("cleanup candidate filesystem error: {error:?}"); + } + CandidateError::Io(error.kind()) +} +fn evidence(error: crate::error::NativeError) -> CandidateError { + match error { + crate::error::NativeError::Io(error) => io(error), + crate::error::NativeError::SourceChanged { .. } => CandidateError::Stale, + _ => CandidateError::InvalidEvidence, + } +} +fn admission(error: locks::FixedControlError) -> CandidateError { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("cleanup admission rejected: {error:?}"); + } + match error { + locks::FixedControlError::Io(error) => io(error), + locks::FixedControlError::Resource(_) => CandidateError::ResourceLimit, + _ => CandidateError::Stale, + } +} + +const CONTROL_BYTES: usize = + 8 + super::maintenance_journal::RETIREMENT_FRAME_BYTES + super::maintenance::CURSOR_BYTES; +struct Control { + leaf: Option, + cursor: super::maintenance::MaintenanceCursor, +} +impl Control { + fn decode( + payload: &[u8], + scope: super::maintenance_admission::RootOriginBinding, + ) -> Result { + if payload.len() == super::maintenance::CURSOR_BYTES { + return Ok(Self { + leaf: None, + cursor: super::maintenance::MaintenanceCursor::decode( + payload.try_into().unwrap(), + scope.incarnation, + ) + .map_err(|_| CandidateError::InvalidEvidence)?, + }); + } + if payload == b"CGBUILD1" { + return Ok(Self { + leaf: None, + cursor: super::maintenance::MaintenanceCursor::new(scope.incarnation), + }); + } + if payload.len() != CONTROL_BYTES || &payload[..8] != b"CGCAND01" { + return Err(CandidateError::InvalidEvidence); + } + let frame: &[u8; 512] = payload[8..520].try_into().unwrap(); + let leaf = if frame.iter().all(|b| *b == 0) { + None + } else { + Some( + super::maintenance_journal::RetirementLeaf::decode(frame) + .map_err(|_| CandidateError::InvalidEvidence)?, + ) + }; + if leaf.is_some_and(|l| l.scope != scope) { + return Err(CandidateError::Stale); + } + let cursor = super::maintenance::MaintenanceCursor::decode( + payload[520..].try_into().unwrap(), + scope.incarnation, + ) + .map_err(|_| CandidateError::InvalidEvidence)?; + Ok(Self { leaf, cursor }) + } + fn encode(&self) -> [u8; CONTROL_BYTES] { + let mut b = [0; CONTROL_BYTES]; + b[..8].copy_from_slice(b"CGCAND01"); + if let Some(leaf) = self.leaf { + b[8..520].copy_from_slice(&leaf.encode()); + } + b[520..].copy_from_slice(&self.cursor.encode()); + b + } +} +/// Return an idle shared cursor only after strict installation-bound decoding. +/// An active leaf or another role is not available for role takeover. +pub(crate) fn idle_control_cursor( + payload: &[u8], + scope: super::maintenance_admission::RootOriginBinding, +) -> Result, CandidateError> { + if payload != b"CGBUILD1" + && payload.len() != super::maintenance::CURSOR_BYTES + && !payload.starts_with(b"CGCAND01") + { + return Ok(None); + } + let control = Control::decode(payload, scope)?; + Ok(if control.leaf.is_none() { + Some(control.cursor) + } else { + None + }) +} + +/// Bulk owners remain off the mutation stack. The hot callback borrows these +/// charged frames; dropping it releases only a RefCell borrow, never the work. +pub(crate) struct PreparedCandidate { + _binding: MemoryBox, + work: RefCell>, + report: CandidateReport, + _floor: MemoryReservation, +} +struct PreparedAppend { + offset: u64, + frame: [u8; 512], +} +enum Work { + Scheduled { + metadata: MemoryBox, + update: MemoryBox, + inner: MemoryBox, + }, + Legacy(MemoryBox), + Generation(MemoryBox), + Backup(MemoryBox), + Terminal(MemoryBox), + WorkspaceRoot { + terminal: Option>, + removal: MemoryBox, + }, + Metadata { + prepared: MemoryBox, + update: MemoryBox, + append: Option, + reset: Option, + }, + Leaf { + ready: MemoryBox, + leaf: super::maintenance_journal::RetirementLeaf, + stamp: FixedFileStamp, + stream_stamp: FixedFileStamp, + held_file: Option, + payload_nonce: Option<[u8; 32]>, + post_rename: Option, + action: LeafAction, + }, +} +#[derive(Clone, Copy)] +enum LeafAction { + Detach, + UnlinkPayload, + UnlinkReceipt, +} +pub(crate) struct CandidateHotFrame<'a> { + prepared: &'a PreparedCandidate, +} +impl PreparedCandidate { + pub(crate) fn borrow_hot_frame(&self) -> CandidateHotFrame<'_> { + CandidateHotFrame { prepared: self } + } + /// The scheduler must retain a pending one-shot confirmation through + /// unrelated modern housekeeping. Legacy enrollment variants opt in here. + pub(crate) fn requires_legacy_confirmation(&self) -> bool { + let work = self.work.borrow(); + let selected = match &**work { + Work::Scheduled { inner, .. } => &**inner, + other => other, + }; + match selected { + Work::Legacy(work) => work.requires_legacy_confirmation(), + Work::Generation(work) => work.requires_legacy_confirmation(), + _ => false, + } + } +} +fn lower_hex(bytes: &[u8]) -> bool { + bytes + .iter() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(b)) +} +fn decode_key(name: &[u8; 64]) -> [u8; 32] { + let mut key = [0; 32]; + let digit = |b: u8| if b <= b'9' { b - b'0' } else { b - b'a' + 10 }; + for (i, pair) in name.chunks_exact(2).enumerate() { + key[i] = digit(pair[0]) * 16 + digit(pair[1]); + } + key +} +fn encode_key(key: &[u8; 32]) -> [u8; 64] { + let mut name = [0; 64]; + let digits = b"0123456789abcdef"; + for (i, b) in key.iter().enumerate() { + name[i * 2] = digits[(b >> 4) as usize]; + name[i * 2 + 1] = digits[(b & 15) as usize]; + } + name +} +fn deferred(report: &mut CandidateReport, bit: u8) { + report.pending = true; + report.deferred = true; + report.reasons |= 1u64 << bit; +} +fn reference(error: super::maintenance_references::ReferenceIssue) -> CandidateError { + use super::maintenance_references::ReferenceIssue as R; + match error { + R::ResourceLimit(_) => CandidateError::ResourceLimit, + R::Io(k) => CandidateError::Io(k), + R::Stale => CandidateError::Stale, + R::Cancelled => CandidateError::Cancelled, + R::Unknown => CandidateError::InvalidEvidence, + } +} +fn metadata_work( + prepared: MemoryBox, + control: &Control, + memory: &CleanupMemory, + append: Option<[u8; 512]>, +) -> Result { + let before = prepared + .committed() + .ok_or(CandidateError::InvalidEvidence)?; + let update = super::maintenance_admission::prepare_metadata_control( + &prepared.origin_scope(), + before, + &control.encode(), + memory, + ) + .map_err(admission)?; + let update = MemoryBox::new(update, memory).map_err(|_| CandidateError::ResourceLimit)?; + Ok(Work::Metadata { + prepared, + update, + append: append.map(|frame| PreparedAppend { + offset: u64::from_le_bytes(frame[32..40].try_into().unwrap()), + frame, + }), + reset: None, + }) +} +fn next_stage( + leaf: &mut super::maintenance_journal::RetirementLeaf, + stage: super::maintenance_journal::RetirementStage, +) -> Result<(), CandidateError> { + leaf.stage_generation = leaf + .stage_generation + .checked_add(1) + .ok_or(CandidateError::InvalidEvidence)?; + leaf.stage = stage; + Ok(()) +} +fn absent(error: &std::io::Error) -> bool { + error.kind() == std::io::ErrorKind::NotFound +} +struct OriginValidation<'a> { + incomplete: bool, + detached: bool, + cancel: &'a dyn Fn() -> bool, +} +fn validate_origin( + root: &RootedDirectory, + directory: &RootedDirectory, + payload: &std::fs::File, + key: [u8; 32], + memory: &CleanupMemory, + mode: OriginValidation<'_>, +) -> Result, CandidateError> { + let (digest, receipt_stamp) = match receipt_digest(directory) { + Ok(v) => v, + Err(CandidateError::Io(std::io::ErrorKind::NotFound)) + | Err(CandidateError::InvalidEvidence) => return Ok(None), + Err(error) => return Err(error), + }; + let observation = if mode.detached { + maintenance_origin::observe_detached_artifact_origin( + root, + directory, + payload, + key, + memory, + mode.cancel, + ) + } else { + maintenance_origin::observe_artifact_origin_checked( + root, + directory, + payload, + key, + memory, + mode.cancel, + ) + } + .map_err(|error| { + if (mode.cancel)() { + CandidateError::Cancelled + } else { + evidence(error) + } + })?; + let receipt = directory + .open_readonly_file(maintenance_origin::ORIGIN_NAME) + .map_err(io)?; + receipt_stamp.recheck(&receipt).map_err(admission)?; + match observation { + OriginObservation::Observed(origin) + if matches!(origin.phase, OriginPhase::Complete | OriginPhase::Published) => + { + Ok(Some((origin, digest))) + } + OriginObservation::Observed(mut origin) if mode.incomplete && origin.payload_created => { + let (len, hash) = hash_payload(payload, mode.cancel)?; + receipt_stamp.recheck(&receipt).map_err(admission)?; + origin.byte_len = len; + origin.payload_sha256 = hash; + Ok(Some((origin, digest))) + } + _ => Ok(None), + } +} +fn hash_payload( + file: &std::fs::File, + cancel: &dyn Fn() -> bool, +) -> Result<(u64, [u8; 32]), CandidateError> { + let stamp = FixedFileStamp::capture(file, FixedControlKind::File).map_err(admission)?; + let mut hash = Sha256::new(); + let mut offset = 0u64; + let mut buffer = [0; 4096]; + while offset < stamp.len() { + if cancel() { + return Err(CandidateError::Cancelled); + } + let count = ((stamp.len() - offset).min(buffer.len() as u64)) as usize; + locks::read_fixed_range(file, offset, &mut buffer[..count]).map_err(io)?; + hash.update(&buffer[..count]); + offset += count as u64; + } + stamp.recheck(file).map_err(admission)?; + Ok((offset, hash.finalize().into())) +} +fn temp_address(name: &[u8]) -> Option { + if name.len() != 134 + || !name.starts_with(b".tmp-") + || name[69] != b'-' + || !lower_hex(&name[5..69]) + || !lower_hex(&name[70..]) + { + return None; + } + let mut address = [0; 64]; + address[..32].copy_from_slice(&decode_key(name[5..69].try_into().ok()?)); + address[32..].copy_from_slice(&decode_key(name[70..].try_into().ok()?)); + Some(super::maintenance::CursorAddress::from_short(address)) +} +fn temp_name(address: super::maintenance::CursorAddress) -> Result<[u8; 134], CandidateError> { + let address = address.as_short().ok_or(CandidateError::InvalidEvidence)?; + let mut name = [0; 134]; + name[..5].copy_from_slice(b".tmp-"); + name[5..69].copy_from_slice(&encode_key(address[..32].try_into().unwrap())); + name[69] = b'-'; + name[70..].copy_from_slice(&encode_key(address[32..].try_into().unwrap())); + Ok(name) +} +fn source_directory( + root: &RootedDirectory, + leaf: &super::maintenance_journal::RetirementLeaf, +) -> Result { + if leaf.source_kind == 1 { + if temp_address(&leaf.source_name).is_none_or(|address| address.0[..32] != leaf.key) { + return Err(CandidateError::InvalidEvidence); + } + root.open_directory_readonly(&leaf.source_name).map_err(io) + } else { + let name = encode_key(&leaf.key); + root.open_directory_readonly(&name[..2]) + .and_then(|prefix| prefix.open_directory_readonly(&name)) + .map_err(io) + } +} + +fn stream_frame(leaf: &super::maintenance_journal::RetirementLeaf) -> [u8; 512] { + let mut initial = *leaf; + initial.stage = super::maintenance_journal::RetirementStage::AppendIntent; + initial.stage_generation = 0; + initial.encode() +} +fn verify_stream( + root: &RootedDirectory, + leaf: &super::maintenance_journal::RetirementLeaf, +) -> Result { + let file = root + .open_readonly_file(b".publication.stream") + .map_err(io)?; + let stamp = FixedFileStamp::capture(&file, FixedControlKind::File).map_err(admission)?; + let mut frame = [0; 512]; + locks::read_fixed_range(&file, leaf.stream_offset, &mut frame).map_err(io)?; + if frame != stream_frame(leaf) { + return Err(CandidateError::InvalidEvidence); + } + stamp.recheck(&file).map_err(admission)?; + Ok(stamp) +} + +fn receipt_digest( + directory: &RootedDirectory, +) -> Result<([u8; 32], FixedFileStamp), CandidateError> { + let mut file = directory + .open_readonly_file(maintenance_origin::ORIGIN_NAME) + .map_err(io)?; + let stamp = FixedFileStamp::capture(&file, FixedControlKind::File).map_err(admission)?; + if stamp.len() > 2048 { + return Err(CandidateError::InvalidEvidence); + } + let mut hash = Sha256::new(); + let mut buffer = [0; 512]; + loop { + let n = file.read(&mut buffer).map_err(io)?; + if n == 0 { + break; + } + hash.update(&buffer[..n]); + } + stamp.recheck(&file).map_err(admission)?; + Ok((hash.finalize().into(), stamp)) +} + +/// Persistent offslot preparation is owned by the joined controller, never by +/// the dispatcher or a global/thread-local registry. Durable checkpoints remain +/// the restart authority; this is only a bounded warm preparation cache. +#[derive(Default)] +pub(crate) struct CandidatePreparationState { + binding: Option<[u8; 32]>, + legacy_interest: bool, + legacy_metadata: Option<( + u64, + Option, + )>, + legacy_jobs: super::maintenance_legacy_engine::LegacyPreparationState, + backup: super::maintenance_backup::BackupPreparationState, + generations: super::maintenance_generation_engine::GenerationPreparationState, + legacy: Option>, + association: Option>, +} +impl CandidatePreparationState { + pub(crate) fn has_parked_payloads(&self) -> bool { + self.legacy.is_some() + || self.association.is_some() + || self.legacy_jobs.has_parked_payloads() + || self.backup.has_parked_payloads() + || self.generations.has_parked_payloads() + } + pub(crate) fn set_legacy_confirmation_metadata( + &mut self, + metadata: Option<( + u64, + Option, + )>, + ) { + self.legacy_metadata = metadata; + } + pub(crate) fn set_legacy_interest(&mut self, interest: bool) { + self.legacy_interest = interest; + self.legacy_jobs.set_legacy_interest(interest); + } + pub(crate) fn clear(&mut self) { + self.backup.clear(); + self.generations.clear(); + self.legacy_jobs.clear(); + drop(self.legacy.take()); + drop(self.association.take()); + self.binding = None; + } + fn bind(&mut self, digest: [u8; 32]) { + if self.binding != Some(digest) { + self.clear(); + self.binding = Some(digest); + } + } +} + +#[inline(never)] +fn duplicate_target_binding_boxed( + binding: &PreparedTargetBinding, + memory: &CleanupMemory, +) -> Result, CandidateError> { + let mut retained = binding.duplicate(memory).map_err(admission)?; + if !retained.is_current().map_err(admission)? { + return Err(CandidateError::Stale); + } + MemoryBox::new(retained, memory).map_err(|_| CandidateError::ResourceLimit) +} +pub(crate) fn prepare_candidate_with_state( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + state: &mut CandidatePreparationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + report: &mut CandidateReport, +) -> Result, CandidateError> { + state.bind(binding.digest()); + state + .legacy_jobs + .set_request_metadata(state.legacy_metadata); + *report = CandidateReport::default(); + if cancel() { + state.clear(); + return Err(CandidateError::Cancelled); + } + let floor = memory + .reserve( + super::maintenance_projection::path_charge( + [ + Some(target.repository_root.as_path()), + Some(target.state_root.as_path()), + target.storage_root.as_deref(), + target.config_path.as_deref(), + target.direct_pair.as_ref().map(|p| p.database.as_path()), + target.direct_pair.as_ref().map(|p| p.manifest.as_path()), + ] + .into_iter() + .flatten(), + 24, + std::mem::size_of::() + 3 * 4096 + CONTROL_BYTES, + ) + .map_err(|_| CandidateError::ResourceLimit)?, + ) + .map_err(|_| CandidateError::ResourceLimit)?; + let retained = duplicate_target_binding_boxed(binding, memory)?; + if let Some(legacy) = super::maintenance_legacy_engine::prepare_bootstrap_work( + target, + binding, + &mut state.legacy_jobs, + memory, + cancel, + state.legacy_interest, + ) + .map_err(admission)? + { + return Ok(Some(PreparedCandidate { + _binding: retained, + work: RefCell::new( + MemoryBox::new(Work::Legacy(legacy), memory) + .map_err(|_| CandidateError::ResourceLimit)?, + ), + report: *report, + _floor: floor, + })); + } + if state.legacy_jobs.needs_operator() { + deferred(report, 3); + } + let domain = if let Some(pair) = &target.direct_pair { + ReferenceDomain::direct(&DirectLayout::new(&pair.database, &pair.manifest)) + } else if let Some(storage) = &target.storage_root { + ReferenceDomain::managed(&ManagedLayout::new(storage)) + } else { + report.unknown = true; + report.reasons |= 1 << 9; + return Ok(None); + } + .map_err(evidence)?; + // A pending one-shot claim pins the exact old DB/manifest pair. Until + // its real enrollment consumes it, no other payload proof may open FDs. + if state.legacy_interest { + // The bound root bootstrap above is the sole initial consumer. Retain + // the request while no actual role can claim it safely. + deferred(report, 3); + return Ok(None); + } + let expected = match ownership::observe_reference_epoch(&domain).map_err(evidence)? { + EpochObservation::Verified(epoch) => epoch, + EpochObservation::Pending(_) => { + deferred(report, if binding.is_legacy() { 3 } else { 2 }); + return Ok(None); + } + }; + let Some(work) = prepare_role_work( + target, binding, &domain, &expected, state, memory, cancel, report, + )? + else { + return Ok(None); + }; + Ok(Some(PreparedCandidate { + _binding: retained, + work: RefCell::new( + MemoryBox::new(work, memory).map_err(|_| CandidateError::ResourceLimit)?, + ), + report: *report, + _floor: floor, + })) +} +#[inline(never)] +fn boxed_canonical( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + memory: &CleanupMemory, +) -> Result, CandidateError> { + let value = super::maintenance_admission::prepare_canonical(target, domain, expected, memory) + .map_err(admission)?; + MemoryBox::new(value, memory).map_err(|_| CandidateError::ResourceLimit) +} +#[inline(never)] +fn boxed_metadata( + target: &MaintenanceTarget, + scope: &super::maintenance_admission::RootOriginBinding, + memory: &CleanupMemory, +) -> Result, CandidateError> { + let value = super::maintenance_admission::prepare_private_metadata(target, scope, memory) + .map_err(admission)?; + MemoryBox::new(value, memory).map_err(|_| CandidateError::ResourceLimit) +} +#[inline(never)] +fn candidate_origin_scope( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + memory: &CleanupMemory, +) -> Result { + Ok( + super::maintenance_admission::prepare_canonical(target, domain, expected, memory) + .map_err(admission)? + .origin_scope(), + ) +} +#[inline(never)] +// Keep independent borrowed authorities and caller-owned scratch explicit. +#[allow(clippy::too_many_arguments)] +fn prepare_role_work( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + state: &mut CandidatePreparationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + report: &mut CandidateReport, +) -> Result, CandidateError> { + use super::maintenance::WorkCategory as C; + let scope = match candidate_origin_scope(target, domain, expected, memory) { + Ok(scope) => scope, + Err(_) => { + return prepare_artifact_candidate( + target, domain, expected, None, memory, cancel, report, + ) + } + }; + let metadata = match boxed_metadata(target, &scope, memory) { + Ok(metadata) => metadata, + Err(_) => { + return prepare_artifact_candidate( + target, domain, expected, None, memory, cancel, report, + ) + } + }; + // A role must not interpret its old payload while an authenticated slot + // transaction still needs completion. This only replays the exact owned + // intent through the existing metadata facade; it grants no payload effect. + if let Some(update) = super::maintenance_admission::prepare_metadata_recovery(&metadata, memory) + .map_err(admission)? + { + deferred(report, 7); + return Ok(Some(Work::Metadata { + prepared: metadata, + update: MemoryBox::new(update, memory).map_err(|_| CandidateError::ResourceLimit)?, + append: None, + reset: None, + })); + } + let Some(payload) = metadata.payload() else { + return prepare_artifact_candidate(target, domain, expected, None, memory, cancel, report); + }; + if super::maintenance_backup::is_backup_payload(payload) { + return super::maintenance_backup::prepare_work( + target, + binding, + domain, + expected, + &mut state.backup, + memory, + cancel, + ) + .map(|x| x.map(Work::Backup)) + .map_err(admission); + } + if super::maintenance_legacy_engine::is_legacy_payload(payload) { + return super::maintenance_legacy_engine::prepare_work( + target, + binding, + domain, + expected, + &mut state.legacy_jobs, + memory, + cancel, + ) + .map(|x| x.map(Work::Legacy)) + .map_err(admission); + } + if super::maintenance_generation_engine::is_generation_payload(payload) { + return super::maintenance_generation_engine::prepare_work( + target, + binding, + domain, + expected, + &mut state.generations, + memory, + cancel, + ) + .map(|x| x.map(Work::Generation)) + .map_err(admission); + } + let Some(mut cursor) = idle_control_cursor(payload, scope)? else { + return prepare_artifact_candidate(target, domain, expected, None, memory, cancel, report); + }; + for _ in 0..8 { + let category = cursor.next_category(); + let selected = match category { + C::Canonical => prepare_artifact_candidate( + target, + domain, + expected, + Some(category), + memory, + cancel, + report, + )?, + C::TemporaryOrBackup => { + if let Some(backup) = super::maintenance_backup::prepare_work( + target, + binding, + domain, + expected, + &mut state.backup, + memory, + cancel, + ) + .map_err(admission)? + { + Some(Work::Backup(backup)) + } else { + prepare_artifact_candidate( + target, + domain, + expected, + Some(category), + memory, + cancel, + report, + )? + } + } + C::Run | C::Phase => { + let selected = prepare_terminal_candidate( + target, + domain, + memory, + cancel, + report, + category, + cursor.after(category), + )?; + report.checked_categories |= 1 << (category as u8); + selected + } + C::Generation => { + let mut selected = super::maintenance_generation_engine::prepare_work( + target, + binding, + domain, + expected, + &mut state.generations, + memory, + cancel, + ) + .map(|x| x.map(Work::Generation)) + .map_err(admission)?; + if selected.is_none() { + selected = super::maintenance_legacy_engine::prepare_native_work( + target, binding, domain, expected, &mut state.legacy_jobs, + super::maintenance_legacy_generations_engine::LegacyNativeScope::ManagedGeneration, + memory, cancel, + ).map(|x| x.map(Work::Legacy)).map_err(admission)?; + } + if let Some(observed) = state.generations.take_observation() { + report.checked_categories |= observed.checked_categories; + report.inspected_entries = report + .inspected_entries + .saturating_add(observed.inspected_entries); + report.unknown |= observed.unknown; + report.deferred |= observed.deferred; + report.reasons |= observed.reasons; + } + selected + } + C::LegacyNative | C::LegacySnapshot | C::DefaultDirect => { + super::maintenance_legacy_engine::prepare_work( + target, + binding, + domain, + expected, + &mut state.legacy_jobs, + memory, + cancel, + ) + .map(|x| x.map(Work::Legacy)) + .map_err(admission)? + } + }; + if let Some(work) = selected { + if matches!(category, C::Run | C::Phase) { + if let Some(address) = terminal_work_address(&work, domain, target) { + cursor.inspected(category, address); + } else { + cursor.advance_empty(category); + } + report.pending = true; + return scheduled_terminal_work(metadata, &scope, &cursor, work, memory).map(Some); + } + return Ok(Some(work)); + } + cursor.advance_empty(category); + } + report.observations_complete = + !report.deferred && !report.unknown && report.checked_categories == u8::MAX; + Ok(None) +} + +#[inline(never)] +fn scheduled_terminal_work( + metadata: MemoryBox, + scope: &super::maintenance_admission::RootOriginBinding, + cursor: &super::maintenance::MaintenanceCursor, + work: Work, + memory: &CleanupMemory, +) -> Result { + let update = super::maintenance_admission::prepare_metadata_control( + scope, + metadata + .committed() + .ok_or(CandidateError::InvalidEvidence)?, + &cursor.encode(), + memory, + ) + .map_err(admission)?; + Ok(Work::Scheduled { + metadata, + update: MemoryBox::new(update, memory).map_err(|_| CandidateError::ResourceLimit)?, + inner: MemoryBox::new(work, memory).map_err(|_| CandidateError::ResourceLimit)?, + }) +} + +#[inline(never)] +fn prepare_artifact_candidate( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + category: Option, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + report: &mut CandidateReport, +) -> Result, CandidateError> { + let ready = match boxed_canonical(target, domain, expected, memory) { + Ok(prepared) => prepared, + Err(_) => { + deferred(report, 2); + return Ok(None); + } + }; + let scope = ready.origin_scope(); + #[cfg(all(test, debug_assertions))] + if fault::paused(scope.root) { + deferred(report, 7); + return Ok(None); + } + let metadata = match boxed_metadata(target, &scope, memory) { + Ok(p) => p, + Err(_) => { + deferred(report, 7); + return Ok(None); + } + }; + if let Some(update) = super::maintenance_admission::prepare_metadata_recovery(&metadata, memory) + .map_err(admission)? + { + deferred(report, 7); + return Ok(Some(Work::Metadata { + prepared: metadata, + update: MemoryBox::new(update, memory).map_err(|_| CandidateError::ResourceLimit)?, + append: None, + reset: None, + })); + } + prepare_artifact_work( + target, domain, expected, category, memory, cancel, report, ready, metadata, + ) +} + +// Keep admission's fixed frames outside payload traversal and replay frames. +#[inline(never)] +// Keep independent borrowed authorities and caller-owned scratch explicit. +#[allow(clippy::too_many_arguments)] +fn prepare_artifact_work( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + category: Option, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + report: &mut CandidateReport, + ready: MemoryBox, + metadata: MemoryBox, +) -> Result, CandidateError> { + let scope = ready.origin_scope(); + let control = Control::decode( + metadata.payload().ok_or(CandidateError::InvalidEvidence)?, + scope, + )?; + let root = + RootedDirectory::open_readonly_root(domain.artifact_root(), scope.root).map_err(io)?; + #[cfg(all(test, debug_assertions))] + fault::preparation_pause(scope.root, cancel)?; + if let Some(leaf) = control.leaf { + prepare_artifact_resume( + target, domain, expected, memory, cancel, report, ready, metadata, root, control, leaf, + ) + } else { + drop(ready); + prepare_artifact_select( + target, domain, expected, category, memory, cancel, report, metadata, root, control, + ) + } +} + +#[inline(never)] +// Keep independent borrowed authorities and caller-owned scratch explicit. +#[allow(clippy::too_many_arguments)] +fn prepare_artifact_resume( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + report: &mut CandidateReport, + ready: MemoryBox, + metadata: MemoryBox, + root: RootedDirectory, + mut control: Control, + mut leaf: super::maintenance_journal::RetirementLeaf, +) -> Result, CandidateError> { + let scope = ready.origin_scope(); + let work = { + use super::maintenance_journal::RetirementStage as S; + report.pending = true; + report.reasons |= 1 << 7; + match leaf.stage { + S::AppendIntent => { + let frame = leaf.encode(); + next_stage(&mut leaf, S::FrameReady)?; + control.leaf = Some(leaf); + metadata_work(metadata, &control, memory, Some(frame))? + } + S::FrameReady | S::Detached | S::PayloadGone => { + let stream_stamp = verify_stream(&root, &leaf)?; + let directory = match source_directory(&root, &leaf) { + Ok(directory) => directory, + Err(CandidateError::Io(std::io::ErrorKind::NotFound)) + if matches!(leaf.stage, S::PayloadGone) => + { + match root.open_readonly_file(&leaf.tombstone()) { + Err(error) if absent(&error) => {} + Err(error) => return Err(io(error)), + Ok(_) => return Err(CandidateError::Stale), + } + next_stage(&mut leaf, S::Done)?; + control.leaf = Some(leaf); + let work = metadata_work(metadata, &control, memory, None)?; + return Ok(Some(work)); + } + Err(error) => return Err(error), + }; + if directory.identity() != leaf.directory { + return Err(CandidateError::Stale); + } + let private = root.open_readonly_file(&leaf.tombstone()); + let public = directory.open_readonly_file(maintenance_origin::PAYLOAD_NAME); + match leaf.stage { + S::FrameReady => match private { + Ok(payload) => { + if locks::file_identity(&payload).map_err(evidence)? != leaf.payload { + return Err(CandidateError::Stale); + } + if matches!(public,Ok(ref p)if locks::file_identity(p).ok()==Some(leaf.payload)) + { + return Err(CandidateError::InvalidEvidence); + } + let (origin, receipt_hash) = validate_origin( + &root, + &directory, + &payload, + leaf.key, + memory, + OriginValidation { + incomplete: leaf.source_kind == 1, + detached: true, + cancel, + }, + )? + .ok_or(CandidateError::InvalidEvidence)?; + if receipt_hash != leaf.receipt_digest { + return Err(CandidateError::Stale); + } + if origin.payload_sha256 != leaf.digest + || origin.byte_len != leaf.byte_len + { + return Err(CandidateError::Stale); + } + next_stage(&mut leaf, S::Detached)?; + control.leaf = Some(leaf); + metadata_work(metadata, &control, memory, None)? + } + Err(error) if absent(&error) => { + if expected.counter() != leaf.source_epoch { + // Own epoch advancement without a rename cannot + // become Detached. Finish only the inert log and + // reset it before selecting a fresh source epoch. + leaf.stage = S::Done; + leaf.stage_generation = 4; + control.leaf = Some(leaf); + metadata_work(metadata, &control, memory, None)? + } else { + let payload = public.map_err(io)?; + let (origin, receipt_hash) = validate_origin( + &root, + &directory, + &payload, + leaf.key, + memory, + OriginValidation { + incomplete: leaf.source_kind == 1, + detached: false, + cancel, + }, + )? + .ok_or(CandidateError::InvalidEvidence)?; + if receipt_hash != leaf.receipt_digest { + return Err(CandidateError::Stale); + } + if origin.payload != leaf.payload + || origin.payload_sha256 != leaf.digest + || origin.byte_len != leaf.byte_len + { + return Err(CandidateError::Stale); + } + let refs = + super::maintenance_references::prepare_artifact_references( + target, domain, expected, leaf.key, memory, cancel, + ) + .map_err(reference)?; + if !refs.complete || refs.referenced { + deferred(report, 1); + return Ok(None); + } + let stamp = + FixedFileStamp::capture(&payload, FixedControlKind::File) + .map_err(admission)?; + Work::Leaf { + ready, + leaf, + stamp, + stream_stamp, + held_file: Some(payload), + payload_nonce: Some(origin.payload_nonce), + post_rename: None, + action: LeafAction::Detach, + } + } + } + Err(error) => return Err(io(error)), + }, + S::Detached => match private { + Ok(payload) => { + let (origin, receipt_hash) = validate_origin( + &root, + &directory, + &payload, + leaf.key, + memory, + OriginValidation { + incomplete: leaf.source_kind == 1, + detached: true, + cancel, + }, + )? + .ok_or(CandidateError::InvalidEvidence)?; + if receipt_hash != leaf.receipt_digest { + return Err(CandidateError::Stale); + } + if origin.payload != leaf.payload + || origin.payload_sha256 != leaf.digest + || origin.byte_len != leaf.byte_len + { + return Err(CandidateError::Stale); + } + let refs = super::maintenance_references::prepare_artifact_references( + target, domain, expected, leaf.key, memory, cancel, + ) + .map_err(reference)?; + if !refs.complete || refs.referenced { + deferred(report, 1); + return Ok(None); + } + let stamp = FixedFileStamp::capture(&payload, FixedControlKind::File) + .map_err(admission)?; + Work::Leaf { + ready, + leaf, + stamp, + stream_stamp, + held_file: Some(payload), + payload_nonce: Some(origin.payload_nonce), + post_rename: None, + action: LeafAction::UnlinkPayload, + } + } + Err(error) if absent(&error) => { + next_stage(&mut leaf, S::PayloadGone)?; + control.leaf = Some(leaf); + metadata_work(metadata, &control, memory, None)? + } + Err(error) => return Err(io(error)), + }, + S::PayloadGone => { + // A new public incarnation is never addressed by an old journal. + match (public, private) { + (Err(a), Err(b)) if absent(&a) && absent(&b) => {} + (Err(error), _) if !absent(&error) => return Err(io(error)), + (_, Err(error)) if !absent(&error) => return Err(io(error)), + _ => { + deferred(report, 5); + return Ok(None); + } + } + match directory.open_readonly_file(maintenance_origin::ORIGIN_NAME) { + Ok(receipt) => { + if locks::file_identity(&receipt).map_err(evidence)? != leaf.receipt + { + return Err(CandidateError::Stale); + } + let (digest, stamp) = receipt_digest(&directory)?; + if digest != leaf.receipt_digest { + return Err(CandidateError::Stale); + } + Work::Leaf { + ready, + leaf, + stamp, + stream_stamp, + held_file: Some(receipt), + payload_nonce: None, + post_rename: None, + action: LeafAction::UnlinkReceipt, + } + } + Err(error) if absent(&error) => { + next_stage(&mut leaf, S::Done)?; + control.leaf = Some(leaf); + metadata_work(metadata, &control, memory, None)? + } + Err(error) => return Err(io(error)), + } + } + _ => unreachable!(), + } + } + S::Done => { + next_stage(&mut leaf, S::ResetIntent)?; + control.leaf = Some(leaf); + metadata_work(metadata, &control, memory, None)? + } + S::ResetIntent => { + let len = metadata.stream_len(); + let end = leaf + .stream_offset + .checked_add(512) + .ok_or(CandidateError::InvalidEvidence)?; + if len != 0 && len != end { + return Err(CandidateError::InvalidEvidence); + } + if len != 0 { + let stream = root + .open_readonly_file(b".publication.stream") + .map_err(io)?; + let stamp = FixedFileStamp::capture(&stream, FixedControlKind::File) + .map_err(admission)?; + let mut offset = 0; + let mut frame = [0; 512]; + while offset < len { + if cancel() { + return Err(CandidateError::Cancelled); + } + locks::read_fixed_range(&stream, offset, &mut frame).map_err(io)?; + let prior = super::maintenance_journal::RetirementLeaf::decode(&frame) + .map_err(|_| CandidateError::InvalidEvidence)?; + if prior.scope != scope + || prior.stream_offset != offset + || prior.stage != S::AppendIntent + || prior.stage_generation != 0 + { + return Err(CandidateError::InvalidEvidence); + } + offset = offset + .checked_add(512) + .ok_or(CandidateError::InvalidEvidence)?; + } + stamp.recheck(&stream).map_err(admission)?; + if frame != stream_frame(&leaf) { + return Err(CandidateError::InvalidEvidence); + } + } + control.leaf = None; + let mut work = metadata_work(metadata, &control, memory, None)?; + if let Work::Metadata { reset, .. } = &mut work { + if len != 0 { + *reset = Some(len); + } + } + work + } + } + }; + if cancel() { + return Err(CandidateError::Cancelled); + } + Ok(Some(work)) +} + +#[inline(never)] +// Keep independent borrowed authorities and caller-owned scratch explicit. +#[allow(clippy::too_many_arguments)] +fn prepare_artifact_select( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + category: Option, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + report: &mut CandidateReport, + metadata: MemoryBox, + root: RootedDirectory, + mut control: Control, +) -> Result, CandidateError> { + let scope = metadata.origin_scope(); + let mut scratch = [0; 4096]; + let mut child_scratch = [0; 4096]; + let work = { + let mut selection = control.cursor.selection(); + root.visit_entries(&mut scratch, &mut |prefix| { + if cancel() { + return Err(std::io::ErrorKind::Interrupted.into()); + } + if let Some(address) = temp_address(prefix) { + if category.is_none_or(|c| c == super::maintenance::WorkCategory::TemporaryOrBackup) + { + selection.observe(super::maintenance::WorkCategory::TemporaryOrBackup, address); + } + report.inspected_entries = report.inspected_entries.saturating_add(1); + return Ok(()); + } + if prefix.len() != 2 || !lower_hex(prefix) { + return Ok(()); + } + let directory = match root.open_directory_readonly(prefix) { + Ok(d) => d, + Err(_) => { + report.unknown = true; + report.reasons |= 1; + return Ok(()); + } + }; + directory.visit_entries(&mut child_scratch, &mut |name| { + if cancel() { + return Err(std::io::ErrorKind::Interrupted.into()); + } + report.inspected_entries = report.inspected_entries.saturating_add(1); + if name.len() == 64 && lower_hex(name) && &name[..2] == prefix { + if category.is_none_or(|c| c == super::maintenance::WorkCategory::Canonical) { + selection.observe( + super::maintenance::WorkCategory::Canonical, + super::maintenance::CursorAddress::from_short(name.try_into().unwrap()), + ); + } + } else { + report.unknown = true; + report.reasons |= 1; + } + Ok(()) + }) + }) + .map_err(|e| { + if cancel() { + CandidateError::Cancelled + } else { + io(e) + } + })?; + report.checked_categories |= 3; + let Some((category, address)) = selection.selected() else { + return Ok(None); + }; + let previous = control.cursor.after(category); + let wrapped = previous.is_some_and(|previous| address <= previous); + control.cursor.inspected(category, address); + report.pending = !wrapped; + let source_kind = if category == super::maintenance::WorkCategory::Canonical { + 0 + } else { + 1 + }; + let source_name = if source_kind == 1 { + temp_name(address)? + } else { + [0; 134] + }; + let name = if source_kind == 0 { + address.as_short().ok_or(CandidateError::InvalidEvidence)? + } else { + encode_key(address.0[..32].try_into().unwrap()) + }; + let key = decode_key(&name); + let directory = if source_kind == 0 { + root.open_directory_readonly(&name[..2]) + .and_then(|prefix| prefix.open_directory_readonly(&name)) + } else { + root.open_directory_readonly(&source_name) + } + .map_err(io)?; + let payload = directory.open_readonly_file(maintenance_origin::PAYLOAD_NAME); + let origin = match payload { + Ok(ref payload) => validate_origin( + &root, + &directory, + payload, + key, + memory, + OriginValidation { + incomplete: source_kind == 1, + detached: false, + cancel, + }, + )?, + Err(_) => None, + }; + if let Some((origin, receipt_hash)) = origin { + if origin.scope.root != scope.root + || origin.scope.coordination != scope.coordination + || origin.scope.cohort != scope.cohort + || origin.scope.publication_binding != scope.publication_binding + || origin.scope.incarnation != scope.incarnation + { + return Err(CandidateError::Stale); + } + let refs = super::maintenance_references::prepare_artifact_references( + target, domain, expected, key, memory, cancel, + ) + .map_err(reference)?; + if !refs.complete { + deferred(report, 1); + } else if refs.referenced { + report.reasons |= 1 << 1; + } else { + let mut operation = [0; 32]; + getrandom::fill(&mut operation).map_err(|_| CandidateError::InvalidEvidence)?; + control.leaf = Some(super::maintenance_journal::RetirementLeaf { + stage: super::maintenance_journal::RetirementStage::AppendIntent, + source_epoch: expected.counter(), + stage_generation: 0, + stream_offset: metadata.stream_len(), + key, + directory: origin.directory, + payload: origin.payload, + receipt: origin.receipt, + scope, + operation, + byte_len: origin.byte_len, + digest: origin.payload_sha256, + directory_created: origin.directory_created, + receipt_digest: receipt_hash, + source_kind, + source_name, + }); + report.pending = true; + report.reasons |= 1 << 7; + } + } else { + report.unknown = true; + report.reasons |= 1; + } + metadata_work(metadata, &control, memory, None)? + }; + if cancel() { + return Err(CandidateError::Cancelled); + } + Ok(Some(work)) +} + +pub(crate) fn advance_candidate( + hot: CandidateHotFrame<'_>, + grant: &mut MutationGrant, + confirmation: Option<&super::maintenance_scheduler::LegacyQuiescenceConfirmation<'_>>, +) -> Result { + let prepared = hot.prepared; + let mut report = prepared.report; + if grant.check_time().is_err() { + deferred(&mut report, 2); + return Ok(CandidateAdvance { + status: CandidateStatus::Deferred, + report, + }); + } + let mut needs_disposal = false; + let mut work = prepared + .work + .try_borrow_mut() + .map_err(|_| CandidateError::InvalidEvidence)?; + if let Work::Scheduled { + metadata, update, .. + } = &mut **work + { + let result = super::maintenance_admission::with_private_metadata( + metadata, + grant, + update.effects(), + &mut |mutation, grant| mutation.publish_control(update, grant), + ); + if let Err(error) = result { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("cleanup scheduling mutation rejected: {error:?}"); + } + match error { + locks::FixedControlError::Resource(_) => return Err(CandidateError::ResourceLimit), + locks::FixedControlError::Io(_) => deferred(&mut report, 12), + locks::FixedControlError::Grant(_) + | locks::FixedControlError::Invalid(locks::FixedControlProblem::Busy) => { + deferred(&mut report, 2) + } + _ => deferred(&mut report, 5), + } + return Ok(CandidateAdvance { + status: CandidateStatus::Deferred, + report, + }); + } + } + let selected = match &mut **work { + Work::Scheduled { inner, .. } => &mut **inner, + other => other, + }; + #[cfg(test)] + let operation = match &*selected { + Work::Scheduled { .. } => "nested schedule", + Work::Legacy(_) => "legacy", + Work::Generation(_) => "generation", + Work::Backup(_) => "backup", + Work::Terminal(_) => "terminal workspace", + Work::WorkspaceRoot { .. } => "workspace root", + Work::Metadata { .. } => "private metadata", + Work::Leaf { action, .. } => match action { + LeafAction::Detach => "artifact detach", + LeafAction::UnlinkPayload => "artifact payload unlink", + LeafAction::UnlinkReceipt => "artifact receipt unlink", + }, + }; + let result = match selected { + Work::Scheduled { .. } => Err(locks::FixedControlError::Invalid( + locks::FixedControlProblem::InvalidRecord, + )), + Work::Legacy(legacy) => legacy.advance(grant, confirmation).map(|actual| { + report = actual; + }), + Work::Generation(generation) => generation.advance(grant, confirmation).map(|actual| { + report = actual; + }), + Work::Backup(backup) => backup.advance(grant, confirmation).map(|actual| { + report = actual; + }), + Work::Terminal(terminal) => terminal.advance(grant, &mut report), + Work::WorkspaceRoot { terminal, removal } => { + use super::maintenance_workspace_gate as gate; + let progress = if removal.is_initial() { + gate::begin_workspace_root_removal( + terminal.as_mut().ok_or(CandidateError::InvalidEvidence)?, + removal, + grant, + ) + } else { + gate::advance_workspace_root_removal(removal, grant, 0, &mut |outcome, _grant| { + // The actual outcome is consumed before the gate's durable + // Idle completion. It cannot be synthesized by discovery. + let _closed = ( + outcome.root_identity(), + outcome.incarnation(), + outcome.source_epoch(), + ); + Ok(()) + }) + }; + progress.map(|progress| { + if progress == gate::WorkspaceRemovalProgress::Complete { + report.mutated_entries = report.mutated_entries.saturating_add(1); + report.reclaimed_entries = report.reclaimed_entries.saturating_add(1); + } + }) + } + Work::Metadata { + prepared, + update, + append, + reset, + } => { + let effects = update.effects() + + if append.is_some() { 3 } else { 0 } + + if reset.is_some() { 2 } else { 0 }; + super::maintenance_admission::with_private_metadata( + prepared, + grant, + effects, + &mut |mutation, grant| { + if let Some(append) = append { + mutation.append_frame(append.offset, &append.frame, grant)?; + } + if let Some(len) = reset { + mutation.reset_stream(*len, grant)?; + } + mutation.publish_control(update, grant) + }, + ) + } + Work::Leaf { + ready, + leaf, + stamp, + stream_stamp, + held_file: _, + payload_nonce, + post_rename, + action, + } => { + let mut changed = false; + let result = super::maintenance_admission::with_prepared_canonical( + ready, + grant, + 28, + &mut |mutation, grant| { + let invalid = + || locks::FixedControlError::Invalid(locks::FixedControlProblem::Changed); + if mutation.origin_scope() != leaf.scope { + return Err(invalid()); + } + let root = mutation.directory(); + grant.check_time()?; + let stream = root.open_readonly_file(b".publication.stream")?; + stream_stamp.recheck(&stream)?; + let mut frame = [0; 512]; + grant.check_time()?; + locks::read_fixed_range(&stream, leaf.stream_offset, &mut frame)?; + if frame != stream_frame(leaf) { + return Err(invalid()); + } + let name = encode_key(&leaf.key); + grant.check_time()?; + let prefix = if leaf.source_kind == 0 { + Some(root.open_directory(&name[..2])?) + } else { + None + }; + let parent = prefix.as_ref().unwrap_or(root); + let source_name: &[u8] = if leaf.source_kind == 0 { + &name + } else { + &leaf.source_name + }; + grant.check_time()?; + let directory = parent.open_directory(source_name)?; + if directory.identity() != leaf.directory { + return Err(invalid()); + } + match action { + LeafAction::Detach => { + if mutation.epoch().before != leaf.source_epoch { + return Err(invalid()); + } + grant.check_time()?; + let payload = + directory.open_existing(maintenance_origin::PAYLOAD_NAME)?; + stamp.recheck(&payload)?; + if locks::created_file_nonce(&payload)? != *payload_nonce { + return Err(invalid()); + } + grant.check_time()?; + directory.rename_file_no_replace( + maintenance_origin::PAYLOAD_NAME, + root, + &leaf.tombstone(), + leaf.payload, + )?; + changed = true; + grant.check_time()?; + let moved = root.open_readonly_file(&leaf.tombstone())?; + let current = FixedFileStamp::capture(&moved, FixedControlKind::File)?; + let mut before = [0; 80]; + let mut after = [0; 80]; + stamp.encode_checkpoint(&mut before); + current.encode_checkpoint(&mut after); + // Only this successfully observed own rename may + // establish its new ctime. Identity/kind/length/link + // and mtime plus immutable EA must still match. + if before[..64] != after[..64] + || locks::created_file_nonce(&moved)? != *payload_nonce + { + return Err(invalid()); + } + *post_rename = Some(current); + grant.check_time()?; + directory.sync_namespace()?; + grant.check_time()?; + root.sync_namespace()?; + #[cfg(all(test, debug_assertions))] + fault::reach(leaf.scope.root, CandidateFault::AfterDetach)?; + } + LeafAction::UnlinkPayload => { + grant.check_time()?; + let payload = root.open_existing(&leaf.tombstone())?; + stamp.recheck(&payload)?; + if locks::created_file_nonce(&payload)? != *payload_nonce { + return Err(invalid()); + } + grant.check_time()?; + root.unlink_file(&leaf.tombstone(), leaf.payload)?; + changed = true; + grant.check_time()?; + root.sync_namespace()?; + #[cfg(all(test, debug_assertions))] + fault::reach(leaf.scope.root, CandidateFault::AfterPayloadUnlink)?; + } + LeafAction::UnlinkReceipt => { + grant.check_time()?; + match directory.open_readonly_file(maintenance_origin::PAYLOAD_NAME) { + Err(error) if absent(&error) => {} + Err(error) => return Err(error.into()), + Ok(_) => return Err(invalid()), + } + grant.check_time()?; + match root.open_readonly_file(&leaf.tombstone()) { + Err(error) if absent(&error) => {} + Err(error) => return Err(error.into()), + Ok(_) => return Err(invalid()), + } + grant.check_time()?; + let receipt = + directory.open_existing(maintenance_origin::ORIGIN_NAME)?; + stamp.recheck(&receipt)?; + grant.check_time()?; + directory.unlink_file(maintenance_origin::ORIGIN_NAME, leaf.receipt)?; + changed = true; + grant.check_time()?; + directory.sync_namespace()?; + // Atomic-empty only; unknown siblings keep their public directory. + if leaf.directory_created { + grant.check_time()?; + match parent.remove_empty_directory(source_name, leaf.directory) { + Ok(()) => { + grant.check_time()?; + parent.sync_namespace()?; + } + Err(error) if super::locks::is_directory_not_empty(&error) => { + report.unknown = true; + report.reasons |= 1; + } + Err(error) => return Err(error.into()), + } + } + } + } + Ok(()) + }, + ); + if changed { + report.mutated_entries = report.mutated_entries.saturating_add(1); + if matches!(action, LeafAction::UnlinkPayload) { + needs_disposal = true; + report.reclaimed_entries = report.reclaimed_entries.saturating_add(1); + report.reclaimed_bytes = report.reclaimed_bytes.saturating_add(leaf.byte_len); + } + } + result.map(|_| ()) + } + }; + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + if let Err(error) = &result { + eprintln!("cleanup {operation} mutation rejected: {error:?}"); + } + } + match result { + Ok(()) => {} + Err(locks::FixedControlError::Grant(_)) + | Err(locks::FixedControlError::Invalid(locks::FixedControlProblem::Busy)) => { + deferred(&mut report, 2) + } + Err(locks::FixedControlError::Invalid(_)) => deferred(&mut report, 5), + Err(locks::FixedControlError::Resource(_)) => return Err(CandidateError::ResourceLimit), + Err(locks::FixedControlError::Io(error)) => { + deferred( + &mut report, + if error.kind() == std::io::ErrorKind::PermissionDenied { + 8 + } else { + 12 + }, + ); + } + } + let status = if needs_disposal { + CandidateStatus::NeedsDisposal + } else if report.deferred { + CandidateStatus::Deferred + } else if report.pending { + CandidateStatus::Pending + } else { + CandidateStatus::Complete + }; + Ok(CandidateAdvance { status, report }) +} +pub(crate) fn finish_candidate( + prepared: &mut PreparedCandidate, + advance: CandidateAdvance, + state: &mut CandidatePreparationState, + cancel: &dyn Fn() -> bool, +) -> Result { + if cancel() { + return Err(CandidateError::Cancelled); + } + let selected = match &mut **prepared.work.get_mut() { + Work::Scheduled { inner, .. } => &mut **inner, + other => other, + }; + if let Work::Legacy(legacy) = &mut *selected { + super::maintenance_legacy_engine::finish_work(legacy, &mut state.legacy_jobs) + .map_err(admission)?; + } + if let Work::Generation(generation) = &mut *selected { + super::maintenance_generation_engine::finish_work(generation, &mut state.generations); + } + if let Work::Backup(backup) = &mut *selected { + super::maintenance_backup::finish_work(backup, &mut state.backup).map_err(admission)?; + } + // Last close of an unlinked payload may perform expensive disposal. Keep + // the preparation handle through mutation and close it only offpermit. + if let Work::Leaf { + held_file, + post_rename, + .. + } = &mut *selected + { + let _observed_own_transition = post_rename.take(); + drop(held_file.take()); + } + if let Work::Terminal(terminal) = &mut *selected { + drop(terminal.held_payload.take()); + } + Ok(advance.report) +} + +#[cfg(all(test, debug_assertions))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum CandidateFault { + AfterDetach, + AfterPayloadUnlink, + DuringPreparation, +} +#[cfg(all(test, debug_assertions))] +pub(crate) use fault::{arm_candidate_fault, arm_preparation_pause, CandidateFaultGuard}; +#[cfg(all(test, debug_assertions))] +mod fault { + use super::*; + use std::sync::{ + atomic::{AtomicU64, Ordering}, + Mutex, + }; + #[derive(Clone, Copy)] + struct Armed { + root: locks::FileIdentity, + point: CandidateFault, + nonce: u64, + hit: bool, + cancelled: bool, + } + static ARMED: Mutex<[Option; 8]> = Mutex::new([None; 8]); + static NEXT: AtomicU64 = AtomicU64::new(1); + pub(crate) struct CandidateFaultGuard { + slot: usize, + nonce: u64, + } + impl CandidateFaultGuard { + pub(crate) fn cancelled(&self) -> bool { + ARMED + .lock() + .ok() + .and_then(|a| a[self.slot]) + .is_some_and(|a| a.nonce == self.nonce && a.cancelled) + } + pub(crate) fn reached(&self) -> bool { + ARMED + .lock() + .ok() + .and_then(|a| a[self.slot]) + .is_some_and(|a| a.nonce == self.nonce && a.hit) + } + } + impl Drop for CandidateFaultGuard { + fn drop(&mut self) { + if let Ok(mut slots) = ARMED.lock() { + if slots[self.slot].is_some_and(|a| a.nonce == self.nonce) { + slots[self.slot] = None; + } + } + } + } + pub(crate) fn arm_candidate_fault( + root: &std::path::Path, + point: CandidateFault, + ) -> Result { + let root = locks::path_identity(root).map_err(evidence)?; + let nonce = NEXT.fetch_add(1, Ordering::Relaxed); + let mut slots = ARMED.lock().map_err(|_| CandidateError::InvalidEvidence)?; + if slots.iter().flatten().any(|a| a.root == root) { + return Err(CandidateError::InvalidEvidence); + } + let slot = slots + .iter() + .position(Option::is_none) + .ok_or(CandidateError::ResourceLimit)?; + slots[slot] = Some(Armed { + root, + point, + nonce, + hit: false, + cancelled: false, + }); + Ok(CandidateFaultGuard { slot, nonce }) + } + pub(crate) fn arm_preparation_pause( + root: &std::path::Path, + ) -> Result { + arm_candidate_fault(root, CandidateFault::DuringPreparation) + } + pub(super) fn preparation_pause( + root: locks::FileIdentity, + cancel: &dyn Fn() -> bool, + ) -> Result<(), CandidateError> { + loop { + let stopped = cancel(); + { + let mut slots = ARMED.lock().map_err(|_| CandidateError::InvalidEvidence)?; + let Some(armed) = slots + .iter_mut() + .flatten() + .find(|a| a.root == root && a.point == CandidateFault::DuringPreparation) + else { + return Ok(()); + }; + armed.hit = true; + if stopped { + armed.cancelled = true; + return Err(CandidateError::Cancelled); + } + } + std::thread::sleep(std::time::Duration::from_millis(1)); + } + } + pub(super) fn paused(root: locks::FileIdentity) -> bool { + ARMED.lock().ok().is_some_and(|slots| { + slots + .iter() + .flatten() + .any(|a| a.root == root && a.hit && a.point != CandidateFault::DuringPreparation) + }) + } + pub(super) fn reach(root: locks::FileIdentity, point: CandidateFault) -> std::io::Result<()> { + let mut slots = ARMED.lock().map_err(|_| std::io::ErrorKind::Other)?; + if let Some(armed) = slots + .iter_mut() + .flatten() + .find(|a| a.root == root && a.point == point && !a.hit) + { + armed.hit = true; + return Err(std::io::ErrorKind::Interrupted.into()); + } + Ok(()) + } +} +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn candidate_recovers_interrupted_role_control_before_parsing_legacy_payload() { + use super::super::maintenance_admission::{ + prepare_metadata_control, prepare_private_metadata, prepare_target_binding, + tests::Fixture, with_private_metadata, METADATA_WRITE_CUT, + }; + use super::super::maintenance_candidate::{ + self as candidate, CandidatePreparationState, CandidateReport, + }; + for cut in 1..=2 { + let mut f = Fixture::new(); + f.target.storage_root = Some(f.layout.storage_root().to_owned()); + let origin = f.prepared().origin_scope(); + let old = b"CGLEG001 original role control"; + let intended = b"CGLEG001 intended role control"; + let mut metadata = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + let initial = + prepare_metadata_control(&origin, metadata.committed().unwrap(), old, &f.memory) + .unwrap(); + with_private_metadata( + &mut metadata, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + initial.effects(), + &mut |mutation, grant| mutation.publish_control(&initial, grant), + ) + .unwrap(); + drop(metadata); + let mut metadata = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + let update = prepare_metadata_control( + &origin, + metadata.committed().unwrap(), + intended, + &f.memory, + ) + .unwrap(); + METADATA_WRITE_CUT.with(|value| *value.borrow_mut() = Some(cut)); + let interrupted = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = with_private_metadata( + &mut metadata, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + update.effects(), + &mut |mutation, grant| mutation.publish_control(&update, grant), + ); + })); + assert!(interrupted.is_err()); + drop(metadata); + f.unlocked(); + let binding = prepare_target_binding(&f.target, &f.memory).unwrap(); + let mut state = CandidatePreparationState::default(); + let mut report = CandidateReport::default(); + let prepared = candidate::prepare_candidate_with_state( + &f.target, + &binding, + &mut state, + &f.memory, + &|| false, + &mut report, + ) + .unwrap() + .expect("interrupted role metadata requires a recovery candidate"); + candidate::advance_candidate( + prepared.borrow_hot_frame(), + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + None, + ) + .unwrap(); + drop(prepared); + let recovered = prepare_private_metadata(&f.target, &origin, &f.memory).unwrap(); + assert_eq!(recovered.committed(), Some(update.next())); + assert_eq!(recovered.payload(), Some(intended.as_slice())); + assert_eq!(f.epoch().counter(), 0); + } + } + + #[test] + fn managed_confirmation_remains_pending_until_legacy_enrollment() { + use super::super::maintenance_admission::{prepare_target_binding, tests::Fixture}; + let mut f = Fixture::new(); + f.target.storage_root = Some(f.layout.storage_root().to_owned()); + let binding = prepare_target_binding(&f.target, &f.memory).unwrap(); + let mut state = CandidatePreparationState::default(); + state.set_legacy_interest(true); + let mut report = CandidateReport::default(); + assert!(prepare_candidate_with_state( + &f.target, + &binding, + &mut state, + &f.memory, + &|| false, + &mut report, + ) + .unwrap() + .is_none()); + assert!(state.legacy_interest); + assert!(report.pending && report.deferred); + assert_ne!(report.reasons & (1 << 3), 0); + } + + #[test] + fn prepared_candidate_crossings_keep_large_state_indirect() { + assert!(std::mem::size_of::() < 2048); + assert!(std::mem::size_of::() < 4096); + eprintln!( + "S3 requested layout: PreparedCandidate={} Work={} Canonical={} Metadata={}", + std::mem::size_of::(), + std::mem::size_of::(), + std::mem::size_of::(), + std::mem::size_of::() + ); + } + #[test] + fn candidate_reason_codes_are_fixed_and_reports_do_not_invent_measurements() { + let r = CandidateReport::default(); + assert!(!r.observations_complete); + assert_eq!(r.reclaimed_allocated_bytes, None); + assert!((0..=12).all(|bit| candidate_reason(bit).is_some())); + assert_eq!(candidate_reason(13), None); + } + #[test] + fn retirement_control_roundtrip_does_not_certify_whole_pass() { + let id = locks::FileIdentity::from_fixed_words(1, 2); + let scope = super::super::maintenance_admission::RootOriginBinding { + root: id, + coordination: id, + cohort: id, + publication_binding: id, + incarnation: [3; 32], + }; + let control = Control::decode(b"CGBUILD1", scope).unwrap(); + let encoded = control.encode(); + let decoded = Control::decode(&encoded, scope).unwrap(); + assert!(decoded.leaf.is_none()); + let mut corrupt = encoded; + corrupt[8] = 1; + assert!(Control::decode(&corrupt, scope).is_err()); + } +} + +/// Produced only after the already-prepared completion control was durably +/// published through the actual private-metadata mutation facade. A decoded +/// completion record is deliberately a different, non-authorizing fact. +pub(crate) struct GenerationCohortDone { + managed: locks::FileIdentity, + removed: locks::FileIdentity, + cohort_digest: [u8; 32], +} +impl GenerationCohortDone { + pub(crate) fn matches( + &self, + managed: locks::FileIdentity, + removed: locks::FileIdentity, + cohort_digest: [u8; 32], + ) -> bool { + self.managed == managed && self.removed == removed && self.cohort_digest == cohort_digest + } +} +/// Both the control image and cursor are constructed and charged off authority. +/// The root-removal callback may borrow this owner; it never moves or drops it. +pub(crate) struct PreparedGenerationCompletion { + managed: locks::FileIdentity, + removed: locks::FileIdentity, + cohort_digest: [u8; 32], + update: MemoryBox, +} +impl PreparedGenerationCompletion { + pub(crate) fn effects(&self) -> usize { + self.update.effects() + } + pub(crate) fn publish( + &self, + mutation: &mut super::maintenance_admission::PrivateMetadataMutation<'_>, + grant: &mut MutationGrant, + ) -> Result { + mutation.publish_control(&self.update, grant)?; + Ok(GenerationCohortDone { + managed: self.managed, + removed: self.removed, + cohort_digest: self.cohort_digest, + }) + } +} +pub(crate) fn prepare_generation_completion( + scope: &super::maintenance_admission::RootOriginBinding, + before: super::maintenance_controls::CommitFact, + managed: locks::FileIdentity, + removed: locks::FileIdentity, + cohort_digest: [u8; 32], + cursor: &super::maintenance::MaintenanceCursor, + memory: &CleanupMemory, +) -> Result { + const SIZE: usize = 72 + super::maintenance::CURSOR_BYTES; + let _scratch = memory + .reserve(SIZE) + .map_err(|_| CandidateError::ResourceLimit)?; + let mut bytes = [0; SIZE]; + bytes[..8].copy_from_slice(b"CGGENDON"); + for (offset, id) in [(8, managed), (24, removed)] { + let (a, b) = id.fixed_words(); + bytes[offset..offset + 8].copy_from_slice(&a.to_le_bytes()); + bytes[offset + 8..offset + 16].copy_from_slice(&b.to_le_bytes()); + } + bytes[40..72].copy_from_slice(&cohort_digest); + bytes[72..].copy_from_slice(&cursor.encode()); + let update = + super::maintenance_admission::prepare_metadata_control(scope, before, &bytes, memory) + .map_err(admission)?; + Ok(PreparedGenerationCompletion { + managed, + removed, + cohort_digest, + update: MemoryBox::new(update, memory).map_err(|_| CandidateError::ResourceLimit)?, + }) +} + +// A workspace output's genuine origin record is its prebound, same-root +// retirement journal. No new namespace or directory is adopted as owned. +const WORKSPACE_RETIRE_BYTES: usize = 512; +const WORKSPACE_PATH_DEPTH: usize = 16; +#[derive(Clone, Copy)] +struct WorkspaceParentPath { + names: [[u8; 255]; WORKSPACE_PATH_DEPTH], + lengths: [u8; WORKSPACE_PATH_DEPTH], + identities: [Option; WORKSPACE_PATH_DEPTH], + depth: usize, +} +impl Default for WorkspaceParentPath { + fn default() -> Self { + Self { + names: [[0; 255]; WORKSPACE_PATH_DEPTH], + lengths: [0; WORKSPACE_PATH_DEPTH], + identities: [None; WORKSPACE_PATH_DEPTH], + depth: 0, + } + } +} +struct TerminalWork { + ready: MemoryBox, + scope: super::maintenance_controls::WorkspaceOriginScope, + parent: MemoryBox, + origin: maintenance_origin::WorkspaceOriginFacts, + receipt_name: [u8; 64], + source_name: [u8; 255], + source_name_len: u8, + receipt_only: bool, + root_metadata_receipt: bool, + receipt_stamp: FixedFileStamp, + receipt_nonce: [u8; 32], + receipt_length: u64, + append_offset: u64, + prefix_length: usize, + prefix_digest: [u8; 32], + payload_stamp: Option, + payload_digest: [u8; 32], + payload_length: u64, + stage: u8, + frame: [u8; WORKSPACE_RETIRE_BYTES], + held_payload: Option, +} +fn workspace_scope_matches( + f: &maintenance_origin::WorkspaceOriginFacts, + s: super::maintenance_controls::WorkspaceOriginScope, +) -> bool { + let b = f.binding; + b.root == s.root + && b.coordination == s.coordination + && b.registration == s.registration + && b.publication_binding == s.publication_binding + && b.incarnation == s.incarnation + && b.registry == s.registry +} +fn workspace_retire_frame( + work: &TerminalWork, + stage: u8, + source: u64, + admission_epoch: u64, +) -> [u8; WORKSPACE_RETIRE_BYTES] { + let mut b = [0; WORKSPACE_RETIRE_BYTES]; + b[..8].copy_from_slice(b"CGWRET01"); + b[8] = stage; + b[16..24].copy_from_slice(&source.to_le_bytes()); + b[24..32].copy_from_slice(&admission_epoch.to_le_bytes()); + b[32..40].copy_from_slice(&(work.prefix_length as u64).to_le_bytes()); + b[40..72].copy_from_slice(&work.prefix_digest); + if let Some(stamp) = work.payload_stamp { + stamp.encode_checkpoint((&mut b[72..152]).try_into().unwrap()); + } + b[152..160].copy_from_slice(&work.payload_length.to_le_bytes()); + b[160..192].copy_from_slice(&work.payload_digest); + b[192..224].copy_from_slice(&work.origin.binding.operation); + for (at, id) in [(224, work.scope.root), (240, work.origin.receipt)] { + let (a, c) = id.fixed_words(); + b[at..at + 8].copy_from_slice(&a.to_le_bytes()); + b[at + 8..at + 16].copy_from_slice(&c.to_le_bytes()); + } + b[256] = u8::from(work.origin.directory); + let sha = Sha256::digest(&b[..480]); + b[480..].copy_from_slice(&sha); + b +} +fn workspace_frame_valid(b: &[u8; 512], work: &TerminalWork, stage: u8) -> bool { + &b[..8] == b"CGWRET01" + && b[8] == stage + && b[9..16].iter().all(|x| *x == 0) + && b[32..72] == workspace_retire_frame(work, stage, 0, 0)[32..72] + && b[192..257] == workspace_retire_frame(work, stage, 0, 0)[192..257] + && b[257..480].iter().all(|x| *x == 0) + && b[480..] == Sha256::digest(&b[..480])[..] +} +fn workspace_parent( + root: &RootedDirectory, + path: &WorkspaceParentPath, +) -> std::io::Result> { + let mut current = None; + for i in 0..path.depth { + let parent = current.as_ref().unwrap_or(root); + let next = parent.open_directory(&path.names[i][..path.lengths[i] as usize])?; + if Some(next.identity()) != path.identities[i] { + return Err(std::io::ErrorKind::InvalidData.into()); + } + current = Some(next); + } + Ok(current) +} +// Iterative fixed-depth discovery owns no exclusion. Every retained directory +// and all caller scratch are charged before construction. Unknown depth is +// preserved; this visitor never recursively deletes a directory. +#[inline(never)] +fn new_workspace_parent_path( + memory: &CleanupMemory, +) -> Result, CandidateError> { + MemoryBox::new(WorkspaceParentPath::default(), memory) + .map_err(|_| CandidateError::ResourceLimit) +} +#[inline(never)] +fn find_workspace_parent( + root: &RootedDirectory, + wanted: locks::FileIdentity, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, CandidateError> { + struct Walk { + dirs: [Option; WORKSPACE_PATH_DEPTH], + last: [[u8; 255]; WORKSPACE_PATH_DEPTH], + lens: [u8; WORKSPACE_PATH_DEPTH], + path: Option>, + scratch: [u8; 4096], + } + let mut w = MemoryBox::new( + Walk { + dirs: std::array::from_fn(|_| None), + last: [[0; 255]; WORKSPACE_PATH_DEPTH], + lens: [0; WORKSPACE_PATH_DEPTH], + path: Some(new_workspace_parent_path(memory)?), + scratch: [0; 4096], + }, + memory, + ) + .map_err(|_| CandidateError::ResourceLimit)?; + if root.identity() == wanted { + return Ok(w.path.take()); + } + let mut depth = 0; + loop { + if cancel() { + return Err(CandidateError::Cancelled); + } + let mut chosen = [0; 255]; + let mut chosen_len = 0usize; + let previous = w.last[depth]; + let previous_len = w.lens[depth] as usize; + let Walk { dirs, scratch, .. } = &mut *w; + let directory = if depth == 0 { + root + } else { + dirs[depth - 1].as_ref().unwrap() + }; + directory + .visit_directories(scratch, &mut |name| { + if name.is_empty() + || name.len() > 255 + || !name.is_ascii() + || name == b"." + || name == b".." + || name <= &previous[..previous_len] + || (chosen_len > 0 && name >= &chosen[..chosen_len]) + { + return Ok(()); + } + if directory.open_directory_readonly(name).is_ok() { + chosen[..name.len()].copy_from_slice(name); + chosen_len = name.len(); + } + Ok(()) + }) + .map_err(io)?; + if chosen_len == 0 { + if depth == 0 { + return Ok(None); + } + depth -= 1; + w.dirs[depth] = None; + w.path.as_mut().unwrap().depth = depth; + continue; + } + w.last[depth] = chosen; + w.lens[depth] = chosen_len as u8; + let parent = if depth == 0 { + root + } else { + w.dirs[depth - 1].as_ref().unwrap() + }; + let child = parent + .open_directory_readonly(&chosen[..chosen_len]) + .map_err(io)?; + w.path.as_mut().unwrap().names[depth] = chosen; + w.path.as_mut().unwrap().lengths[depth] = chosen_len as u8; + w.path.as_mut().unwrap().identities[depth] = Some(child.identity()); + w.path.as_mut().unwrap().depth = depth + 1; + if child.identity() == wanted { + return Ok(w.path.take()); + } + if depth + 1 == WORKSPACE_PATH_DEPTH { + continue; + } + w.dirs[depth] = Some(child); + depth += 1; + w.last[depth] = [0; 255]; + w.lens[depth] = 0; + } +} +fn terminal_name_address(kind: u8, name: &[u8]) -> Option { + if name.is_empty() || name.len() > 127 { + return None; + } + let mut address = [0; 128]; + address[0] = kind; + address[1..1 + name.len()].copy_from_slice(name); + Some(super::maintenance::CursorAddress(address)) +} +fn terminal_path_address( + path: &std::path::Path, + domain: &ReferenceDomain, + target: &MaintenanceTarget, +) -> Option<( + super::maintenance::WorkCategory, + super::maintenance::CursorAddress, +)> { + for kind in 1..=3 { + let base = domain.workspace_registry(kind, Some(&target.state_root))?; + if path.parent() == Some(base.as_path()) { + return Some(( + if kind == 3 { + super::maintenance::WorkCategory::Phase + } else { + super::maintenance::WorkCategory::Run + }, + terminal_name_address(kind, path.file_name()?.as_encoded_bytes())?, + )); + } + } + None +} +fn terminal_work_address( + work: &Work, + domain: &ReferenceDomain, + target: &MaintenanceTarget, +) -> Option { + let root = match work { + Work::Terminal(work) => work.ready.root(), + Work::WorkspaceRoot { removal, .. } => removal.root(), + _ => return None, + }; + terminal_path_address(root, domain, target).map(|(_, address)| address) +} +#[inline(never)] +fn boxed_pending_workspace_removal( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + memory: &CleanupMemory, +) -> Result< + Option>, + CandidateError, +> { + super::maintenance_workspace_gate::prepare_pending_workspace_root_removal_for_target( + target, domain, memory, + ) + .map_err(admission) +} +#[inline(never)] +fn boxed_workspace_removal( + path: &std::path::Path, + domain: &ReferenceDomain, + terminal: &super::maintenance_controls::PreparedTerminalWorkspace, + memory: &CleanupMemory, +) -> Result, CandidateError> +{ + super::maintenance_workspace_gate::prepare_workspace_root_removal_boxed( + path, + domain, + Some(terminal), + memory, + ) + .map_err(|error| { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("workspace preparation failed: {error:?}"); + } + admission(error) + }) +} +#[inline(never)] +fn prepare_terminal_candidate( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + report: &mut CandidateReport, + category: super::maintenance::WorkCategory, + after: Option, +) -> Result, CandidateError> { + if let Some(removal) = boxed_pending_workspace_removal(target, domain, memory)? { + if terminal_path_address(removal.root(), domain, target).is_some_and(|(c, _)| c == category) + { + return Ok(Some(Work::WorkspaceRoot { + terminal: None, + removal, + })); + } + } + for pass in 0..2 { + if pass == 1 && after.is_none() { + break; + } + for (kind, base) in [ + (1, domain.workspace_registry(1, Some(&target.state_root))), + (2, domain.workspace_registry(2, Some(&target.state_root))), + (3, domain.workspace_registry(3, Some(&target.state_root))), + ] { + if (category == super::maintenance::WorkCategory::Phase) != (kind == 3) { + continue; + } + let Some(base) = base else { + continue; + }; + let id = match locks::path_identity(&base) { + Ok(id) => id, + Err(_) => continue, + }; + let root = RootedDirectory::open_readonly_root(&base, id).map_err(io)?; + let mut next = [0; 255]; + let mut next_len = 0; + let mut scratch = [0; 4096]; + // Each root is examined independently. Foreign roots are never mutated. + root.visit_entries(&mut scratch, &mut |name| { + if cancel() { + return Err(std::io::ErrorKind::Interrupted.into()); + } + let selected_pass = terminal_name_address(kind, name).is_some_and(|address| { + after.is_none_or(|previous| { + if pass == 0 { + address > previous + } else { + address <= previous + } + }) + }); + if selected_pass + && name.len() <= 255 + && !name.is_empty() + && name.is_ascii() + && name != b"." + && name != b".." + && (next_len == 0 || name < &next[..next_len]) + && root.open_directory_readonly(name).is_ok() + { + next = [0; 255]; + next[..name.len()].copy_from_slice(name); + next_len = name.len(); + } + Ok(()) + }) + .map_err(io)?; + while next_len > 0 { + if cancel() { + return Err(CandidateError::Cancelled); + } + let name = std::str::from_utf8(&next[..next_len]) + .map_err(|_| CandidateError::InvalidEvidence)?; + let path = base.join(name); + match super::maintenance_controls::prepare_terminal_workspace_boxed( + &path, domain, memory, + ) { + Ok(ready) => { + if let Some(work) = prepare_terminal_output(ready, memory, cancel, report)? + { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("terminal candidate selected output: {}", path.display()); + } + return Ok(Some(Work::Terminal(work))); + } + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("terminal candidate has no output: {}", path.display()); + } + let terminal = + super::maintenance_controls::prepare_terminal_workspace_boxed( + &path, domain, memory, + ) + .map_err(admission)?; + match boxed_workspace_removal(&path, domain, &terminal, memory) { + Ok(removal) => { + report.pending = true; + report.reasons |= 1 << 7; + return Ok(Some(Work::WorkspaceRoot { + terminal: Some(terminal), + removal, + })); + } + Err(error) => { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "terminal candidate root removal rejected: {}: {error:?}", + path.display() + ); + } + let _ = error; + } + } + } + Err(error) => { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "terminal candidate workspace rejected: {}: {error:?}", + path.display() + ); + } + let _ = error; + deferred(report, 2); + } + } + let previous = next; + let previous_len = next_len; + next = [0; 255]; + next_len = 0; + root.visit_entries(&mut scratch, &mut |name| { + if cancel() { + return Err(std::io::ErrorKind::Interrupted.into()); + } + let selected_pass = terminal_name_address(kind, name).is_some_and(|address| { + after.is_none_or(|previous| { + if pass == 0 { + address > previous + } else { + address <= previous + } + }) + }); + if selected_pass + && name.len() <= 255 + && !name.is_empty() + && name.is_ascii() + && name > &previous[..previous_len] + && (next_len == 0 || name < &next[..next_len]) + && root.open_directory_readonly(name).is_ok() + { + next[..name.len()].copy_from_slice(name); + next_len = name.len(); + } + Ok(()) + }) + .map_err(io)?; + } + } + } + Ok(None) +} +#[inline(never)] +fn workspace_has_child_receipts( + registry: &RootedDirectory, + parent: locks::FileIdentity, + scope: super::maintenance_controls::WorkspaceOriginScope, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + let mut found = false; + let mut scratch = [0; 4096]; + registry + .visit_entries(&mut scratch, &mut |entry| { + if cancel() { + return Err(std::io::ErrorKind::Interrupted.into()); + } + if found || entry.len() != 64 || !lower_hex(entry) { + return Ok(()); + } + let Ok(file) = registry.open_readonly_file(entry) else { + found = true; + return Ok(()); + }; + let Ok(stamp) = FixedFileStamp::capture(&file, FixedControlKind::File) else { + found = true; + return Ok(()); + }; + let mut bytes = [0; 1024]; + if locks::read_fixed_range(&file, 0, &mut bytes).is_err() { + found = true; + return Ok(()); + } + found = match maintenance_origin::decode_workspace_output_origin( + &bytes, + stamp.identity(), + None, + memory, + ) { + Ok(fact) => { + // This is only a conservative veto. It grants no authority + // over a child, including one whose payload is already gone. + workspace_scope_matches(&fact, scope) && fact.binding.parent == parent + } + Err(_) => true, + }; + Ok(()) + }) + .map_err(io)?; + Ok(found) +} + +fn prepare_terminal_output( + ready: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + report: &mut CandidateReport, +) -> Result>, CandidateError> { + let scope = ready.origin_scope(); + let root = RootedDirectory::open_readonly_root(ready.root(), scope.root).map_err(io)?; + let registry = root + .open_directory_readonly(b".workspace-origins") + .map_err(io)?; + if registry.identity() != scope.registry { + return Err(CandidateError::Stale); + } + let mut name = [0; 64]; + let mut found = false; + let mut scratch = [0; 4096]; + for want_directory in [false, true] { + registry + .visit_entries(&mut scratch, &mut |entry| { + if cancel() { + return Err(std::io::ErrorKind::Interrupted.into()); + } + if entry.len() == 64 && lower_hex(entry) && (!found || entry < &name) { + let Ok(file) = registry.open_readonly_file(entry) else { + return Ok(()); + }; + let Ok(stamp) = FixedFileStamp::capture(&file, FixedControlKind::File) else { + return Ok(()); + }; + let mut bound = [0; 1024]; + if locks::read_fixed_range(&file, 0, &mut bound).is_err() { + return Ok(()); + } + let Ok(fact) = maintenance_origin::decode_workspace_output_origin( + &bound, + stamp.identity(), + None, + memory, + ) else { + return Ok(()); + }; + let receipt_nonce = super::maintenance_controls::workspace_receipt_nonce( + fact.binding.incarnation, + fact.binding.parent, + fact.binding.output, + fact.binding.operation, + ); + if locks::created_file_nonce(&file)? != Some(receipt_nonce) { + report.unknown = true; + return Ok(()); + } + if fact.directory != want_directory + || !workspace_scope_matches(&fact, scope) + || super::maintenance_controls::workspace_origin_name( + &fact.binding.incarnation, + fact.binding.parent, + fact.binding.output, + &fact.binding.operation, + ) != entry + { + return Ok(()); + } + if fact.directory { + let Ok(Some(path)) = + find_workspace_parent(&root, fact.binding.parent, memory, cancel) + else { + return Ok(()); + }; + let Ok(parent) = workspace_parent(&root, &path) else { + return Ok(()); + }; + let p = parent.as_ref().unwrap_or(&root); + match p.open_directory_readonly( + &fact.binding.name[..fact.binding.name_len as usize], + ) { + Ok(directory) => { + if directory.identity() != fact.binding.output { + return Ok(()); + } + // Children keep their parent until their own + // retirement receipts have also been consumed. + // Otherwise a restart cannot resolve the parent + // identity of a child already removed from disk. + if workspace_has_child_receipts( + ®istry, + directory.identity(), + scope, + memory, + cancel, + ) + .map_err(|_| std::io::ErrorKind::InvalidData)? + { + return Ok(()); + } + let mut contents = [0; 4096]; + let mut empty = true; + directory.visit_entries(&mut contents, &mut |name| { + if cancel() { + return Err(std::io::ErrorKind::Interrupted.into()); + } + if name != b"." && name != b".." { + empty = false + } + Ok(()) + })?; + if !empty { + return Ok(()); + } + } + Err(e) if absent(&e) => { + if file.metadata()?.len() == 1024 { + return Ok(()); + } + } + Err(_) => return Ok(()), + } + } + name.copy_from_slice(entry); + found = true; + } + Ok(()) + }) + .map_err(io)?; + if found { + break; + } + } + if !found { + return Ok(None); + } + let mut receipt = registry.open_readonly_file(&name).map_err(io)?; + let receipt_stamp = + FixedFileStamp::capture(&receipt, FixedControlKind::File).map_err(admission)?; + let _scratch = memory + .reserve(4096 + std::mem::size_of::()) + .map_err(|_| CandidateError::ResourceLimit)?; + let mut bytes = [0; 4096]; + let n = receipt.read(&mut bytes).map_err(io)?; + if n != receipt.metadata().map_err(io)?.len() as usize || n < 1024 { + return Ok(None); + } + let prefix_len = if n >= 2048 && &bytes[1024..1032] == b"CGWOUT01" { + 2048 + } else { + 1024 + }; + let origin = match maintenance_origin::decode_workspace_output_origin( + &bytes[..1024], + receipt_stamp.identity(), + None, + memory, + ) { + Ok(f) if workspace_scope_matches(&f, scope) => f, + _ => { + report.unknown = true; + return Ok(None); + } + }; + let receipt_nonce = super::maintenance_controls::workspace_receipt_nonce( + origin.binding.incarnation, + origin.binding.parent, + origin.binding.output, + origin.binding.operation, + ); + if locks::created_file_nonce(&receipt).map_err(io)? != Some(receipt_nonce) { + report.unknown = true; + return Ok(None); + } + let Some(parent) = find_workspace_parent(&root, origin.binding.parent, memory, cancel)? else { + return Ok(None); + }; + let parent_dir = workspace_parent(&root, &parent).map_err(io)?; + let parent_ref = parent_dir.as_ref().unwrap_or(&root); + let mut source_name = origin.binding.name; + let mut source_name_len = origin.binding.name_len; + let mut receipt_only = false; + let root_metadata_receipt = workspace_registered_metadata(&origin, &ready); + if origin.control { + let mut matched = false; + for name in [ + &origin.binding.name[..origin.binding.name_len as usize], + &origin.intended_name[..origin.intended_name_len as usize], + ] { + if workspace_file_matches( + parent_ref, + name, + origin.binding.output, + origin.binding.operation, + ) + .map_err(io)? + { + source_name = [0; 255]; + source_name[..name.len()].copy_from_slice(name); + source_name_len = name.len() as u8; + matched = true; + break; + } + } + // A detached control has neither public alias. Its existing intent + // still governs the private payload and subsequent removal frames. + // Only a never-started, already-consumed output is receipt-only. + receipt_only = (!matched && n == prefix_len) || root_metadata_receipt; + } + let output_name = &source_name[..source_name_len as usize]; + let tombstone = workspace_tombstone(origin.binding.operation); + let mut held = if receipt_only { + None + } else if origin.fifo { + #[cfg(unix)] + { + let pin = super::maintenance_controls::workspace_fifo_pin_name(&origin.binding); + match registry + .open_pinned_fifo(&pin, origin.binding.output, 2) + .or_else(|_| registry.open_pinned_fifo(&pin, origin.binding.output, 1)) + { + Ok(file) => Some(file), + Err(error) if absent(&error) => None, + Err(error) => return Err(io(error)), + } + } + #[cfg(not(unix))] + { + return Ok(None); + } + } else if origin.directory { + match parent_ref.open_directory_readonly(output_name) { + Ok(dir) => Some(dir.pin_handle().map_err(io)?), + Err(e) if absent(&e) => None, + Err(e) => return Err(io(e)), + } + } else { + match root.open_readonly_file(&tombstone) { + Ok(f) => { + // A replacement at the original name is foreign to this retired + // incarnation. Preserve it; it cannot replace the private proof. + Some(f) + } + Err(e) if absent(&e) => match parent_ref.open_readonly_file(output_name) { + Ok(f) => Some(f), + Err(e) if absent(&e) => None, + Err(e) => return Err(io(e)), + }, + Err(e) => return Err(io(e)), + } + }; + if !origin.directory && !origin.fifo && !origin.control { + if let Some(file) = &held { + if locks::file_identity(file).map_err(evidence)? != origin.binding.output + || locks::created_file_nonce(file).map_err(io)? != Some(origin.binding.operation) + { + // An unrelated replacement does not become this receipt's + // payload. Retain that file and dispose only our old receipt. + report.unknown = true; + held = None; + } + } + } + let (stamp, length, digest) = if origin.fifo { + (None, 0, [0; 32]) + } else if let Some(file) = &mut held { + let stamp = FixedFileStamp::capture( + file, + if origin.directory { + FixedControlKind::Directory + } else { + FixedControlKind::File + }, + ) + .map_err(admission)?; + if stamp.identity() != origin.binding.output + || locks::created_file_nonce(file).map_err(io)? != Some(origin.binding.operation) + { + return Ok(None); + } + let mut hash = Sha256::new(); + let mut length = 0u64; + let mut buffer = [0; 8192]; + if !origin.directory { + loop { + if cancel() { + return Err(CandidateError::Cancelled); + } + let n = file.read(&mut buffer).map_err(io)?; + if n == 0 { + break; + } + length = length + .checked_add(n as u64) + .ok_or(CandidateError::InvalidEvidence)?; + hash.update(&buffer[..n]); + } + } + stamp.recheck(file).map_err(admission)?; + (Some(stamp), length, hash.finalize().into()) + } else { + (None, 0, [0; 32]) + }; + // Completed origin always checks the selected actual payload. If it is + // already gone, the intact retirement journal below supplies exact facts. + let full_origin = if held.is_some() && !origin.directory && !origin.fifo { + maintenance_origin::decode_workspace_output_origin( + &bytes[..prefix_len], + receipt_stamp.identity(), + Some((length, digest)), + memory, + ) + .map_err(evidence)? + } else { + origin + }; + let mut work = MemoryBox::new( + TerminalWork { + ready, + scope, + parent, + origin: full_origin, + receipt_name: name, + source_name, + source_name_len, + receipt_only, + root_metadata_receipt, + receipt_stamp, + receipt_nonce, + receipt_length: n as u64, + append_offset: n as u64, + prefix_length: prefix_len, + prefix_digest: Sha256::digest(&bytes[..prefix_len]).into(), + payload_stamp: stamp, + payload_digest: digest, + payload_length: length, + stage: 0, + frame: [0; 512], + held_payload: held, + }, + memory, + ) + .map_err(|_| CandidateError::ResourceLimit)?; + let tail = &bytes[prefix_len..n]; + if tail.len() > 1536 { + report.unknown = true; + return Ok(None); + } + for (i, frame) in tail.chunks_exact(512).enumerate() { + let frame: &[u8; 512] = frame.try_into().unwrap(); + if !workspace_frame_valid(frame, &work, (i + 1) as u8) + || u64::from_le_bytes(frame[16..24].try_into().unwrap()) != work.ready.source_epoch() + || u64::from_le_bytes(frame[24..32].try_into().unwrap()) != work.ready.admission_epoch() + { + report.unknown = true; + return Ok(None); + } + if i == 0 { + let journal_length = u64::from_le_bytes(frame[152..160].try_into().unwrap()); + let journal_digest: [u8; 32] = frame[160..192].try_into().unwrap(); + if work.held_payload.is_some() && (length != journal_length || digest != journal_digest) + { + return Ok(None); + } + work.payload_length = journal_length; + work.payload_digest = journal_digest; + } + if work.held_payload.is_none() && frame[72..152].iter().any(|x| *x != 0) { + let stamp = FixedFileStamp::decode_checkpoint(frame[72..152].try_into().unwrap()) + .map_err(admission)?; + if stamp.identity() != work.origin.binding.output { + return Ok(None); + } + work.payload_stamp = Some(stamp); + } + if i > 0 + && (frame[152..160] != work.payload_length.to_le_bytes() + || frame[160..192] != work.payload_digest) + { + return Ok(None); + } + work.stage = (i + 1) as u8; + } + if work.stage == 0 && work.held_payload.is_none() && !work.receipt_only { + if !work.origin.directory && !work.origin.fifo { + // A producer may already have consumed its temporary file. The + // genuine nonce-bound receipt remains our metadata; no payload is + // adopted or removed by this receipt-only transition. + work.receipt_only = true; + } else { + report.unknown = true; + return Ok(None); + } + } + if work.receipt_only { + if !tail.is_empty() { + report.unknown = true; + return Ok(None); + } + work.stage = 4; + } + work.frame = workspace_retire_frame( + &work, + work.stage + 1, + work.ready.source_epoch(), + work.ready.admission_epoch(), + ); + let partial = &tail[(tail.len() / 512) * 512..]; + if partial != &work.frame[..partial.len()] { + report.unknown = true; + return Ok(None); + } + work.append_offset = (prefix_len + (tail.len() / 512) * 512) as u64; + report.pending = true; + report.reasons |= 1 << 7; + + Ok(Some(work)) +} +fn workspace_tombstone(operation: [u8; 32]) -> [u8; 73] { + let mut name = [0; 73]; + name[..9].copy_from_slice(b".retired-"); + name[9..].copy_from_slice(&encode_key(&operation)); + name +} +impl TerminalWork { + fn advance( + &mut self, + grant: &mut MutationGrant, + report: &mut CandidateReport, + ) -> Result<(), locks::FixedControlError> { + let stage = self.stage; + let scope = self.scope; + let origin = self.origin; + let source_name = self.source_name; + let source_name_len = self.source_name_len; + let receipt_only = self.receipt_only; + let root_metadata_receipt = self.root_metadata_receipt; + let name = self.receipt_name; + let payload_length = self.payload_length; + let receipt_stamp = self.receipt_stamp; + let receipt_nonce = self.receipt_nonce; + let receipt_length = self.receipt_length; + let append_offset = self.append_offset; + let payload_stamp = self.payload_stamp; + let frame = self.frame; + let parent = &self.parent; + let effects = parent.depth + 25; + super::maintenance_controls::with_terminal_workspace( + &mut self.ready, + grant, + effects, + &mut |lease, grant| { + let invalid = + || locks::FixedControlError::Invalid(locks::FixedControlProblem::Changed); + if lease.origin_scope() != scope { + return Err(invalid()); + } + let root = lease.directory(); + let registry = root.open_directory(b".workspace-origins")?; + if registry.identity() != scope.registry { + return Err(invalid()); + } + let mut receipt = registry.open_existing(&name)?; + receipt_stamp.recheck(&receipt)?; + if receipt.metadata()?.len() != receipt_length + || locks::created_file_nonce(&receipt)? != Some(receipt_nonce) + { + return Err(invalid()); + } + let parent_dir = workspace_parent(root, parent)?; + let parent = parent_dir.as_ref().unwrap_or(root); + let source = &source_name[..source_name_len as usize]; + let private = workspace_tombstone(origin.binding.operation); + let append = |receipt: &mut std::fs::File, + grant: &mut MutationGrant| + -> Result<(), locks::FixedControlError> { + use std::io::{Seek, SeekFrom, Write}; + grant.check_time()?; + receipt.seek(SeekFrom::Start(append_offset))?; + receipt.write_all(&frame)?; + receipt.sync_all()?; + let mut actual = [0; 512]; + locks::read_fixed_range(receipt, append_offset, &mut actual)?; + if actual != frame { + return Err(invalid()); + } + Ok(()) + }; + if receipt_only { + // Terminal admission rechecks the root metadata seal. Its + // creator receipt can be disposed while the exact sealed + // root control stays for the fixed-name final removal. + if !root_metadata_receipt { + let present = if origin.control { + workspace_control_present(parent, &origin)? + } else { + workspace_file_matches( + parent, + source, + origin.binding.output, + origin.binding.operation, + )? || workspace_file_matches( + root, + &private, + origin.binding.output, + origin.binding.operation, + )? + }; + if present { + return Err(invalid()); + } + } + grant.check_time()?; + registry.unlink_file(&name, origin.receipt)?; + registry.sync_namespace()?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + report.reclaimed_bytes = report.reclaimed_bytes.saturating_add(receipt_length); + return Ok(()); + } + if origin.fifo { + #[cfg(unix)] + { + let pin = + super::maintenance_controls::workspace_fifo_pin_name(&origin.binding); + match stage { + 0 => { + append(&mut receipt, grant)?; + } + 1 => { + // A public replacement is retained. Only the actual + // creator identity can consume the public FIFO link. + match parent.open_pinned_fifo(source, origin.binding.output, 2) { + Ok(_held) => { + let _pin = registry.open_pinned_fifo( + &pin, + origin.binding.output, + 2, + )?; + grant.check_time()?; + parent.unlink_pinned_fifo( + source, + origin.binding.output, + 2, + )?; + parent.sync_namespace()?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + } + Err(_) => { + let _pin = registry.open_pinned_fifo( + &pin, + origin.binding.output, + 1, + )?; + } + } + append(&mut receipt, grant)?; + } + 2 => { + match registry.open_pinned_fifo(&pin, origin.binding.output, 1) { + Ok(_held) => { + grant.check_time()?; + registry.unlink_pinned_fifo( + &pin, + origin.binding.output, + 1, + )?; + registry.sync_namespace()?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + } + Err(error) if absent(&error) => {} + Err(error) => return Err(error.into()), + } + append(&mut receipt, grant)?; + } + 3 => { + match registry.open_pinned_fifo(&pin, origin.binding.output, 1) { + Err(error) if absent(&error) => {} + _ => return Err(invalid()), + } + grant.check_time()?; + registry.unlink_file(&name, origin.receipt)?; + registry.sync_namespace()?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + report.reclaimed_bytes = + report.reclaimed_bytes.saturating_add(receipt_length); + } + _ => return Err(invalid()), + } + return Ok(()); + } + #[cfg(not(unix))] + { + return Err(invalid()); + } + } + if origin.directory { + match stage { + 0 => append(&mut receipt, grant)?, + 1 => { + match parent.open_directory(source) { + Ok(directory) => { + if directory.identity() != origin.binding.output { + return Err(invalid()); + } + let file = directory.pin_handle()?; + payload_stamp.ok_or_else(invalid)?.recheck(&file)?; + if locks::created_file_nonce(&file)? + != Some(origin.binding.operation) + { + return Err(invalid()); + } + grant.check_time()?; + // Atomic emptiness is checked by the OS; + // unknown concurrent children stay public. + parent.remove_empty_directory(source, origin.binding.output)?; + parent.sync_namespace()?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + } + Err(e) if absent(&e) => {} + Err(e) => return Err(e.into()), + } + append(&mut receipt, grant)?; + } + 2 => { + match parent.open_directory_readonly(source) { + Err(e) if absent(&e) => {} + _ => return Err(invalid()), + } + append(&mut receipt, grant)?; + } + 3 => { + match parent.open_directory_readonly(source) { + Err(e) if absent(&e) => {} + _ => return Err(invalid()), + } + grant.check_time()?; + registry.unlink_file(&name, origin.receipt)?; + registry.sync_namespace()?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + report.reclaimed_bytes = + report.reclaimed_bytes.saturating_add(receipt_length); + } + _ => return Err(invalid()), + } + return Ok(()); + } + match stage { + 0 => append(&mut receipt, grant)?, + 1 => { + match root.open_existing(&private) { + Ok(file) => { + if Some(FixedFileStamp::capture(&file, FixedControlKind::File)?) + != payload_stamp + || locks::created_file_nonce(&file)? + != Some(origin.binding.operation) + { + return Err(invalid()); + } + } + Err(e) if absent(&e) => { + let file = parent.open_existing(source)?; + payload_stamp.ok_or_else(invalid)?.recheck(&file)?; + if locks::created_file_nonce(&file)? + != Some(origin.binding.operation) + { + return Err(invalid()); + } + grant.check_time()?; + parent.rename_file_no_replace( + source, + root, + &private, + origin.binding.output, + )?; + parent.sync_namespace()?; + root.sync_namespace()?; + let moved = root.open_readonly_file(&private)?; + let actual = + FixedFileStamp::capture(&moved, FixedControlKind::File)?; + let mut before = [0; 80]; + let mut after = [0; 80]; + payload_stamp + .ok_or_else(invalid)? + .encode_checkpoint(&mut before); + actual.encode_checkpoint(&mut after); + if before[..64] != after[..64] + || locks::created_file_nonce(&moved)? + != Some(origin.binding.operation) + { + return Err(invalid()); + } + report.mutated_entries += 1; + // Preparation must observe the actual post-own + // stamp outside authority before sealing Detached. + return Ok(()); + } + Err(e) => return Err(e.into()), + } + append(&mut receipt, grant)?; + } + 2 => { + match root.open_existing(&private) { + Ok(file) => { + payload_stamp.ok_or_else(invalid)?.recheck(&file)?; + if locks::created_file_nonce(&file)? + != Some(origin.binding.operation) + { + return Err(invalid()); + } + grant.check_time()?; + root.unlink_file(&private, origin.binding.output)?; + root.sync_namespace()?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + report.reclaimed_bytes = + report.reclaimed_bytes.saturating_add(payload_length); + } + Err(e) if absent(&e) => {} + Err(e) => return Err(e.into()), + } + append(&mut receipt, grant)?; + } + 3 => { + if workspace_file_matches( + root, + &private, + origin.binding.output, + origin.binding.operation, + )? || workspace_file_matches( + parent, + source, + origin.binding.output, + origin.binding.operation, + )? { + return Err(invalid()); + } + grant.check_time()?; + registry.unlink_file(&name, origin.receipt)?; + registry.sync_namespace()?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + report.reclaimed_bytes = + report.reclaimed_bytes.saturating_add(receipt_length); + } + _ => return Err(invalid()), + } + Ok(()) + }, + ) + .map(|_| ()) + } +} + +/// Membership in a durably completed rooted R4 cohort. Fields stay private to +/// the actual stream publisher/selector; decoding an association or certificate +/// independently cannot construct this token. +pub(crate) use super::maintenance_legacy_engine::LegacyStagingCohortLeaf; + +fn workspace_file_matches( + parent: &RootedDirectory, + name: &[u8], + id: locks::FileIdentity, + nonce: [u8; 32], +) -> std::io::Result { + match parent.open_readonly_file(name) { + Ok(file) => Ok(locks::file_identity(&file).map_err(|error| match error { + crate::error::NativeError::Io(error) => error, + _ => std::io::Error::from(std::io::ErrorKind::InvalidData), + })? == id + && locks::created_file_nonce(&file)? == Some(nonce)), + Err(e) if absent(&e) => Ok(false), + Err(e) => Err(e), + } +} +fn workspace_control_present( + parent: &RootedDirectory, + origin: &maintenance_origin::WorkspaceOriginFacts, +) -> std::io::Result { + Ok(workspace_file_matches( + parent, + &origin.binding.name[..origin.binding.name_len as usize], + origin.binding.output, + origin.binding.operation, + )? || workspace_file_matches( + parent, + &origin.intended_name[..origin.intended_name_len as usize], + origin.binding.output, + origin.binding.operation, + )?) +} + +fn workspace_registered_metadata( + origin: &maintenance_origin::WorkspaceOriginFacts, + ready: &super::maintenance_controls::PreparedTerminalWorkspace, +) -> bool { + origin.control + && origin.binding.parent == origin.binding.root + && ready.metadata_proofs().iter().flatten().any(|proof| { + proof.identity == origin.binding.output + && proof.operation == origin.binding.operation + && proof.receipt == Some(origin.receipt) + }) +} diff --git a/src/storage/maintenance_controls.rs b/src/storage/maintenance_controls.rs new file mode 100644 index 00000000..0e557f16 --- /dev/null +++ b/src/storage/maintenance_controls.rs @@ -0,0 +1,3537 @@ +//! Fixed prebound control records. Codecs produce facts, never ownership. +//! Commit and Intent occupy independent checksum/write regions; Intent retains +//! the complete next slot bytes so recovery never guesses from highest version. +use sha2::{Digest, Sha256}; + +pub(crate) const PAYLOAD_BYTES: usize = 2048; +pub(crate) const SLOT_BYTES: usize = 56 + PAYLOAD_BYTES + 32; +pub(crate) const COMMIT_BYTES: usize = 128; +pub(crate) const INTENT_BYTES: usize = 96 + SLOT_BYTES + 32; +pub(crate) const SELECTOR_BYTES: usize = COMMIT_BYTES + INTENT_BYTES; +const SLOT_MAGIC: &[u8; 8] = b"CGSLOT01"; +const COMMIT_MAGIC: &[u8; 8] = b"CGCOMM01"; +const INTENT_MAGIC: &[u8; 8] = b"CGINTN01"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ControlFailure { + Size, + Version, + Checksum, + Incarnation, + Sequence, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct SlotFact { + pub(crate) generation: u64, + pub(crate) payload_len: usize, + pub(crate) digest: [u8; 32], +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct CommitFact { + pub(crate) generation: u64, + pub(crate) active: usize, + pub(crate) slot_digest: [u8; 32], +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct IntentFact { + pub(crate) before: u64, + pub(crate) after: u64, + pub(crate) target: usize, + pub(crate) slot_digest: [u8; 32], +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum RecoveryStep { + Committed(CommitFact), + RewriteIntendedSlot(IntentFact), + CommitIntendedSlot(IntentFact), + Unknown, +} +fn number(bytes: &[u8]) -> u64 { + u64::from_le_bytes(bytes.try_into().expect("fixed codec field")) +} +fn finish(bytes: &mut [u8]) { + let end = bytes.len() - 32; + let digest = Sha256::digest(&bytes[..end]); + bytes[end..].copy_from_slice(&digest); +} +fn validate<'a>( + bytes: &'a [u8], + len: usize, + magic: &[u8; 8], + incarnation: &[u8; 32], +) -> Result<&'a [u8], ControlFailure> { + if bytes.len() != len { + return Err(ControlFailure::Size); + } + if &bytes[..8] != magic { + return Err(ControlFailure::Version); + } + if &bytes[8..40] != incarnation { + return Err(ControlFailure::Incarnation); + } + let end = len - 32; + if Sha256::digest(&bytes[..end]).as_slice() != &bytes[end..] { + return Err(ControlFailure::Checksum); + } + Ok(bytes) +} +/// Preparation only: caller owns/reserves both source and destination bytes. +pub(crate) fn encode_slot( + bytes: &mut [u8], + incarnation: &[u8; 32], + generation: u64, + payload: &[u8], +) -> Result { + if bytes.len() != SLOT_BYTES || payload.len() > PAYLOAD_BYTES { + return Err(ControlFailure::Size); + } + bytes.fill(0); + bytes[..8].copy_from_slice(SLOT_MAGIC); + bytes[8..40].copy_from_slice(incarnation); + bytes[40..48].copy_from_slice(&generation.to_le_bytes()); + bytes[48..56].copy_from_slice(&(payload.len() as u64).to_le_bytes()); + bytes[56..56 + payload.len()].copy_from_slice(payload); + finish(bytes); + decode_slot(bytes, incarnation) +} +pub(crate) fn decode_slot( + bytes: &[u8], + incarnation: &[u8; 32], +) -> Result { + let bytes = validate(bytes, SLOT_BYTES, SLOT_MAGIC, incarnation)?; + let payload_len = usize::try_from(number(&bytes[48..56])).map_err(|_| ControlFailure::Size)?; + if payload_len > PAYLOAD_BYTES + || bytes[56 + payload_len..SLOT_BYTES - 32] + .iter() + .any(|b| *b != 0) + { + return Err(ControlFailure::Size); + } + Ok(SlotFact { + generation: number(&bytes[40..48]), + payload_len, + digest: bytes[SLOT_BYTES - 32..].try_into().unwrap(), + }) +} +pub(crate) fn encode_commit( + bytes: &mut [u8], + incarnation: &[u8; 32], + fact: CommitFact, +) -> Result<(), ControlFailure> { + if bytes.len() != COMMIT_BYTES || fact.active > 1 { + return Err(ControlFailure::Size); + } + bytes.fill(0); + bytes[..8].copy_from_slice(COMMIT_MAGIC); + bytes[8..40].copy_from_slice(incarnation); + bytes[40..48].copy_from_slice(&fact.generation.to_le_bytes()); + bytes[48..56].copy_from_slice(&(fact.active as u64).to_le_bytes()); + bytes[56..88].copy_from_slice(&fact.slot_digest); + finish(bytes); + Ok(()) +} +pub(crate) fn decode_commit( + bytes: &[u8], + incarnation: &[u8; 32], +) -> Result { + let bytes = validate(bytes, COMMIT_BYTES, COMMIT_MAGIC, incarnation)?; + let active = usize::try_from(number(&bytes[48..56])).map_err(|_| ControlFailure::Size)?; + if active > 1 || bytes[88..96].iter().any(|b| *b != 0) { + return Err(ControlFailure::Size); + } + Ok(CommitFact { + generation: number(&bytes[40..48]), + active, + slot_digest: bytes[56..88].try_into().unwrap(), + }) +} +pub(crate) fn encode_intent( + bytes: &mut [u8], + incarnation: &[u8; 32], + current: CommitFact, + next_slot: &[u8], +) -> Result { + if bytes.len() != INTENT_BYTES || current.active > 1 { + return Err(ControlFailure::Size); + } + let next = decode_slot(next_slot, incarnation)?; + if current.generation.checked_add(1) != Some(next.generation) { + return Err(ControlFailure::Sequence); + } + bytes.fill(0); + bytes[..8].copy_from_slice(INTENT_MAGIC); + bytes[8..40].copy_from_slice(incarnation); + bytes[40..48].copy_from_slice(¤t.generation.to_le_bytes()); + bytes[48..56].copy_from_slice(&next.generation.to_le_bytes()); + bytes[56..64].copy_from_slice(&((1 - current.active) as u64).to_le_bytes()); + bytes[64..96].copy_from_slice(&next.digest); + bytes[96..96 + SLOT_BYTES].copy_from_slice(next_slot); + finish(bytes); + decode_intent(bytes, incarnation) +} +pub(crate) fn decode_intent( + bytes: &[u8], + incarnation: &[u8; 32], +) -> Result { + let bytes = validate(bytes, INTENT_BYTES, INTENT_MAGIC, incarnation)?; + let before = number(&bytes[40..48]); + let after = number(&bytes[48..56]); + let target = usize::try_from(number(&bytes[56..64])).map_err(|_| ControlFailure::Size)?; + let slot = decode_slot(&bytes[96..96 + SLOT_BYTES], incarnation)?; + if before.checked_add(1) != Some(after) + || slot.generation != after + || target > 1 + || slot.digest.as_slice() != &bytes[64..96] + { + return Err(ControlFailure::Sequence); + } + Ok(IntentFact { + before, + after, + target, + slot_digest: slot.digest, + }) +} +/// All checksum/codec work is preparation outside actual authority. The later +/// admitted step compares exact prepared bytes/stamps and makes one own effect. +pub(crate) fn recovery_step( + commit: Option, + intent: Option, + slots: [Option; 2], +) -> RecoveryStep { + if commit.is_some_and(|c| c.active > 1) + || intent.is_some_and(|i| i.target > 1 || i.before.checked_add(1) != Some(i.after)) + { + return RecoveryStep::Unknown; + } + let matches = |index: usize, generation: u64, digest: [u8; 32]| { + slots.get(index).is_some_and(|slot| { + slot.is_some_and(|s| s.generation == generation && s.digest == digest) + }) + }; + match (commit, intent) { + (Some(c), None) if matches(c.active, c.generation, c.slot_digest) => { + RecoveryStep::Committed(c) + } + (Some(c), Some(i)) + if c.generation == i.after + && c.active == i.target + && c.slot_digest == i.slot_digest + && matches(c.active, c.generation, c.slot_digest) => + { + RecoveryStep::Committed(c) + } + (c, Some(i)) + if c.is_none() + || c.is_some_and(|c| { + c.generation == i.before + && c.active == 1 - i.target + && matches(c.active, c.generation, c.slot_digest) + }) => + { + if matches(i.target, i.after, i.slot_digest) { + RecoveryStep::CommitIntendedSlot(i) + } else { + RecoveryStep::RewriteIntendedSlot(i) + } + } + _ => RecoveryStep::Unknown, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::maintenance_budget::CleanupMemory; + + #[test] + fn dc_control_codec_reserves_before_bytes_and_preserves_full_intended_slot() { + let capacity = 2 * SLOT_BYTES + INTENT_BYTES + COMMIT_BYTES; + let memory = CleanupMemory::new(capacity + 1024).unwrap(); + let baseline = memory.used(); + // The account's own exact allocation and caller's bookkeeping floor + // share this ceiling; neither is available again to buffer reserves. + let floor = memory + .reserve(memory.limit() - baseline - capacity) + .unwrap(); + let mut old = memory + .reserve(SLOT_BYTES) + .unwrap() + .bytes(SLOT_BYTES) + .unwrap(); + let mut next = memory + .reserve(SLOT_BYTES) + .unwrap() + .bytes(SLOT_BYTES) + .unwrap(); + let mut intent = memory + .reserve(INTENT_BYTES) + .unwrap() + .bytes(INTENT_BYTES) + .unwrap(); + let mut commit = memory + .reserve(COMMIT_BYTES) + .unwrap() + .bytes(COMMIT_BYTES) + .unwrap(); + let nonce = [17; 32]; + let old_fact = encode_slot(old.as_mut_slice(), &nonce, 12, b"old").unwrap(); + let next_fact = + encode_slot(next.as_mut_slice(), &nonce, 13, b"complete next payload").unwrap(); + let current = CommitFact { + generation: 12, + active: 0, + slot_digest: old_fact.digest, + }; + encode_commit(commit.as_mut_slice(), &nonce, current).unwrap(); + assert_eq!(decode_commit(commit.as_slice(), &nonce).unwrap(), current); + let fact = encode_intent(intent.as_mut_slice(), &nonce, current, next.as_slice()).unwrap(); + assert_eq!(&intent.as_slice()[96..96 + SLOT_BYTES], next.as_slice()); + assert_eq!(fact.after, next_fact.generation); + assert_eq!(memory.used(), memory.limit()); + assert!(memory.reserve(1).is_err()); + drop((old, next, intent, commit, floor)); + assert_eq!(memory.used(), baseline); + } + + #[test] + fn dc_control_independent_regions_recover_every_partial_write_cut() { + let nonce = [21; 32]; + let mut old = [0; SLOT_BYTES]; + let mut next = [0; SLOT_BYTES]; + let old_slot = encode_slot(&mut old, &nonce, 3, b"old committed value").unwrap(); + let next_slot = + encode_slot(&mut next, &nonce, 4, b"new exact reconstructible value").unwrap(); + let current = CommitFact { + generation: 3, + active: 0, + slot_digest: old_slot.digest, + }; + let mut old_commit = [0; COMMIT_BYTES]; + encode_commit(&mut old_commit, &nonce, current).unwrap(); + let mut intent = [0; INTENT_BYTES]; + let i = encode_intent(&mut intent, &nonce, current, &next).unwrap(); + let mut selector = [0; SELECTOR_BYTES]; + selector[..COMMIT_BYTES].copy_from_slice(&old_commit); + for cut in 0..=INTENT_BYTES { + selector[COMMIT_BYTES..].fill(0); + selector[COMMIT_BYTES..COMMIT_BYTES + cut].copy_from_slice(&intent[..cut]); + // Writing the independent Intent can NEVER destroy old Commit. + assert_eq!( + decode_commit(&selector[..COMMIT_BYTES], &nonce).unwrap(), + current + ); + let observed = decode_intent(&selector[COMMIT_BYTES..], &nonce).ok(); + let step = recovery_step(Some(current), observed, [Some(old_slot), None]); + assert_eq!( + step, + if observed.is_some() { + RecoveryStep::RewriteIntendedSlot(i) + } else { + RecoveryStep::Committed(current) + } + ); + } + for cut in 0..=SLOT_BYTES { + let mut partial = [0; SLOT_BYTES]; + partial[..cut].copy_from_slice(&next[..cut]); + let slot = decode_slot(&partial, &nonce).ok(); + assert_eq!( + recovery_step(Some(current), Some(i), [Some(old_slot), slot]), + if slot == Some(next_slot) { + RecoveryStep::CommitIntendedSlot(i) + } else { + RecoveryStep::RewriteIntendedSlot(i) + } + ); + } + let final_commit = CommitFact { + generation: 4, + active: 1, + slot_digest: next_slot.digest, + }; + let mut commit = [0; COMMIT_BYTES]; + encode_commit(&mut commit, &nonce, final_commit).unwrap(); + selector[COMMIT_BYTES..].copy_from_slice(&intent); + for cut in 0..=COMMIT_BYTES { + selector[..COMMIT_BYTES].copy_from_slice(&old_commit); + selector[..cut].copy_from_slice(&commit[..cut]); + assert_eq!(decode_intent(&selector[COMMIT_BYTES..], &nonce).unwrap(), i); + let observed = decode_commit(&selector[..COMMIT_BYTES], &nonce).ok(); + let step = recovery_step(observed, Some(i), [Some(old_slot), Some(next_slot)]); + assert_eq!( + step, + if observed == Some(final_commit) { + RecoveryStep::Committed(final_commit) + } else { + RecoveryStep::CommitIntendedSlot(i) + } + ); + } + } + + #[test] + fn dc_control_invalid_witnesses_and_unrelated_generations_stay_unknown() { + let nonce = [9; 32]; + let mut slot = [0; SLOT_BYTES]; + let s = encode_slot(&mut slot, &nonce, u64::MAX, b"last version").unwrap(); + let c = CommitFact { + generation: u64::MAX, + active: 0, + slot_digest: s.digest, + }; + let mut intent = [0; INTENT_BYTES]; + assert_eq!( + encode_intent(&mut intent, &nonce, c, &slot), + Err(ControlFailure::Sequence) + ); + assert_eq!( + decode_slot(&slot, &[8; 32]), + Err(ControlFailure::Incarnation) + ); + for index in [0, 7, 8, 39, 40, 55, 56, SLOT_BYTES - 1] { + let mut corrupt = slot; + corrupt[index] ^= 1; + assert!(decode_slot(&corrupt, &nonce).is_err()); + } + assert_eq!( + recovery_step(None, None, [Some(s), Some(s)]), + RecoveryStep::Unknown + ); + let foreign = CommitFact { + generation: 99, + active: 0, + slot_digest: s.digest, + }; + let i = IntentFact { + before: 7, + after: 8, + target: 1, + slot_digest: s.digest, + }; + assert_eq!( + recovery_step(Some(foreign), Some(i), [Some(s), Some(s)]), + RecoveryStep::Unknown + ); + assert_eq!( + decode_slot(&slot[..SLOT_BYTES - 1], &nonce), + Err(ControlFailure::Size) + ); + let mut too_small = [0; SLOT_BYTES - 1]; + assert_eq!( + encode_slot(&mut too_small, &nonce, 0, b""), + Err(ControlFailure::Size) + ); + } +} + +pub(crate) const BINDING_BYTES: usize = 512; +pub(crate) const DATA_NAMES: [&[u8]; 2] = [b".publication.data0", b".publication.data1"]; +pub(crate) const SELECTOR_NAME: &[u8] = b".publication.selector"; +pub(crate) const STREAM_NAME: &[u8] = b".publication.stream"; +pub(crate) const BINDING_NAME: &[u8] = b".publication.binding"; +const BINDING_MAGIC: &[u8; 8] = b"CGBIND01"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct PublisherBinding { + pub(crate) root: super::locks::FileIdentity, + pub(crate) coordination: super::locks::FileIdentity, + pub(crate) data: [super::locks::FileIdentity; 2], + pub(crate) selector: super::locks::FileIdentity, + pub(crate) stream: Option, + pub(crate) incarnation: [u8; 32], +} +fn identity_into(bytes: &mut [u8], identity: super::locks::FileIdentity) { + let (volume, index) = identity.fixed_words(); + bytes[..8].copy_from_slice(&volume.to_le_bytes()); + bytes[8..16].copy_from_slice(&index.to_le_bytes()); +} +fn identity_from(bytes: &[u8]) -> super::locks::FileIdentity { + super::locks::FileIdentity::from_fixed_words(number(&bytes[..8]), number(&bytes[8..16])) +} +impl PublisherBinding { + pub(crate) fn encode(&self, bytes: &mut [u8]) -> Result<(), ControlFailure> { + if bytes.len() != BINDING_BYTES { + return Err(ControlFailure::Size); + } + bytes.fill(0); + bytes[..8].copy_from_slice(BINDING_MAGIC); + bytes[8..16].copy_from_slice(&1u64.to_le_bytes()); + bytes[16..48].copy_from_slice(&self.incarnation); + for (offset, identity) in [ + (48, self.root), + (64, self.coordination), + (80, self.data[0]), + (96, self.data[1]), + (112, self.selector), + ] { + identity_into(&mut bytes[offset..offset + 16], identity); + } + if let Some(stream) = self.stream { + bytes[128..136].copy_from_slice(&1u64.to_le_bytes()); + identity_into(&mut bytes[136..152], stream); + } + finish(bytes); + Ok(()) + } + pub(crate) fn decode(bytes: &[u8]) -> Result { + if bytes.len() != BINDING_BYTES { + return Err(ControlFailure::Size); + } + if &bytes[..8] != BINDING_MAGIC || number(&bytes[8..16]) != 1 { + return Err(ControlFailure::Version); + } + if Sha256::digest(&bytes[..BINDING_BYTES - 32]).as_slice() != &bytes[BINDING_BYTES - 32..] { + return Err(ControlFailure::Checksum); + } + if bytes[152..BINDING_BYTES - 32].iter().any(|b| *b != 0) { + return Err(ControlFailure::Size); + } + let stream = match number(&bytes[128..136]) { + 0 if bytes[136..152].iter().all(|b| *b == 0) => None, + 1 => Some(identity_from(&bytes[136..152])), + _ => return Err(ControlFailure::Version), + }; + Ok(Self { + root: identity_from(&bytes[48..64]), + coordination: identity_from(&bytes[64..80]), + data: [ + identity_from(&bytes[80..96]), + identity_from(&bytes[96..112]), + ], + selector: identity_from(&bytes[112..128]), + stream, + incarnation: bytes[16..48].try_into().unwrap(), + }) + } +} + +/// Actual simultaneous PRIMARY factory storage. No heap/account is hidden in +/// this type. Cleanup preparation separately reserves every concurrent image. +pub(crate) struct PublicationFactoryScratch { + slot: [u8; SLOT_BYTES], + selector: [u8; SELECTOR_BYTES], + binding: [u8; BINDING_BYTES], + readback: [u8; SELECTOR_BYTES], +} +impl PublicationFactoryScratch { + pub(crate) fn new() -> Self { + Self { + slot: [0; SLOT_BYTES], + selector: [0; SELECTOR_BYTES], + binding: [0; BINDING_BYTES], + readback: [0; SELECTOR_BYTES], + } + } +} +fn factory_write( + root: &super::locks::RootedDirectory, + name: &[u8], + bytes: &[u8], + readback: &mut [u8], +) -> std::io::Result { + use std::io::{Read, Seek, SeekFrom, Write}; + let mut file = root.create_new(name)?; + file.write_all(bytes)?; + file.sync_all()?; + if file.metadata()?.len() != bytes.len() as u64 { + return Err(invalid_control_io()); + } + file.seek(SeekFrom::Start(0))?; + file.read_exact(&mut readback[..bytes.len()])?; + if &readback[..bytes.len()] != bytes { + return Err(invalid_control_io()); + } + let identity = super::locks::file_identity(&file).map_err(control_native_io)?; + let named = root.open_existing(name)?; + if super::locks::file_identity(&named).map_err(control_native_io)? != identity { + return Err(invalid_control_io()); + } + Ok(identity) +} +fn control_native_io(error: crate::error::NativeError) -> std::io::Error { + match error { + crate::error::NativeError::Io(error) => error, + _ => invalid_control_io(), + } +} +/// The only caller holds genuine fresh-root evidence minted in the actual +/// create_dir success branch. Existing producer/acquisition has no constructor. +/// An error leaves finite unknown names; callers keep primary work optional. +pub(crate) fn initialize_publication_controls( + fresh: &super::maintenance_ownership::FreshPublicationRoot<'_>, + scratch: &mut PublicationFactoryScratch, +) -> std::io::Result { + initialize_creation_controls(fresh, scratch, b"CGBUILD1") +} +pub(super) fn initialize_creation_controls( + fresh: &super::maintenance_ownership::FreshPublicationRoot<'_>, + scratch: &mut PublicationFactoryScratch, + initial: &[u8], +) -> std::io::Result { + let root = fresh.anchor(); + let nonce = fresh.incarnation(); + let first = + encode_slot(&mut scratch.slot, &nonce, 0, initial).map_err(|_| invalid_control_io())?; + let data0 = factory_write(root, DATA_NAMES[0], &scratch.slot, &mut scratch.readback)?; + encode_slot(&mut scratch.slot, &nonce, 0, b"").map_err(|_| invalid_control_io())?; + let data1 = factory_write(root, DATA_NAMES[1], &scratch.slot, &mut scratch.readback)?; + scratch.selector.fill(0); + encode_commit( + &mut scratch.selector[..COMMIT_BYTES], + &nonce, + CommitFact { + generation: 0, + active: 0, + slot_digest: first.digest, + }, + ) + .map_err(|_| invalid_control_io())?; + let selector = factory_write( + root, + SELECTOR_NAME, + &scratch.selector, + &mut scratch.readback, + )?; + // Empty initial stream proves one inode only, no journal/source/version. + let stream = factory_write(root, STREAM_NAME, b"", &mut scratch.readback)?; + root.sync_namespace()?; + let binding = PublisherBinding { + root: root.identity(), + coordination: fresh.coordination_identity()?, + data: [data0, data1], + selector, + stream: Some(stream), + incarnation: nonce, + }; + binding + .encode(&mut scratch.binding) + .map_err(|_| invalid_control_io())?; + factory_write(root, BINDING_NAME, &scratch.binding, &mut scratch.readback)?; + root.sync_namespace()?; + Ok(binding) +} + +/// Ordinary PRODUCER publication namespace ownership. This is never a janitor +/// mutation seal; its preparation/revalidation may parse fixed control codecs. +pub(crate) struct RootPublicationLease<'a, 'root> { + selector: super::locks::FixedLockedFile, + directory: super::locks::RootedDirectory, + capability: &'a mut (dyn super::maintenance_ownership::PublicationRootAuthority + 'root), + binding: PublisherBinding, + binding_identity: super::locks::FileIdentity, + commit: CommitFact, + usable: bool, +} +impl RootPublicationLease<'_, '_> { + #[cfg(test)] + pub(crate) fn directory(&self) -> &super::locks::RootedDirectory { + &self.directory + } + #[cfg(test)] + pub(crate) fn root(&self) -> &std::path::Path { + self.capability.root() + } + pub(crate) fn binding(&self) -> &PublisherBinding { + &self.binding + } + #[cfg(test)] + pub(crate) fn committed(&self) -> CommitFact { + self.commit + } + pub(crate) fn publication_binding_identity(&self) -> super::locks::FileIdentity { + self.binding_identity + } + pub(crate) fn root_cohort_identity(&self) -> super::locks::FileIdentity { + self.capability.cohort_identity() + } + /// Producer-only read of the exact committed fixed payload. Domain codecs + /// must reject unknown/nonterminal state before choosing a new operation. + pub(crate) fn read_primary_control( + &self, + output: &mut [u8; PAYLOAD_BYTES], + ) -> Result { + if !self.revalidate_existing()? { + return Err(invalid_control_io().into()); + } + let file = self + .directory + .open_readonly_file(DATA_NAMES[self.commit.active])?; + if super::locks::file_identity(&file)? != self.binding.data[self.commit.active] { + return Err(invalid_control_io().into()); + } + let image = read_exact_image::(&file)?; + let fact = + decode_slot(&image, &self.binding.incarnation).map_err(|_| invalid_control_io())?; + if fact.generation != self.commit.generation || fact.digest != self.commit.slot_digest { + return Err(invalid_control_io().into()); + } + output.fill(0); + output[..fact.payload_len].copy_from_slice(&image[56..56 + fact.payload_len]); + Ok(fact.payload_len) + } + pub(crate) fn revalidate_existing(&self) -> Result { + if !self.usable || !self.capability.revalidate()? { + return Ok(false); + } + let named = self.directory.open_readonly_file(SELECTOR_NAME)?; + let binding = self.directory.open_readonly_file(BINDING_NAME)?; + Ok( + super::locks::file_identity(&binding)? == self.binding_identity + && read_exact_image::(&binding) + .ok() + .and_then(|bytes| PublisherBinding::decode(&bytes).ok()) + == Some(self.binding) + && super::locks::file_identity(&named)? == self.binding.selector + && super::locks::file_identity(self.selector.file())? == self.binding.selector + && read_exact_image::(self.selector.file()) + .ok() + .and_then(|bytes| { + decode_commit(&bytes[..COMMIT_BYTES], &self.binding.incarnation).ok() + }) + == Some(self.commit) + && self.selector.mode() == super::locks::LockMode::Exclusive + && self.directory.identity() == self.binding.root, + ) + } +} +fn read_exact_image(file: &std::fs::File) -> std::io::Result<[u8; N]> { + use std::io::{Read, Seek, SeekFrom}; + if file.metadata()?.len() != N as u64 { + return Err(invalid_control_io()); + } + let stamp = super::locks::FixedFileStamp::capture(file, super::locks::FixedControlKind::File) + .map_err(|_| invalid_control_io())?; + let mut bytes = [0; N]; + let mut cursor = file; + cursor.seek(SeekFrom::Start(0))?; + cursor.read_exact(&mut bytes)?; + stamp.recheck(file).map_err(|_| invalid_control_io())?; + Ok(bytes) +} +/// No creation, repair, adoption or new publisher on an existing root. Missing, +/// incomplete or noncommitted namespace returns no optional attestation. The +/// caller must retain its approved safe untracked primary-publication lanes. +pub(crate) fn try_begin_root_publication<'a, 'root>( + capability: &'a mut super::maintenance_ownership::ArtifactRootProducerCapability<'root>, +) -> Result>, crate::error::NativeError> { + try_begin_publication(capability) +} +pub(super) fn try_begin_publication<'a, 'root>( + capability: &'a mut (dyn super::maintenance_ownership::PublicationRootAuthority + 'root), +) -> Result>, crate::error::NativeError> { + if !capability.revalidate()? { + return Ok(None); + } + let directory = + super::locks::RootedDirectory::open(capability.root(), capability.root_identity())?; + let binding_file = match directory.open_readonly_file(BINDING_NAME) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(error.into()), + }; + let binding_bytes = match read_exact_image::(&binding_file) { + Ok(bytes) => bytes, + Err(_) => return Ok(None), + }; + let binding = match PublisherBinding::decode(&binding_bytes) { + Ok(binding) => binding, + Err(_) => return Ok(None), + }; + if binding.root != capability.root_identity() + || binding.coordination != capability.coordination_identity() + || binding.incarnation != capability.incarnation() + { + return Ok(None); + } + let selector_file = directory.open_existing(SELECTOR_NAME)?; + if super::locks::file_identity(&selector_file)? != binding.selector { + return Ok(None); + } + let Some(selector) = + super::locks::FixedLockedFile::try_lock(selector_file, super::locks::LockMode::Exclusive) + .map_err(|error| match error { + super::locks::FixedControlError::Io(error) => crate::error::NativeError::Io(error), + _ => crate::error::NativeError::Io(invalid_control_io()), + })? + else { + return Ok(None); + }; + let named_binding = directory.open_readonly_file(BINDING_NAME)?; + if super::locks::file_identity(&named_binding)? != super::locks::file_identity(&binding_file)? + || read_exact_image::(&named_binding) + .ok() + .as_ref() + != Some(&binding_bytes) + { + return Ok(None); + } + let selector_bytes = match read_exact_image::(selector.file()) { + Ok(bytes) => bytes, + Err(_) => return Ok(None), + }; + let mut slots = [None; 2]; + for index in 0..2 { + let file = directory.open_readonly_file(DATA_NAMES[index])?; + if super::locks::file_identity(&file)? != binding.data[index] { + return Ok(None); + } + let image = match read_exact_image::(&file) { + Ok(image) => image, + Err(_) => return Ok(None), + }; + slots[index] = decode_slot(&image, &binding.incarnation).ok(); + } + if let Some(expected) = binding.stream { + let stream = directory.open_readonly_file(STREAM_NAME)?; + if super::locks::file_identity(&stream)? != expected { + return Ok(None); + } + } + let commit = decode_commit(&selector_bytes[..COMMIT_BYTES], &binding.incarnation).ok(); + let intent = decode_intent(&selector_bytes[COMMIT_BYTES..], &binding.incarnation).ok(); + let RecoveryStep::Committed(commit) = recovery_step(commit, intent, slots) else { + return Ok(None); + }; + let binding_identity = super::locks::file_identity(&binding_file)?; + let lease = RootPublicationLease { + selector, + directory, + capability, + binding, + binding_identity, + commit, + usable: true, + }; + if !lease.revalidate_existing()? { + return Ok(None); + } + Ok(Some(lease)) +} + +/// Primary-operation fixed images; no heap/second cleanup account. A janitor +/// prepares equivalent borrowed slices from its SAME accounted offslot Work. +pub(crate) struct PrimaryPublicationScratch { + slot: [u8; SLOT_BYTES], + intent: [u8; INTENT_BYTES], + commit: [u8; COMMIT_BYTES], + readback: [u8; SELECTOR_BYTES], +} +impl PrimaryPublicationScratch { + pub(crate) fn new() -> Self { + Self { + slot: [0; SLOT_BYTES], + intent: [0; INTENT_BYTES], + commit: [0; COMMIT_BYTES], + readback: [0; SELECTOR_BYTES], + } + } +} +fn primary_range( + file: &std::fs::File, + offset: u64, + bytes: &[u8], + readback: &mut [u8], +) -> std::io::Result<()> { + use std::io::{Read, Seek, SeekFrom, Write}; + let mut cursor = file; + cursor.seek(SeekFrom::Start(offset))?; + cursor.write_all(bytes)?; + cursor.sync_all()?; + cursor.seek(SeekFrom::Start(offset))?; + cursor.read_exact(&mut readback[..bytes.len()])?; + if &readback[..bytes.len()] != bytes { + return Err(invalid_control_io()); + } + Ok(()) +} +impl RootPublicationLease<'_, '_> { + /// Ordinary producer only, outside janitor authority/budget. Publish a fixed + /// creation/progress intent before the corresponding S3 namespace effect. + /// The complete next slot is durable in Intent; each region preserves its + /// independent opposite witness. An interrupted update requires recovery; + /// this method never silently adopts a newer generation. + pub(crate) fn publish_primary_control( + &mut self, + payload: &[u8], + scratch: &mut PrimaryPublicationScratch, + ) -> Result { + if !self.revalidate_existing()? { + return Err(invalid_control_io().into()); + } + let selector = read_exact_image::(self.selector.file())?; + if decode_commit(&selector[..COMMIT_BYTES], &self.binding.incarnation).ok() + != Some(self.commit) + { + return Err(invalid_control_io().into()); + } + let generation = self + .commit + .generation + .checked_add(1) + .ok_or_else(invalid_control_io)?; + let slot = encode_slot( + &mut scratch.slot, + &self.binding.incarnation, + generation, + payload, + ) + .map_err(|_| invalid_control_io())?; + let intent = encode_intent( + &mut scratch.intent, + &self.binding.incarnation, + self.commit, + &scratch.slot, + ) + .map_err(|_| invalid_control_io())?; + let next = CommitFact { + generation, + active: intent.target, + slot_digest: slot.digest, + }; + encode_commit(&mut scratch.commit, &self.binding.incarnation, next) + .map_err(|_| invalid_control_io())?; + let data = self.directory.open_existing(DATA_NAMES[next.active])?; + if super::locks::file_identity(&data)? != self.binding.data[next.active] + || data.metadata()?.len() != SLOT_BYTES as u64 + { + return Err(invalid_control_io().into()); + } + // Any following error requires exact recovery under a fresh lease; + // an old live object cannot replace a partially published Intent. + self.usable = false; + primary_range( + self.selector.file(), + COMMIT_BYTES as u64, + &scratch.intent, + &mut scratch.readback, + )?; + primary_range(&data, 0, &scratch.slot, &mut scratch.readback)?; + primary_range( + self.selector.file(), + 0, + &scratch.commit, + &mut scratch.readback, + )?; + self.commit = next; + self.usable = true; + Ok(next) + } +} + +#[cfg(test)] +mod factory_codec_tests { + use super::*; + use crate::storage::locks::FileIdentity; + #[test] + fn dc_control_binding_modes_are_explicit_and_reserve_unknown_bytes() { + let id = FileIdentity::from_fixed_words(5, 7); + let mut binding = PublisherBinding { + root: id, + coordination: id, + data: [id, id], + selector: id, + stream: None, + incarnation: [3; 32], + }; + let mut bytes = [0; BINDING_BYTES]; + binding.encode(&mut bytes).unwrap(); + assert_eq!(PublisherBinding::decode(&bytes).unwrap(), binding); + binding.stream = Some(FileIdentity::from_fixed_words(0, 0)); + binding.encode(&mut bytes).unwrap(); + assert_eq!( + PublisherBinding::decode(&bytes).unwrap().stream, + binding.stream + ); + bytes[128..136].copy_from_slice(&2u64.to_le_bytes()); + finish(&mut bytes); + assert!(PublisherBinding::decode(&bytes).is_err()); + binding.stream = None; + binding.encode(&mut bytes).unwrap(); + bytes[136] = 1; + finish(&mut bytes); + assert!(PublisherBinding::decode(&bytes).is_err()); + binding.encode(&mut bytes).unwrap(); + bytes[200] = 1; + finish(&mut bytes); + assert!(PublisherBinding::decode(&bytes).is_err()); + assert_eq!(std::mem::size_of::(), 7432); + } +} + +fn invalid_control_io() -> std::io::Error { + std::io::ErrorKind::InvalidData.into() +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub(crate) enum WorkspacePhase { + Building = 0, + NeedsRecovery = 1, + OwnerTerminal = 2, + Closing = 3, + Quiescent = 4, + Retiring = 5, + Removing = 6, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub(crate) enum WorkspaceDisposition { + None = 0, + PublishedConsumed = 1, + AbortedNotPublished = 2, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct WorkspaceMetadataCreator { + identity: super::locks::FileIdentity, + operation: [u8; 32], + len: u64, + digest: [u8; 32], + receipt: Option, +} +const ROOT_METADATA_NAMES: [&[u8]; 3] = [ + b"journal.json", + b"cleanup-barrier.json", + b".reference-scope.json", +]; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct WorkspaceControl { + pub(crate) phase: WorkspacePhase, + pub(crate) disposition: WorkspaceDisposition, + pub(crate) source_epoch: u64, + pub(crate) admission_epoch: u64, + pub(crate) child_generation: u64, + pub(crate) registered: u64, + pub(crate) closed: u64, + pub(crate) sealed_source: u64, + metadata_seal: [u8; 32], + metadata_creators: [Option; 3], +} +impl WorkspaceControl { + pub(crate) fn building() -> Self { + Self { + phase: WorkspacePhase::Building, + disposition: WorkspaceDisposition::None, + source_epoch: 0, + admission_epoch: 0, + child_generation: 0, + registered: 0, + closed: 0, + sealed_source: 0, + metadata_seal: [0; 32], + metadata_creators: [None; 3], + } + } + pub(crate) fn encode(self) -> [u8; 456] { + let mut bytes = [0; 456]; + bytes[..8].copy_from_slice(b"CGWCTL01"); + bytes[8] = self.phase as u8; + bytes[9] = self.disposition as u8; + for (index, value) in [ + self.source_epoch, + self.admission_epoch, + self.child_generation, + self.registered, + self.closed, + self.sealed_source, + ] + .into_iter() + .enumerate() + { + bytes[16 + index * 8..24 + index * 8].copy_from_slice(&value.to_le_bytes()); + } + bytes[64..96].copy_from_slice(&self.metadata_seal); + for (i, creator) in self.metadata_creators.into_iter().enumerate() { + if let Some(c) = creator { + let base = 96 + i * 120; + bytes[base] = 1; + identity_into(&mut bytes[base + 8..base + 24], c.identity); + bytes[base + 24..base + 56].copy_from_slice(&c.operation); + bytes[base + 56..base + 64].copy_from_slice(&c.len.to_le_bytes()); + bytes[base + 64..base + 96].copy_from_slice(&c.digest); + if let Some(id) = c.receipt { + bytes[base + 96] = 1; + identity_into(&mut bytes[base + 104..base + 120], id); + } + } + } + bytes + } + pub(crate) fn decode(bytes: &[u8]) -> Result { + if bytes.len() != 456 || &bytes[..8] != b"CGWCTL01" || bytes[10..16].iter().any(|b| *b != 0) + { + return Err(ControlFailure::Version); + } + let phase = match bytes[8] { + 0 => WorkspacePhase::Building, + 1 => WorkspacePhase::NeedsRecovery, + 2 => WorkspacePhase::OwnerTerminal, + 3 => WorkspacePhase::Closing, + 4 => WorkspacePhase::Quiescent, + 5 => WorkspacePhase::Retiring, + 6 => WorkspacePhase::Removing, + _ => return Err(ControlFailure::Version), + }; + let disposition = match bytes[9] { + 0 => WorkspaceDisposition::None, + 1 => WorkspaceDisposition::PublishedConsumed, + 2 => WorkspaceDisposition::AbortedNotPublished, + _ => return Err(ControlFailure::Version), + }; + let mut numbers = [0; 6]; + for (index, value) in numbers.iter_mut().enumerate() { + *value = number(&bytes[16 + index * 8..24 + index * 8]); + } + if numbers[4] > numbers[3] + || (matches!( + phase, + WorkspacePhase::Building | WorkspacePhase::NeedsRecovery + ) && disposition != WorkspaceDisposition::None) + || (matches!( + phase, + WorkspacePhase::OwnerTerminal + | WorkspacePhase::Closing + | WorkspacePhase::Quiescent + | WorkspacePhase::Retiring + | WorkspacePhase::Removing + ) && disposition == WorkspaceDisposition::None) + { + return Err(ControlFailure::Sequence); + } + Ok(Self { + phase, + disposition, + source_epoch: numbers[0], + admission_epoch: numbers[1], + child_generation: numbers[2], + registered: numbers[3], + closed: numbers[4], + sealed_source: numbers[5], + metadata_seal: bytes[64..96].try_into().unwrap(), + metadata_creators: { + let mut result = [None; 3]; + for (i, out) in result.iter_mut().enumerate() { + let base = 96 + i * 120; + let entry = &bytes[base..base + 120]; + if entry[0] == 0 { + if entry.iter().any(|x| *x != 0) { + return Err(ControlFailure::Version); + } + } else { + if entry[0] != 1 + || entry[1..8].iter().chain(&entry[97..104]).any(|x| *x != 0) + || entry[96] > 1 + || (entry[96] == 0 && entry[104..120].iter().any(|x| *x != 0)) + { + return Err(ControlFailure::Version); + } + *out = Some(WorkspaceMetadataCreator { + identity: identity_from(&entry[8..24]), + operation: entry[24..56].try_into().unwrap(), + len: number(&entry[56..64]), + digest: entry[64..96].try_into().unwrap(), + receipt: (entry[96] == 1).then(|| identity_from(&entry[104..120])), + }); + } + } + result + }, + }) + } +} + +fn workspace_metadata_seal( + root: &std::path::Path, + id: super::locks::FileIdentity, +) -> Result<[u8; 32], crate::error::NativeError> { + let directory = super::locks::RootedDirectory::open_readonly_root(root, id)?; + let mut hash = Sha256::new(); + hash.update(b"CGW-METADATA-01"); + for name in [ + b".workspace.binding".as_slice(), + b".workspace.domain.json", + b".workspace.parent.json", + b".reference-scope.json", + b"journal.json", + b"cleanup-barrier.json", + b".publication.binding", + ] { + hash.update(name); + match directory.open_readonly_file(name) { + Ok(file) => { + let stamp = super::locks::FixedFileStamp::capture( + &file, + super::locks::FixedControlKind::File, + ) + .map_err(|_| invalid_control_io())?; + if stamp.len() > 8 * 1024 * 1024 { + return Err(invalid_control_io().into()); + } + hash.update([1]); + let (volume, index) = stamp.identity().fixed_words(); + hash.update(volume.to_le_bytes()); + hash.update(index.to_le_bytes()); + hash.update(stamp.len().to_le_bytes()); + let mut bytes = [0u8; 8192]; + let mut offset = 0u64; + while offset < stamp.len() { + let len = (stamp.len() - offset).min(bytes.len() as u64) as usize; + super::locks::read_fixed_range(&file, offset, &mut bytes[..len])?; + hash.update(&bytes[..len]); + offset += len as u64; + } + stamp.recheck(&file).map_err(|_| invalid_control_io())?; + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => hash.update([0]), + Err(error) => return Err(error.into()), + } + } + Ok(hash.finalize().into()) +} + +const WORKSPACE_SCOPE_NAME: &[u8] = b".workspace.binding"; +const WORKSPACE_DOMAIN_NAME: &[u8] = b".workspace.domain.json"; +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +struct WorkspaceDirectoryIncarnation { + identity: super::locks::FileIdentity, + nonce: [u8; 32], +} +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +struct WorkspaceBinding { + root: super::locks::FileIdentity, + owner: super::locks::FileIdentity, + registry: super::locks::FileIdentity, + parent_root: super::locks::FileIdentity, + parent_coordination: super::locks::FileIdentity, + parent_cohort: super::locks::FileIdentity, + publication_binding: super::locks::FileIdentity, + nonce: [u8; 32], + parent_nonce: [u8; 32], + kind: u8, + writers: super::locks::FileIdentity, + origins: super::locks::FileIdentity, + parent_record: Option, + startup_gate: super::locks::FileIdentity, + created_directories: Option<[WorkspaceDirectoryIncarnation; 2]>, + origins_nonce: [u8; 32], +} +impl WorkspaceBinding { + fn encode(self) -> [u8; 512] { + let mut bytes = [0u8; 512]; + bytes[..8].copy_from_slice(b"CGWSCP01"); + bytes[8] = self.kind; + if let Some(parent) = self.parent_record { + bytes[9] = 1; + identity_into(&mut bytes[224..240], parent); + } + identity_into(&mut bytes[240..256], self.startup_gate); + if let Some(directories) = self.created_directories { + bytes[10] = 1; + identity_into(&mut bytes[256..272], directories[0].identity); + bytes[288..320].copy_from_slice(&directories[0].nonce); + identity_into(&mut bytes[272..288], directories[1].identity); + bytes[320..352].copy_from_slice(&directories[1].nonce); + } + for (index, id) in [ + self.root, + self.owner, + self.registry, + self.parent_root, + self.parent_coordination, + self.parent_cohort, + self.publication_binding, + ] + .into_iter() + .enumerate() + { + identity_into(&mut bytes[16 + index * 16..32 + index * 16], id); + } + bytes[128..160].copy_from_slice(&self.nonce); + bytes[160..192].copy_from_slice(&self.parent_nonce); + identity_into(&mut bytes[192..208], self.writers); + identity_into(&mut bytes[208..224], self.origins); + bytes[352..384].copy_from_slice(&self.origins_nonce); + finish(&mut bytes); + bytes + } + fn decode(bytes: &[u8]) -> Result { + if bytes.len() != 512 + || &bytes[..8] != b"CGWSCP01" + || !matches!(bytes[8], 1..=3) + || bytes[9] > 1 + || bytes[10] > 1 + || bytes[11..16] + .iter() + .chain(&bytes[384..480]) + .any(|b| *b != 0) + || (bytes[10] == 0 && bytes[256..352].iter().any(|b| *b != 0)) + || (bytes[8] == 1 && bytes[10] != 1) + || (bytes[9] == 0 && bytes[224..240].iter().any(|b| *b != 0)) + || (bytes[8] == 3 && bytes[9] != 1) + || Sha256::digest(&bytes[..480]).as_slice() != &bytes[480..] + { + return Err(ControlFailure::Version); + } + Ok(Self { + kind: bytes[8], + root: identity_from(&bytes[16..32]), + owner: identity_from(&bytes[32..48]), + registry: identity_from(&bytes[48..64]), + parent_root: identity_from(&bytes[64..80]), + parent_coordination: identity_from(&bytes[80..96]), + parent_cohort: identity_from(&bytes[96..112]), + publication_binding: identity_from(&bytes[112..128]), + nonce: bytes[128..160].try_into().unwrap(), + parent_nonce: bytes[160..192].try_into().unwrap(), + writers: identity_from(&bytes[192..208]), + origins: identity_from(&bytes[208..224]), + parent_record: (bytes[9] == 1).then(|| identity_from(&bytes[224..240])), + startup_gate: identity_from(&bytes[240..256]), + origins_nonce: bytes[352..384].try_into().unwrap(), + created_directories: (bytes[10] == 1).then(|| { + [ + WorkspaceDirectoryIncarnation { + identity: identity_from(&bytes[256..272]), + nonce: bytes[288..320].try_into().unwrap(), + }, + WorkspaceDirectoryIncarnation { + identity: identity_from(&bytes[272..288]), + nonce: bytes[320..352].try_into().unwrap(), + }, + ] + }), + }) + } +} +#[derive(serde::Serialize, serde::Deserialize)] +struct WorkspaceParentRecord { + root: std::path::PathBuf, + scope: WorkspaceBinding, + scope_file: super::locks::FileIdentity, +} +/// Factory provenance only. Operations still borrow actual live owner/activity +/// and startup gates; this object cannot itself lock, start or delete payloads. +#[derive(Debug)] +pub(crate) struct WorkspaceRegistration { + root: std::path::PathBuf, + gate: std::path::PathBuf, + scope: WorkspaceBinding, + scope_file: super::locks::FileIdentity, + domain: super::maintenance_ownership::ReferenceDomain, +} +impl WorkspaceRegistration { + pub(crate) fn create( + fresh: &super::layout::FreshReferenceRoot, + domain: &super::maintenance_ownership::ReferenceDomain, + mutation: &super::maintenance_ownership::ReferenceMutation<'_>, + owner: &super::locks::LockedFile, + kind: u8, + ) -> Result, crate::error::NativeError> { + Self::create_with_parent(fresh, domain, mutation, owner, kind, None) + } + pub(crate) fn create_with_parent( + fresh: &super::layout::FreshReferenceRoot, + domain: &super::maintenance_ownership::ReferenceDomain, + mutation: &super::maintenance_ownership::ReferenceMutation<'_>, + owner: &super::locks::LockedFile, + kind: u8, + parent: Option<&super::run_workspace::ParentRunScope>, + ) -> Result, crate::error::NativeError> { + use std::io::Write; + if kind == 3 && parent.is_none() { + return Ok(None); + } + if let Some(parent) = parent { + let registration = parent.registration(); + if !registration.revalidate()? + || !registration.domain.same_scope(domain) + || !parent.owner().verifies( + ®istration.root.join("lease.lock"), + super::locks::LockMode::Exclusive, + )? + { + return Ok(None); + } + registration.startup_binding().check_building()?; + } + let Some(created) = super::maintenance_ownership::initialize_workspace_publisher( + fresh, domain, mutation, owner, kind, + )? + else { + return Ok(None); + }; + let writers = super::locks::create_new_coordination_in( + &created.anchor, + b".workspace-writers.lock", + &fresh.path().join(".workspace-writers.lock"), + )?; + writers.initialize_creation_incarnation(created.nonce)?; + let origins = created.anchor.create_directory(b".workspace-origins")?; + let mut origins_nonce = [0; 32]; + getrandom::fill(&mut origins_nonce).map_err(|_| invalid_control_io())?; + super::locks::set_created_file_nonce(&origins.pin_handle()?, &origins_nonce)?; + let created_directories = if kind == 1 { + let staging = created.anchor.create_directory(b"staging")?; + let candidate = created.anchor.create_directory(b"candidate")?; + let mut staging_nonce = [0; 32]; + let mut candidate_nonce = [0; 32]; + getrandom::fill(&mut staging_nonce).map_err(|_| invalid_control_io())?; + getrandom::fill(&mut candidate_nonce).map_err(|_| invalid_control_io())?; + super::locks::set_created_file_nonce(&staging.pin_handle()?, &staging_nonce)?; + super::locks::set_created_file_nonce(&candidate.pin_handle()?, &candidate_nonce)?; + Some([ + WorkspaceDirectoryIncarnation { + identity: staging.identity(), + nonce: staging_nonce, + }, + WorkspaceDirectoryIncarnation { + identity: candidate.identity(), + nonce: candidate_nonce, + }, + ]) + } else { + None + }; + let parent_record = if let Some(parent) = parent { + let registration = parent.registration(); + let descriptor = WorkspaceParentRecord { + root: registration.root.clone(), + scope: registration.scope, + scope_file: registration.scope_file, + }; + let mut file = created.anchor.create_new(b".workspace.parent.json")?; + serde_json::to_writer(&mut file, &descriptor)?; + file.sync_all()?; + Some(super::locks::file_identity(&file)?) + } else { + None + }; + let scope = WorkspaceBinding { + root: created.anchor.identity(), + owner: created.owner, + registry: created.registry, + parent_root: created.parent_root, + parent_coordination: created.parent_coordination, + parent_cohort: created.parent_cohort, + publication_binding: created.publication_binding, + nonce: created.nonce, + parent_nonce: created.parent_nonce, + kind, + writers: writers.identity()?, + origins: origins.identity(), + parent_record, + startup_gate: super::locks::path_identity(&domain.workspace_startup_path())?, + created_directories, + origins_nonce, + }; + let mut domain_file = created.anchor.create_new(WORKSPACE_DOMAIN_NAME)?; + serde_json::to_writer(&mut domain_file, domain)?; + domain_file.sync_all()?; + let mut scope_file = created.anchor.create_new(WORKSPACE_SCOPE_NAME)?; + scope_file.write_all(&scope.encode())?; + scope_file.sync_all()?; + let scope_id = super::locks::file_identity(&scope_file)?; + created.anchor.sync_namespace()?; + Ok(Some(Self { + root: fresh.path().to_owned(), + gate: domain.workspace_startup_path(), + scope, + scope_file: scope_id, + domain: domain.clone(), + })) + } + /// Reacquire only an existing run registration while borrowing its actual + /// exclusive owner and the destination's live reference mutation. Stored + /// scope describes the objects; the normal capability checks supply authority. + pub(super) fn recover_run( + root: &std::path::Path, + domain: &super::maintenance_ownership::ReferenceDomain, + owner: &super::locks::LockedFile, + mutation: &super::maintenance_ownership::ReferenceMutation<'_>, + ) -> Result, crate::error::NativeError> { + let directory = super::locks::RootedDirectory::open_readonly_root( + root, + super::locks::path_identity(root)?, + )?; + let file = match directory.open_readonly_file(WORKSPACE_SCOPE_NAME) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(error.into()), + }; + let scope = WorkspaceBinding::decode(&read_exact_image::<512>(&file)?) + .map_err(|_| invalid_control_io())?; + let registration = Self { + root: root.to_owned(), + gate: domain.workspace_startup_path(), + scope, + scope_file: super::locks::file_identity(&file)?, + domain: domain.clone(), + }; + if scope.kind != 1 + || !domain.revalidate()? + || super::locks::path_identity(®istration.gate)? != scope.startup_gate + { + return Err(invalid_control_io().into()); + } + let gate = super::locks::open_existing_shared_coordination(®istration.gate)?; + let capability = WorkspaceOwnerCapability { + registration: ®istration, + owner, + mutation, + gate: &gate, + }; + use super::maintenance_ownership::PublicationRootAuthority; + if !capability.revalidate()? { + return Err(invalid_control_io().into()); + } + registration.startup_binding().read_control()?; + drop(gate); + Ok(Some(registration)) + } + pub(super) fn terminal_complete(&self) -> Result { + let control = self.startup_binding().read_control()?; + Ok(matches!( + control.phase, + WorkspacePhase::Quiescent | WorkspacePhase::Retiring + ) && control.disposition != WorkspaceDisposition::None + && control.registered == control.closed + && control.source_epoch == control.sealed_source) + } + fn revalidate(&self) -> Result { + let directory = + super::locks::RootedDirectory::open_readonly_root(&self.root, self.scope.root)?; + let file = directory.open_readonly_file(WORKSPACE_SCOPE_NAME)?; + Ok(super::locks::file_identity(&file)? == self.scope_file + && read_exact_image::<512>(&file)? == self.scope.encode() + && super::locks::file_identity(&directory.open_readonly_file(b"lease.lock")?)? + == self.scope.owner + && super::locks::file_identity(&directory.open_readonly_file(BINDING_NAME)?)? + == self.scope.publication_binding) + } + fn update( + &self, + owner: &super::locks::LockedFile, + mutation: &super::maintenance_ownership::ReferenceMutation<'_>, + mode: super::locks::LockMode, + change: &mut impl FnMut(&mut WorkspaceControl) -> Result<(), crate::error::NativeError>, + ) -> Result<(), crate::error::NativeError> { + let Some(gate) = super::locks::try_open_existing_locked(&self.gate, mode)? else { + return Err(invalid_control_io().into()); + }; + let _writers = if mode == super::locks::LockMode::Exclusive { + let directory = + super::locks::RootedDirectory::open_readonly_root(&self.root, self.scope.root)?; + let file = directory.open_existing(b".workspace-writers.lock")?; + let Some(lock) = + super::locks::FixedLockedFile::try_lock(file, super::locks::LockMode::Exclusive) + .map_err(|_| invalid_control_io())? + else { + return Err(invalid_control_io().into()); + }; + if super::locks::file_identity(lock.file())? != self.scope.writers + || !lock + .matches_fixed_header(&super::locks::encode_coordination_incarnation( + self.scope.nonce, + )) + .map_err(|_| invalid_control_io())? + { + return Err(invalid_control_io().into()); + } + crate::storage::run_workspace::ensure_persisted_cleanup_allowed(&self.root)?; + crate::storage::containment::ensure_retired_registry_empty(&self.root) + .map_err(crate::error::NativeError::InvalidInput)?; + Some(lock) + } else { + None + }; + let mut capability = WorkspaceOwnerCapability { + registration: self, + owner, + mutation, + gate: &gate, + }; + let Some(mut publication) = try_begin_publication(&mut capability)? else { + return Err(invalid_control_io().into()); + }; + let mut payload = [0u8; PAYLOAD_BYTES]; + let len = publication.read_primary_control(&mut payload)?; + let mut control = + WorkspaceControl::decode(&payload[..len]).map_err(|_| invalid_control_io())?; + change(&mut control)?; + let mut scratch = PrimaryPublicationScratch::new(); + publication.publish_primary_control(&control.encode(), &mut scratch)?; + Ok(()) + } + pub(crate) fn source_change( + &self, + owner: &super::locks::LockedFile, + mutation: &super::maintenance_ownership::ReferenceMutation<'_>, + publishing: bool, + resolved: bool, + ) -> Result<(), crate::error::NativeError> { + self.update( + owner, + mutation, + super::locks::LockMode::Shared, + &mut |control| { + if matches!( + control.phase, + WorkspacePhase::OwnerTerminal + | WorkspacePhase::Closing + | WorkspacePhase::Quiescent + | WorkspacePhase::Retiring + | WorkspacePhase::Removing + ) { + return Ok(()); + } + if control.phase == WorkspacePhase::NeedsRecovery && !resolved { + return Err(invalid_control_io().into()); + } + control.source_epoch = control + .source_epoch + .checked_add(1) + .ok_or_else(invalid_control_io)?; + control.admission_epoch = control + .admission_epoch + .checked_add(1) + .ok_or_else(invalid_control_io)?; + control.phase = if publishing { + WorkspacePhase::NeedsRecovery + } else { + WorkspacePhase::Building + }; + Ok(()) + }, + ) + } + /// Primary owner event only: caller first proves actual cleanup barriers, + /// current publication outcome and ALL root lifecycle containment empty. + /// Startup Exclusive prevents a fresh or paused launcher crossing closure. + pub(crate) fn terminal( + &self, + owner: &super::locks::LockedFile, + mutation: &super::maintenance_ownership::ReferenceMutation<'_>, + disposition: WorkspaceDisposition, + ) -> Result<(), crate::error::NativeError> { + if disposition == WorkspaceDisposition::None { + return Err(invalid_control_io().into()); + } + // Retirement markers can emit creation receipts while startup is still + // open. The final check inside update is nonmutating under Exclusive; + // any new unretired lifecycle between checks defers closure safely. + crate::storage::run_workspace::ensure_persisted_cleanup_allowed(&self.root)?; + crate::storage::containment::ensure_registry_empty(&self.root) + .map_err(crate::error::NativeError::InvalidInput)?; + self.update( + owner, + mutation, + super::locks::LockMode::Exclusive, + &mut |control| { + if control.phase == WorkspacePhase::NeedsRecovery { + return Err(invalid_control_io().into()); + } + control.admission_epoch = control + .admission_epoch + .checked_add(1) + .ok_or_else(invalid_control_io)?; + control.phase = WorkspacePhase::Quiescent; + control.disposition = disposition; + control.closed = control.registered; + control.sealed_source = control.source_epoch; + validate_metadata_creators( + &self.root, + self.scope.root, + &control.metadata_creators, + )?; + control.metadata_seal = workspace_metadata_seal(&self.root, self.scope.root)?; + Ok(()) + }, + ) + } + pub(crate) fn startup_binding(&self) -> WorkspaceStartupBinding { + WorkspaceStartupBinding { + root: self.root.clone(), + gate: self.gate.clone(), + scope: self.scope, + scope_file: self.scope_file, + } + } + pub(crate) fn domain(&self) -> &super::maintenance_ownership::ReferenceDomain { + &self.domain + } +} +struct WorkspaceOwnerCapability<'a, 'mutation> { + registration: &'a WorkspaceRegistration, + owner: &'a super::locks::LockedFile, + mutation: &'a super::maintenance_ownership::ReferenceMutation<'mutation>, + gate: &'a super::locks::LockedFile, +} +impl super::maintenance_ownership::PublicationRootAuthority for WorkspaceOwnerCapability<'_, '_> { + fn root(&self) -> &std::path::Path { + &self.registration.root + } + fn root_identity(&self) -> super::locks::FileIdentity { + self.registration.scope.root + } + fn coordination_identity(&self) -> super::locks::FileIdentity { + self.registration.scope.owner + } + fn incarnation(&self) -> [u8; 32] { + self.registration.scope.nonce + } + fn cohort_identity(&self) -> super::locks::FileIdentity { + self.registration.scope_file + } + fn revalidate(&self) -> Result { + let Some(parent) = self.mutation.artifact_root_producer_capability()? else { + return Ok(false); + }; + let scope = self.registration.scope; + Ok(self.registration.revalidate()? + && self.owner.verifies( + &self.registration.root.join("lease.lock"), + super::locks::LockMode::Exclusive, + )? + && self.owner.identity()? == scope.owner + && self.owner.coordination_incarnation()? == Some(scope.nonce) + && self + .gate + .verifies(&self.registration.gate, self.gate.mode())? + && parent.revalidate()? + && parent.root_identity() == scope.parent_root + && parent.coordination_identity() == scope.parent_coordination + && parent.incarnation() == scope.parent_nonce + && parent.cohort_identity() == scope.parent_cohort) + } +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub(crate) struct WorkspaceStartupBinding { + root: std::path::PathBuf, + gate: std::path::PathBuf, + scope: WorkspaceBinding, + scope_file: super::locks::FileIdentity, +} +pub(crate) struct WorkspaceStartupGuard { + gate: super::locks::LockedFile, + binding: WorkspaceStartupBinding, + _same_thread: std::marker::PhantomData>, +} +impl WorkspaceStartupBinding { + /// Ordinary launcher discovery only; these serialized facts are not a + /// capability. The child independently opens and checks its OWN actual gate. + pub(crate) fn discover( + workspace: &std::path::Path, + ) -> Result, crate::error::NativeError> { + for root in workspace.ancestors() { + let path = root.join(std::str::from_utf8(WORKSPACE_SCOPE_NAME).unwrap()); + let file = match std::fs::File::open(&path) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue, + Err(error) => return Err(error.into()), + }; + let bytes = match read_exact_image::<512>(&file) { + Ok(bytes) => bytes, + Err(_) => return Ok(None), + }; + let scope = match WorkspaceBinding::decode(&bytes) { + Ok(scope) => scope, + Err(_) => return Ok(None), + }; + let domain_file = std::fs::File::open( + root.join(std::str::from_utf8(WORKSPACE_DOMAIN_NAME).unwrap()), + )?; + #[cfg(windows)] + const DOMAIN_BOUND: u64 = 8 * 1024 * 1024; + #[cfg(not(windows))] + const DOMAIN_BOUND: u64 = 256 * 1024; + if domain_file.metadata()?.len() > DOMAIN_BOUND { + return Ok(None); + } + let domain: super::maintenance_ownership::ReferenceDomain = + serde_json::from_reader(domain_file)?; + let binding = Self { + root: root.to_owned(), + gate: domain.workspace_startup_path(), + scope, + scope_file: super::locks::file_identity(&file)?, + }; + binding.check_building()?; + return Ok(Some(binding)); + } + Ok(None) + } + fn read_control(&self) -> Result { + let directory = + super::locks::RootedDirectory::open_readonly_root(&self.root, self.scope.root)?; + let scope_file = directory.open_readonly_file(WORKSPACE_SCOPE_NAME)?; + if super::locks::file_identity(&scope_file)? != self.scope_file + || read_exact_image::<512>(&scope_file)? != self.scope.encode() + || super::locks::file_identity(&directory.open_readonly_file(b"lease.lock")?)? + != self.scope.owner + { + return Err(invalid_control_io().into()); + } + let binding_file = directory.open_readonly_file(BINDING_NAME)?; + if super::locks::file_identity(&binding_file)? != self.scope.publication_binding { + return Err(invalid_control_io().into()); + } + let binding = PublisherBinding::decode(&read_exact_image::<512>(&binding_file)?) + .map_err(|_| invalid_control_io())?; + if binding.root != self.scope.root + || binding.coordination != self.scope.owner + || binding.incarnation != self.scope.nonce + { + return Err(invalid_control_io().into()); + } + let selector_file = directory.open_readonly_file(SELECTOR_NAME)?; + let selector = read_exact_image::(&selector_file)?; + let data0 = directory.open_readonly_file(DATA_NAMES[0])?; + let data1 = directory.open_readonly_file(DATA_NAMES[1])?; + if super::locks::file_identity(&selector_file)? != binding.selector + || super::locks::file_identity(&data0)? != binding.data[0] + || super::locks::file_identity(&data1)? != binding.data[1] + { + return Err(invalid_control_io().into()); + } + let images = [ + read_exact_image::(&data0)?, + read_exact_image::(&data1)?, + ]; + let commit = decode_commit(&selector[..COMMIT_BYTES], &self.scope.nonce).ok(); + let intent = decode_intent(&selector[COMMIT_BYTES..], &self.scope.nonce).ok(); + let slots = [ + decode_slot(&images[0], &self.scope.nonce).ok(), + decode_slot(&images[1], &self.scope.nonce).ok(), + ]; + let RecoveryStep::Committed(commit) = recovery_step(commit, intent, slots) else { + return Err(invalid_control_io().into()); + }; + let slot = &images[commit.active]; + let len = number(&slot[48..56]) as usize; + let control = + WorkspaceControl::decode(&slot[56..56 + len]).map_err(|_| invalid_control_io())?; + Ok(control) + } + fn check_building(&self) -> Result<(), crate::error::NativeError> { + if self.read_control()?.phase != WorkspacePhase::Building { + return Err(crate::error::NativeError::InvalidInput( + "workspace startup is closed".into(), + )); + } + Ok(()) + } + pub(crate) fn acquire(&self) -> Result { + let gate = super::locks::open_existing_shared_coordination(&self.gate)?; + if !gate.is_current()? || gate.identity()? != self.scope.startup_gate { + return Err(invalid_control_io().into()); + } + self.check_building()?; + Ok(WorkspaceStartupGuard { + gate, + binding: self.clone(), + _same_thread: std::marker::PhantomData, + }) + } +} +impl WorkspaceStartupGuard { + pub(crate) fn recheck_building(&self) -> Result<(), crate::error::NativeError> { + if !self.gate.is_current()? { + return Err(invalid_control_io().into()); + } + self.binding.check_building() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct WorkspaceOutputBinding { + pub(crate) root: super::locks::FileIdentity, + pub(crate) coordination: super::locks::FileIdentity, + pub(crate) registration: super::locks::FileIdentity, + pub(crate) publication_binding: super::locks::FileIdentity, + pub(crate) incarnation: [u8; 32], + pub(crate) registry: super::locks::FileIdentity, + pub(crate) parent: super::locks::FileIdentity, + pub(crate) output: super::locks::FileIdentity, + pub(crate) operation: [u8; 32], + pub(crate) name: [u8; 255], + pub(crate) name_len: u8, +} +pub(crate) fn workspace_origin_name( + incarnation: &[u8; 32], + parent: super::locks::FileIdentity, + output: super::locks::FileIdentity, + operation: &[u8; 32], +) -> [u8; 64] { + let mut hash = Sha256::new(); + hash.update(incarnation); + for id in [parent, output] { + let (a, b) = id.fixed_words(); + hash.update(a.to_le_bytes()); + hash.update(b.to_le_bytes()); + } + hash.update(operation); + let digest = hash.finalize(); + let mut name = [0; 64]; + const HEX: &[u8; 16] = b"0123456789abcdef"; + for (index, byte) in digest.iter().enumerate() { + name[index * 2] = HEX[(byte >> 4) as usize]; + name[index * 2 + 1] = HEX[(byte & 15) as usize]; + } + name +} +/// Actual shared writer-control + live reference mutation, not stored markers. +/// It is retained through completed-origin publication and cannot cross threads. +struct WorkspaceOutputLease { + binding: WorkspaceStartupBinding, + origins: super::locks::RootedDirectory, + mutation: super::maintenance_ownership::ReferenceMutation<'static>, + writers: super::locks::FixedLockedFile, +} +impl WorkspaceOutputLease { + fn acquire(path: &std::path::Path) -> Result, crate::error::NativeError> { + let Some(binding) = WorkspaceStartupBinding::discover(path.parent().unwrap_or(path))? + else { + return Ok(None); + }; + let startup = binding.acquire()?; + let directory = super::locks::RootedDirectory::open(&binding.root, binding.scope.root)?; + let file = directory.open_existing(b".workspace-writers.lock")?; + let Some(writers) = + super::locks::FixedLockedFile::try_lock(file, super::locks::LockMode::Shared) + .map_err(|_| invalid_control_io())? + else { + return Err(invalid_control_io().into()); + }; + if super::locks::file_identity(writers.file())? != binding.scope.writers + || !writers + .matches_fixed_header(&super::locks::encode_coordination_incarnation( + binding.scope.nonce, + )) + .map_err(|_| invalid_control_io())? + { + return Ok(None); + } + let domain_file = directory.open_readonly_file(WORKSPACE_DOMAIN_NAME)?; + #[cfg(windows)] + const DOMAIN_BOUND: u64 = 8 * 1024 * 1024; + #[cfg(not(windows))] + const DOMAIN_BOUND: u64 = 256 * 1024; + if domain_file.metadata()?.len() > DOMAIN_BOUND { + return Ok(None); + } + let domain: super::maintenance_ownership::ReferenceDomain = + serde_json::from_reader(domain_file)?; + let mutation = super::maintenance_ownership::ReferenceMutation::begin_in_domain(&domain)?; + let Some(parent) = mutation.artifact_root_producer_capability()? else { + return Ok(None); + }; + if parent.root_identity() != binding.scope.parent_root + || parent.coordination_identity() != binding.scope.parent_coordination + || parent.incarnation() != binding.scope.parent_nonce + || parent.cohort_identity() != binding.scope.parent_cohort + { + return Ok(None); + } + let origins = directory.open_directory(b".workspace-origins")?; + if origins.identity() != binding.scope.origins + || super::locks::created_file_nonce(&origins.pin_handle()?)? + != Some(binding.scope.origins_nonce) + { + return Ok(None); + } + startup.recheck_building()?; + drop(startup); + Ok(Some(Self { + binding, + writers, + origins, + mutation, + })) + } + fn revalidate(&self) -> Result<(), crate::error::NativeError> { + self.binding.check_building()?; + let Some(parent) = self.mutation.artifact_root_producer_capability()? else { + return Err(invalid_control_io().into()); + }; + if parent.root_identity() != self.binding.scope.parent_root + || parent.coordination_identity() != self.binding.scope.parent_coordination + || parent.incarnation() != self.binding.scope.parent_nonce + || parent.cohort_identity() != self.binding.scope.parent_cohort + || super::locks::file_identity(self.writers.file())? != self.binding.scope.writers + || !self + .writers + .matches_fixed_header(&super::locks::encode_coordination_incarnation( + self.binding.scope.nonce, + )) + .map_err(|_| invalid_control_io())? + { + return Err(invalid_control_io().into()); + } + Ok(()) + } +} +/// Genuine O_EXCL-created output. There is no constructor accepting an opened +/// File or a claimed creation bool. Only accepted OS bytes enter the digest. +pub(crate) struct WorkspaceOutputFile { + file: std::fs::File, + parent: super::locks::RootedDirectory, + binding: WorkspaceOutputBinding, + accepted: Option, + hash: Sha256, + scope: WorkspaceOutputLease, +} +impl WorkspaceOutputFile { + pub(crate) fn create_new( + path: &std::path::Path, + ) -> Result, crate::error::NativeError> { + use std::path::Component; + let Some(scope) = WorkspaceOutputLease::acquire(path)? else { + return Ok(None); + }; + let relative = match path.strip_prefix(&scope.binding.root) { + Ok(path) => path, + Err(_) => return Ok(None), + }; + let mut components = relative.components().peekable(); + let mut parent = + super::locks::RootedDirectory::open(&scope.binding.root, scope.binding.scope.root)?; + let mut basename = None; + while let Some(component) = components.next() { + let Component::Normal(name) = component else { + return Ok(None); + }; + let Some(name) = name.to_str() else { + return Ok(None); + }; + if !name.is_ascii() || name.len() > 255 { + return Ok(None); + } + if components.peek().is_none() { + basename = Some(name.as_bytes()); + break; + } + parent = parent.open_directory(name.as_bytes())?; + } + let Some(name) = basename else { + return Ok(None); + }; + let file = parent.create_new(name)?; + let output = super::locks::file_identity(&file)?; + let mut operation = [0; 32]; + if getrandom::fill(&mut operation).is_err() + || super::locks::set_created_file_nonce(&file, &operation).is_err() + { + return Ok(None); + } + let mut encoded = [0; 255]; + encoded[..name.len()].copy_from_slice(name); + let binding = WorkspaceOutputBinding { + root: scope.binding.scope.root, + coordination: scope.binding.scope.owner, + registration: scope.binding.scope_file, + publication_binding: scope.binding.scope.publication_binding, + incarnation: scope.binding.scope.nonce, + registry: scope.binding.scope.origins, + parent: parent.identity(), + output, + operation, + name: encoded, + name_len: name.len() as u8, + }; + Ok(Some(Self { + file, + scope, + parent, + binding, + accepted: Some(0), + hash: Sha256::new(), + })) + } + pub(crate) fn origin_binding(&self) -> WorkspaceOutputBinding { + self.binding + } + pub(crate) fn create_origin( + &self, + ) -> Result, crate::error::NativeError> { + create_workspace_origin(&self.scope, self.binding, true) + } + pub(crate) fn finish(self) -> Result { + self.file.sync_all()?; + self.scope.revalidate()?; + let stamp = + super::locks::FixedFileStamp::capture(&self.file, super::locks::FixedControlKind::File) + .map_err(|_| invalid_control_io())?; + let accepted = self.accepted.ok_or_else(invalid_control_io)?; + if stamp.identity() != self.binding.output + || stamp.len() != accepted + || super::locks::created_file_nonce(&self.file)? != Some(self.binding.operation) + || super::locks::file_identity( + &self + .parent + .open_readonly_file(&self.binding.name[..self.binding.name_len as usize])?, + )? != self.binding.output + { + return Err(invalid_control_io().into()); + } + let expected: [u8; 32] = self.hash.finalize().into(); + let mut actual = Sha256::new(); + let mut bytes = [0u8; 8192]; + let mut offset = 0u64; + while offset < accepted { + let len = (accepted - offset).min(bytes.len() as u64) as usize; + super::locks::read_fixed_range(&self.file, offset, &mut bytes[..len])?; + actual.update(&bytes[..len]); + offset += len as u64; + } + stamp + .recheck(&self.file) + .map_err(|_| invalid_control_io())?; + let digest: [u8; 32] = actual.finalize().into(); + if digest != expected { + return Err(invalid_control_io().into()); + } + Ok(WorkspaceCompletedOutput { + file: self.file, + scope: self.scope, + parent: self.parent, + binding: self.binding, + len: accepted, + digest, + }) + } +} +impl std::io::Write for WorkspaceOutputFile { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + let count = self.file.write(bytes)?; + self.hash.update(&bytes[..count]); + self.accepted = self.accepted.and_then(|old| old.checked_add(count as u64)); + Ok(count) + } + fn flush(&mut self) -> std::io::Result<()> { + self.file.flush() + } +} +/// Live guard-backed completed event; no Clone/serde/raw File extraction. +pub(crate) struct WorkspaceCompletedOutput { + file: std::fs::File, + parent: super::locks::RootedDirectory, + binding: WorkspaceOutputBinding, + len: u64, + digest: [u8; 32], + scope: WorkspaceOutputLease, +} +impl WorkspaceCompletedOutput { + pub(crate) fn origin_binding(&self) -> WorkspaceOutputBinding { + self.binding + } + pub(crate) fn len(&self) -> u64 { + self.len + } + pub(crate) fn digest(&self) -> [u8; 32] { + self.digest + } + pub(crate) fn create_origin( + &self, + ) -> Result, crate::error::NativeError> { + create_workspace_origin(&self.scope, self.binding, true) + } + pub(crate) fn open_origin(&self) -> Result, crate::error::NativeError> { + self.scope.revalidate()?; + if super::locks::file_identity(&self.file)? != self.binding.output + || super::locks::file_identity( + &self + .parent + .open_readonly_file(&self.binding.name[..self.binding.name_len as usize])?, + )? != self.binding.output + || self.file.metadata()?.len() != self.len + || super::locks::created_file_nonce(&self.file)? != Some(self.binding.operation) + { + return Err(invalid_control_io().into()); + } + create_workspace_origin(&self.scope, self.binding, false) + } +} +/// Metadata-only file facade borrows the live output scope, so it cannot escape +/// the actual writer/activity guard or expose a payload File/clone. +pub(crate) struct WorkspaceOriginFile<'a> { + file: std::fs::File, + identity: super::locks::FileIdentity, + name: [u8; 64], + scope: &'a WorkspaceOutputLease, +} +fn create_workspace_origin( + scope: &WorkspaceOutputLease, + binding: WorkspaceOutputBinding, + create: bool, +) -> Result, crate::error::NativeError> { + scope.revalidate()?; + let name = workspace_origin_name( + &binding.incarnation, + binding.parent, + binding.output, + &binding.operation, + ); + let file = if create { + scope.origins.create_new(&name)? + } else { + scope.origins.open_existing(&name)? + }; + let identity = super::locks::file_identity(&file)?; + let receipt_nonce = workspace_receipt_nonce( + binding.incarnation, + binding.parent, + binding.output, + binding.operation, + ); + if create { + super::locks::set_created_file_nonce(&file, &receipt_nonce)?; + } else if super::locks::created_file_nonce(&file)? != Some(receipt_nonce) { + return Err(invalid_control_io().into()); + } + Ok(WorkspaceOriginFile { + file, + identity, + name, + scope, + }) +} +impl WorkspaceOriginFile<'_> { + pub(crate) fn identity(&self) -> super::locks::FileIdentity { + self.identity + } + pub(crate) fn len(&self) -> std::io::Result { + Ok(self.file.metadata()?.len()) + } + pub(crate) fn sync_all(&self) -> std::io::Result<()> { + self.file.sync_all() + } + pub(crate) fn sync_parent(&self) -> std::io::Result<()> { + if super::locks::file_identity(&self.scope.origins.open_readonly_file(&self.name)?) + .map_err(|_| invalid_control_io())? + != self.identity + { + return Err(invalid_control_io()); + } + self.scope.origins.sync_namespace() + } +} +impl std::io::Read for WorkspaceOriginFile<'_> { + fn read(&mut self, bytes: &mut [u8]) -> std::io::Result { + self.file.read(bytes) + } +} +impl std::io::Write for WorkspaceOriginFile<'_> { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + self.file.write(bytes) + } + fn flush(&mut self) -> std::io::Result<()> { + self.file.flush() + } +} +impl std::io::Seek for WorkspaceOriginFile<'_> { + fn seek(&mut self, position: std::io::SeekFrom) -> std::io::Result { + self.file.seek(position) + } +} + +/// Advisory local workspace seal. Bulk candidate/reference work remains with +/// the caller; this value owns only fixed controls and pre-reserved paths. +pub(crate) struct PreparedTerminalWorkspace { + frame: TerminalScopeFrame, + barrier: Option, + gate: std::path::PathBuf, + gate_stamp: super::locks::FixedFileStamp, + epoch: super::maintenance_admission::PreparedScopedInvalidation, + scratch: super::locks::FixedControlScratch, + _memory: super::maintenance_budget::MemoryReservation, +} +struct TerminalScopeFrame { + root: std::path::PathBuf, + parent: std::path::PathBuf, + scope: WorkspaceBinding, + control: WorkspaceControl, + paths: [std::path::PathBuf; 11], + stamps: [Option; 11], + barrier_directory: Option, + _pin: std::fs::File, +} +// A sealed registry may already have disappeared. Bind that absence to the +// actual containing directory so a newly appeared registry cannot be ignored. +// Fixed inline protocol/lease state: boxing would add a separately accounted allocation. +#[allow(clippy::large_enum_variant)] +enum TerminalBarrierDirectory { + Present { + path: std::path::PathBuf, + identity: super::locks::FileIdentity, + _pin: std::fs::File, + }, + Absent { + parent: std::path::PathBuf, + identity: super::locks::FileIdentity, + name: [u8; 255], + name_len: u8, + _pin: std::fs::File, + }, +} +fn terminal_invalid() -> super::locks::FixedControlError { + super::locks::FixedControlError::Invalid(super::locks::FixedControlProblem::InvalidRecord) +} +#[cfg(test)] +fn trace_terminal_preparation(stage: &str, error: &super::locks::FixedControlError) { + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("cleanup terminal preparation {stage}: {error:?}"); + } +} +fn terminal_io(error: crate::error::NativeError) -> super::locks::FixedControlError { + match error { + crate::error::NativeError::Io(error) => error.into(), + _ => terminal_invalid(), + } +} +impl TerminalScopeFrame { + fn prepare( + root: &std::path::Path, + scratch: &mut super::locks::FixedControlScratch, + ) -> Result { + use super::locks::{open_fixed_control, FixedControlKind as K, FixedFileStamp}; + let names = [ + ".workspace.binding", + "lease.lock", + ".workspace-writers.lock", + ".publication.binding", + ".publication.selector", + ".publication.data0", + ".publication.data1", + "cleanup-barrier.json", + "journal.json", + ".workspace.domain.json", + ".workspace.parent.json", + ]; + let paths = names.map(|name| root.join(name)); + let mut stamps = [None; 11]; + for (index, path) in paths.iter().enumerate() { + match open_fixed_control(path, K::File, false, scratch) { + Ok(file) => stamps[index] = Some(FixedFileStamp::capture(&file, K::File)?), + Err(super::locks::FixedControlError::Io(error)) + if matches!(index, 7 | 8 | 10) + && error.kind() == std::io::ErrorKind::NotFound => {} + Err(error) => return Err(error), + } + } + let scope_file = open_fixed_control(&paths[0], K::File, false, scratch)?; + let scope = WorkspaceBinding::decode(&read_exact_image::<512>(&scope_file)?) + .map_err(|_| terminal_invalid())?; + let directory = super::locks::RootedDirectory::open_readonly_root(root, scope.root)?; + if scope.parent_record != stamps[10].map(|stamp| stamp.identity()) { + #[cfg(test)] + trace_terminal_preparation("parent_record", &terminal_invalid()); + return Err(terminal_invalid()); + } + let barrier_directory = if stamps[7].is_some() { + let file = open_fixed_control(&paths[7], K::File, false, scratch)?; + if file.metadata()?.len() > 131072 { + #[cfg(test)] + trace_terminal_preparation("barrier_size", &terminal_invalid()); + return Err(terminal_invalid()); + } + let path: std::path::PathBuf = + serde_json::from_reader(file).map_err(|_| terminal_invalid())?; + if !path.is_absolute() || path == root { + #[cfg(test)] + trace_terminal_preparation("barrier_path", &terminal_invalid()); + return Err(terminal_invalid()); + } + match open_fixed_control(&path, K::Directory, false, scratch) { + Ok(pin) => Some(TerminalBarrierDirectory::Present { + identity: FixedFileStamp::capture(&pin, K::Directory)?.identity(), + path, + _pin: pin, + }), + Err(super::locks::FixedControlError::Io(error)) + if error.kind() == std::io::ErrorKind::NotFound => + { + let parent = path.parent().ok_or_else(terminal_invalid)?; + let basename = path + .file_name() + .and_then(|name| name.to_str()) + .ok_or_else(terminal_invalid)? + .as_bytes(); + if basename.is_empty() || basename.len() > 255 { + return Err(terminal_invalid()); + } + let pin = open_fixed_control(parent, K::Directory, false, scratch)?; + let identity = FixedFileStamp::capture(&pin, K::Directory)?.identity(); + let directory = + super::locks::RootedDirectory::open_readonly_root(parent, identity)?; + match directory.open_directory_readonly(basename) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} + Err(error) => return Err(error.into()), + Ok(_) => return Err(terminal_invalid()), + } + let mut name = [0; 255]; + name[..basename.len()].copy_from_slice(basename); + Some(TerminalBarrierDirectory::Absent { + parent: parent.to_owned(), + identity, + name, + name_len: basename.len() as u8, + _pin: pin, + }) + } + Err(error) => return Err(error), + } + } else { + None + }; + if stamps[1].unwrap().identity() != scope.owner + || stamps[2].unwrap().identity() != scope.writers + || stamps[3].unwrap().identity() != scope.publication_binding + { + #[cfg(test)] + trace_terminal_preparation("scope_control_ids", &terminal_invalid()); + return Err(terminal_invalid()); + } + for index in [1, 2] { + let file = open_fixed_control(&paths[index], K::File, false, scratch)?; + if super::locks::read_coordination_incarnation(&file).map_err(terminal_io)? + != Some(scope.nonce) + { + #[cfg(test)] + trace_terminal_preparation("scope_coordination", &terminal_invalid()); + return Err(terminal_invalid()); + } + } + let parent = root.parent().ok_or_else(terminal_invalid)?; + if super::locks::path_identity(parent).map_err(terminal_io)? != scope.registry { + #[cfg(test)] + trace_terminal_preparation("registry_parent", &terminal_invalid()); + return Err(terminal_invalid()); + } + let binding_file = open_fixed_control(&paths[3], K::File, false, scratch)?; + let binding = PublisherBinding::decode(&read_exact_image::<512>(&binding_file)?) + .map_err(|_| terminal_invalid())?; + if binding.root != scope.root + || binding.coordination != scope.owner + || binding.incarnation != scope.nonce + || binding.selector != stamps[4].unwrap().identity() + || binding.data != [stamps[5].unwrap().identity(), stamps[6].unwrap().identity()] + { + #[cfg(test)] + trace_terminal_preparation("publication_binding", &terminal_invalid()); + return Err(terminal_invalid()); + } + let selector = read_exact_image::(&open_fixed_control( + &paths[4], + K::File, + false, + scratch, + )?)?; + let images = [ + read_exact_image::(&open_fixed_control( + &paths[5], + K::File, + false, + scratch, + )?)?, + read_exact_image::(&open_fixed_control( + &paths[6], + K::File, + false, + scratch, + )?)?, + ]; + let commit = decode_commit(&selector[..COMMIT_BYTES], &scope.nonce).ok(); + let intent = decode_intent(&selector[COMMIT_BYTES..], &scope.nonce).ok(); + let slots = [ + decode_slot(&images[0], &scope.nonce).ok(), + decode_slot(&images[1], &scope.nonce).ok(), + ]; + let RecoveryStep::Committed(commit) = recovery_step(commit, intent, slots) else { + #[cfg(test)] + trace_terminal_preparation("control_commit", &terminal_invalid()); + return Err(terminal_invalid()); + }; + let image = &images[commit.active]; + let len = number(&image[48..56]) as usize; + let control = + WorkspaceControl::decode(&image[56..56 + len]).map_err(|_| terminal_invalid())?; + if !matches!( + control.phase, + WorkspacePhase::Quiescent | WorkspacePhase::Retiring + ) || control.disposition == WorkspaceDisposition::None + || control.registered != control.closed + || control.source_epoch != control.sealed_source + { + #[cfg(test)] + trace_terminal_preparation("terminal_control", &terminal_invalid()); + return Err(terminal_invalid()); + } + validate_metadata_creators(root, scope.root, &control.metadata_creators) + .map_err(terminal_io)?; + if workspace_metadata_seal(root, scope.root).map_err(terminal_io)? != control.metadata_seal + { + #[cfg(test)] + trace_terminal_preparation("metadata_seal", &terminal_invalid()); + return Err(terminal_invalid()); + } + for (path, stamp) in paths.iter().zip(stamps) { + if let Some(stamp) = stamp { + stamp.recheck(&open_fixed_control(path, K::File, false, scratch)?)?; + } + } + Ok(Self { + root: root.to_owned(), + parent: parent.to_owned(), + scope, + control, + paths, + stamps, + barrier_directory, + _pin: directory.pin_handle()?, + }) + } + fn check( + &self, + scratch: &mut super::locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError> { + use super::locks::{open_fixed_control, FixedControlKind as K}; + grant.take_entries(2)?; + if super::locks::RootedDirectory::open_fixed(&self.root, self.scope.root, false, scratch)? + .identity() + != self.scope.root + || super::locks::RootedDirectory::open_fixed( + &self.parent, + self.scope.registry, + false, + scratch, + )? + .identity() + != self.scope.registry + { + return Err(terminal_invalid()); + } + if let Some(barrier) = &self.barrier_directory { + grant.take_entry()?; + match barrier { + TerminalBarrierDirectory::Present { path, identity, .. } => { + super::locks::RootedDirectory::open_fixed(path, *identity, false, scratch)?; + } + TerminalBarrierDirectory::Absent { + parent, + identity, + name, + name_len, + .. + } => { + let directory = super::locks::RootedDirectory::open_fixed( + parent, *identity, false, scratch, + )?; + grant.take_entry()?; + match directory.open_directory_readonly(&name[..usize::from(*name_len)]) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} + Err(error) => return Err(error.into()), + Ok(_) => return Err(terminal_invalid()), + } + } + } + } + for (path, stamp) in self.paths.iter().zip(self.stamps) { + grant.take_entry()?; + match (open_fixed_control(path, K::File, false, scratch), stamp) { + (Ok(file), Some(stamp)) => stamp.recheck(&file)?, + (Err(super::locks::FixedControlError::Io(error)), None) + if error.kind() == std::io::ErrorKind::NotFound => {} + (Err(error), _) => return Err(error), + _ => return Err(terminal_invalid()), + } + } + Ok(()) + } + fn lock( + &self, + index: usize, + scratch: &mut super::locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, + ) -> Result { + grant.take_entry()?; + let file = super::locks::open_fixed_control( + &self.paths[index], + super::locks::FixedControlKind::File, + true, + scratch, + )?; + self.stamps[index] + .ok_or_else(terminal_invalid)? + .recheck(&file)?; + super::locks::FixedLockedFile::try_lock(file, super::locks::LockMode::Exclusive)?.ok_or( + super::locks::FixedControlError::Invalid(super::locks::FixedControlProblem::Busy), + ) + } +} +/// Prepare only genuine registered, durably terminal roots. A persisted external +/// phase-parent association requires its OWN terminal run proof. External +/// barrier identity is fixed by the real owner closure, never nested Empty. +#[cfg(test)] +#[inline(never)] +pub(crate) fn prepare_terminal_workspace( + root: &std::path::Path, + domain: &super::maintenance_ownership::ReferenceDomain, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result { + prepare_terminal_workspace_boxed(root, domain, memory) + .map(super::maintenance_budget::MemoryBox::into_inner) +} +/// Keep the prepared aggregate out of caller Result/Option stack slots. Every +/// fallible preparation has an ordinary RAII owner; only the final infallible +/// field writes initialize the already charged exact-layout allocation. +#[inline(never)] +pub(crate) fn prepare_terminal_workspace_boxed( + root: &std::path::Path, + domain: &super::maintenance_ownership::ReferenceDomain, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result< + super::maintenance_budget::MemoryBox, + super::locks::FixedControlError, +> { + let mut prepared = + super::maintenance_budget::MemoryBox::::new_uninit(memory) + .map_err(super::locks::FixedControlError::Resource)?; + let size = root + .as_os_str() + .as_encoded_bytes() + .len() + .checked_add(domain.artifact_root().as_os_str().as_encoded_bytes().len()) + .and_then(|n| n.checked_add(1024)) + .and_then(|n| n.checked_mul(40)) + .and_then(|n| n.checked_add(32768)) + .ok_or(super::locks::FixedControlError::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?; + let reservation = memory + .reserve(size) + .map_err(super::locks::FixedControlError::Resource)?; + let mut scratch = super::locks::FixedControlScratch::new( + memory + .reserve(super::locks::FixedControlScratch::required_bytes()) + .map_err(super::locks::FixedControlError::Resource)?, + )?; + let frame = match TerminalScopeFrame::prepare(root, &mut scratch) { + Ok(frame) => frame, + Err(error) => { + #[cfg(test)] + trace_terminal_preparation("frame", &error); + return Err(error); + } + }; + let epoch = match super::maintenance_admission::prepare_scoped_invalidation(domain, memory) { + Ok(epoch) => epoch, + Err(error) => { + #[cfg(test)] + trace_terminal_preparation("epoch", &error); + return Err(error); + } + }; + let (root_id, coordination, nonce, cohort) = epoch.root_facts(); + if ( + frame.scope.parent_root, + frame.scope.parent_coordination, + frame.scope.parent_nonce, + frame.scope.parent_cohort, + ) != (root_id, coordination, nonce, cohort) + { + #[cfg(test)] + trace_terminal_preparation("parent_artifact_scope", &terminal_invalid()); + return Err(terminal_invalid()); + } + let barrier = if let Some(parent_record) = frame.scope.parent_record { + let file = super::locks::open_fixed_control( + &frame.paths[10], + super::locks::FixedControlKind::File, + false, + &mut scratch, + )?; + if super::locks::file_identity(&file).map_err(terminal_io)? != parent_record + || file.metadata()?.len() > 131072 + { + #[cfg(test)] + trace_terminal_preparation("parent_record_file", &terminal_invalid()); + return Err(terminal_invalid()); + } + let parent: WorkspaceParentRecord = + serde_json::from_reader(file).map_err(|_| terminal_invalid())?; + if !parent.root.is_absolute() || parent.root == root { + #[cfg(test)] + trace_terminal_preparation("parent_record_path", &terminal_invalid()); + return Err(terminal_invalid()); + } + let barrier = TerminalScopeFrame::prepare(&parent.root, &mut scratch)?; + if barrier.scope != parent.scope + || barrier.stamps[0].map(|s| s.identity()) != Some(parent.scope_file) + || barrier.scope.parent_record.is_some() + || barrier.scope.parent_root != frame.scope.parent_root + || barrier.scope.parent_nonce != frame.scope.parent_nonce + { + #[cfg(test)] + trace_terminal_preparation("parent_scope", &terminal_invalid()); + return Err(terminal_invalid()); + } + Some(barrier) + } else { + None + }; + let gate = domain.workspace_startup_path(); + let gate_file = super::locks::open_fixed_control( + &gate, + super::locks::FixedControlKind::File, + false, + &mut scratch, + )?; + let gate_stamp = + super::locks::FixedFileStamp::capture(&gate_file, super::locks::FixedControlKind::File)?; + if gate_stamp.identity() != frame.scope.startup_gate + || barrier + .as_ref() + .is_some_and(|parent| parent.scope.startup_gate != frame.scope.startup_gate) + { + #[cfg(test)] + trace_terminal_preparation("startup_gate", &terminal_invalid()); + return Err(terminal_invalid()); + } + let target = prepared.as_mut_ptr(); + // SAFETY: allocation is uniquely owned and has PreparedTerminalWorkspace's + // exact layout. No fallible operation or user callback follows the first + // write; every field is moved exactly once before assume_init. Before this + // block, any failure drops only initialized locals and uninitialized storage. + unsafe { + std::ptr::addr_of_mut!((*target).frame).write(frame); + std::ptr::addr_of_mut!((*target).barrier).write(barrier); + std::ptr::addr_of_mut!((*target).gate).write(gate); + std::ptr::addr_of_mut!((*target).gate_stamp).write(gate_stamp); + std::ptr::addr_of_mut!((*target).epoch).write(epoch); + std::ptr::addr_of_mut!((*target).scratch).write(scratch); + std::ptr::addr_of_mut!((*target)._memory).write(reservation); + Ok(prepared.assume_init()) + } +} +/// Scoped authority exists only while actual gate, owner, writer and selector +/// locks remain held. It cannot be cloned, serialized or moved off the callback. +pub(crate) struct TerminalWorkspaceLease<'a> { + directory: &'a super::locks::RootedDirectory, + frame: &'a TerminalScopeFrame, +} +impl TerminalWorkspaceLease<'_> { + pub(crate) fn directory(&self) -> &super::locks::RootedDirectory { + self.directory + } + // SourceE is the original sealed source, distinct from the current/admission epoch. + #[allow(clippy::misnamed_getters)] + pub(crate) fn source_epoch(&self) -> u64 { + self.frame.control.sealed_source + } + pub(crate) fn admission_epoch(&self) -> u64 { + self.frame.control.admission_epoch + } + #[cfg(test)] + pub(crate) fn root_identity(&self) -> super::locks::FileIdentity { + self.frame.scope.root + } +} +pub(crate) fn with_terminal_workspace( + prepared: &mut PreparedTerminalWorkspace, + grant: &mut super::maintenance_budget::MutationGrant, + effect_entries: usize, + apply: &mut impl FnMut( + &TerminalWorkspaceLease<'_>, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError>, +) -> Result { + use super::locks::{open_fixed_control, FixedControlKind as K, FixedLockedFile, LockMode}; + grant.take_entry()?; + let gate_file = open_fixed_control(&prepared.gate, K::File, true, &mut prepared.scratch)?; + prepared.gate_stamp.recheck(&gate_file)?; + let _gate = FixedLockedFile::try_lock(gate_file, LockMode::Exclusive)?.ok_or( + super::locks::FixedControlError::Invalid(super::locks::FixedControlProblem::Busy), + )?; + let _owner = prepared.frame.lock(1, &mut prepared.scratch, grant)?; + let _writers = prepared.frame.lock(2, &mut prepared.scratch, grant)?; + let _selector = prepared.frame.lock(4, &mut prepared.scratch, grant)?; + let _barrier_owner = prepared + .barrier + .as_ref() + .map(|frame| frame.lock(1, &mut prepared.scratch, grant)) + .transpose()?; + let _barrier_writers = prepared + .barrier + .as_ref() + .map(|frame| frame.lock(2, &mut prepared.scratch, grant)) + .transpose()?; + let _barrier_selector = prepared + .barrier + .as_ref() + .map(|frame| frame.lock(4, &mut prepared.scratch, grant)) + .transpose()?; + grant.take_entry()?; + let directory = super::locks::RootedDirectory::open_fixed( + &prepared.frame.root, + prepared.frame.scope.root, + true, + &mut prepared.scratch, + )?; + super::maintenance_admission::with_scoped_reference_invalidation( + &mut prepared.epoch, + grant, + effect_entries, + &mut |scratch, grant| { + prepared.frame.check(scratch, grant)?; + if let Some(barrier) = &prepared.barrier { + barrier.check(scratch, grant)?; + } + Ok(()) + }, + &mut |_, grant| { + apply( + &TerminalWorkspaceLease { + directory: &directory, + frame: &prepared.frame, + }, + grant, + ) + }, + ) +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct WorkspaceOriginScope { + pub(crate) root: super::locks::FileIdentity, + pub(crate) coordination: super::locks::FileIdentity, + pub(crate) registration: super::locks::FileIdentity, + pub(crate) publication_binding: super::locks::FileIdentity, + pub(crate) incarnation: [u8; 32], + pub(crate) registry: super::locks::FileIdentity, +} +impl TerminalScopeFrame { + fn origin_scope(&self) -> WorkspaceOriginScope { + WorkspaceOriginScope { + root: self.scope.root, + coordination: self.scope.owner, + registration: self.stamps[0].unwrap().identity(), + publication_binding: self.scope.publication_binding, + incarnation: self.scope.nonce, + registry: self.scope.origins, + } + } +} +impl PreparedTerminalWorkspace { + pub(crate) fn root(&self) -> &std::path::Path { + &self.frame.root + } + pub(crate) fn origin_scope(&self) -> WorkspaceOriginScope { + self.frame.origin_scope() + } + // SourceE is the original sealed source, distinct from the current/admission epoch. + #[allow(clippy::misnamed_getters)] + pub(crate) fn source_epoch(&self) -> u64 { + self.frame.control.sealed_source + } + pub(crate) fn admission_epoch(&self) -> u64 { + self.frame.control.admission_epoch + } +} +impl TerminalWorkspaceLease<'_> { + pub(crate) fn origin_scope(&self) -> WorkspaceOriginScope { + self.frame.origin_scope() + } +} + +/// Actual O_EXCL mkdir event. Existing directories have no conversion API. +/// The scope guards remain live until its optional Bound-directory receipt is +/// durable; this event never certifies bytes or loader usability. +pub(crate) struct WorkspaceCreatedDirectory { + directory: super::locks::RootedDirectory, + parent: super::locks::RootedDirectory, + binding: WorkspaceOutputBinding, + scope: WorkspaceOutputLease, +} +impl WorkspaceCreatedDirectory { + pub(crate) fn from_lifecycle( + fresh: &crate::process_lifecycle::FreshLifecycleDirectory, + ) -> Result, crate::error::NativeError> { + let path = fresh.path(); + let Some(scope) = WorkspaceOutputLease::acquire(path)? else { + return Ok(None); + }; + let Some(name) = path.file_name().and_then(|name| name.to_str()) else { + return Ok(None); + }; + if !name.is_ascii() || name.len() > 255 { + return Ok(None); + } + let parent_path = path.parent().ok_or_else(invalid_control_io)?; + let parent_id = super::locks::path_identity(parent_path)?; + let parent = super::locks::RootedDirectory::open(parent_path, parent_id)?; + let directory = parent.open_directory(name.as_bytes())?; + if directory.identity() != fresh.directory().identity() { + return Err(invalid_control_io().into()); + } + let mut operation = [0; 32]; + if getrandom::fill(&mut operation).is_err() + || super::locks::set_created_file_nonce(&directory.pin_handle()?, &operation).is_err() + { + return Ok(None); + } + let mut encoded = [0; 255]; + encoded[..name.len()].copy_from_slice(name.as_bytes()); + let binding = WorkspaceOutputBinding { + root: scope.binding.scope.root, + coordination: scope.binding.scope.owner, + registration: scope.binding.scope_file, + publication_binding: scope.binding.scope.publication_binding, + incarnation: scope.binding.scope.nonce, + registry: scope.binding.scope.origins, + parent: parent.identity(), + output: directory.identity(), + operation, + name: encoded, + name_len: name.len() as u8, + }; + parent.sync_namespace()?; + Ok(Some(Self { + directory, + parent, + binding, + scope, + })) + } + + pub(crate) fn create_new( + path: &std::path::Path, + ) -> Result, crate::error::NativeError> { + use std::path::Component; + let Some(scope) = WorkspaceOutputLease::acquire(path)? else { + return Ok(None); + }; + let relative = match path.strip_prefix(&scope.binding.root) { + Ok(path) => path, + Err(_) => return Ok(None), + }; + let mut components = relative.components().peekable(); + let mut parent = + super::locks::RootedDirectory::open(&scope.binding.root, scope.binding.scope.root)?; + while let Some(component) = components.next() { + let Component::Normal(name) = component else { + return Ok(None); + }; + let Some(name) = name.to_str() else { + return Ok(None); + }; + if !name.is_ascii() || name.len() > 255 { + return Ok(None); + } + if components.peek().is_some() { + parent = parent.open_directory(name.as_bytes())?; + continue; + } + let directory = parent.create_directory(name.as_bytes())?; + let mut operation = [0; 32]; + if getrandom::fill(&mut operation).is_err() + || super::locks::set_created_file_nonce(&directory.pin_handle()?, &operation) + .is_err() + { + return Ok(None); + } + let mut encoded = [0; 255]; + encoded[..name.len()].copy_from_slice(name.as_bytes()); + let binding = WorkspaceOutputBinding { + root: scope.binding.scope.root, + coordination: scope.binding.scope.owner, + registration: scope.binding.scope_file, + publication_binding: scope.binding.scope.publication_binding, + incarnation: scope.binding.scope.nonce, + registry: scope.binding.scope.origins, + parent: parent.identity(), + output: directory.identity(), + operation, + name: encoded, + name_len: name.len() as u8, + }; + parent.sync_namespace()?; + return Ok(Some(Self { + directory, + parent, + binding, + scope, + })); + } + Ok(None) + } + pub(crate) fn origin_binding(&self) -> WorkspaceOutputBinding { + self.binding + } + pub(crate) fn create_origin( + &self, + ) -> Result, crate::error::NativeError> { + self.scope.revalidate()?; + if self.directory.identity() != self.binding.output + || self + .parent + .open_directory_readonly(&self.binding.name[..self.binding.name_len as usize])? + .identity() + != self.binding.output + || super::locks::created_file_nonce(&self.directory.pin_handle()?)? + != Some(self.binding.operation) + { + return Err(invalid_control_io().into()); + } + create_workspace_origin(&self.scope, self.binding, true) + } +} + +/// Retention-only predicate for old producer destructors. Any partial, malformed +/// or inaccessible workspace marker protects residue; this mints NO ownership. +pub(crate) fn workspace_retention_required(path: &std::path::Path) -> bool { + for (index, ancestor) in path.ancestors().enumerate() { + if index == 64 { + return true; + } + for name in [ + ".workspace.binding", + ".workspace.domain.json", + ".workspace-writers.lock", + ] { + match std::fs::symlink_metadata(ancestor.join(name)) { + Ok(_) => return true, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} + Err(_) => return true, + } + } + } + false +} + +#[derive(Clone, Copy)] +pub(crate) struct WorkspaceRemovalScope { + pub(crate) kind: u8, + pub(crate) root: super::locks::FileIdentity, + pub(crate) parent: super::locks::FileIdentity, + pub(crate) nonce: [u8; 32], + pub(crate) startup_gate: super::locks::FileIdentity, + pub(crate) parent_reference: ( + super::locks::FileIdentity, + super::locks::FileIdentity, + [u8; 32], + super::locks::FileIdentity, + ), + pub(crate) directories: [Option<(super::locks::FileIdentity, [u8; 32])>; 3], +} +impl PreparedTerminalWorkspace { + pub(crate) fn removal_scope(&self) -> WorkspaceRemovalScope { + self.frame.removal_scope() + } +} +impl TerminalScopeFrame { + fn removal_scope(&self) -> WorkspaceRemovalScope { + let d = self.scope.created_directories; + WorkspaceRemovalScope { + kind: self.scope.kind, + root: self.scope.root, + parent: self.scope.registry, + nonce: self.scope.nonce, + startup_gate: self.scope.startup_gate, + parent_reference: ( + self.scope.parent_root, + self.scope.parent_coordination, + self.scope.parent_nonce, + self.scope.parent_cohort, + ), + directories: [ + Some((self.scope.origins, self.scope.origins_nonce)), + d.map(|x| (x[0].identity, x[0].nonce)), + d.map(|x| (x[1].identity, x[1].nonce)), + ], + } + } +} +impl TerminalWorkspaceLease<'_> { + pub(crate) fn removal_scope(&self) -> WorkspaceRemovalScope { + self.frame.removal_scope() + } +} + +impl PreparedTerminalWorkspace { + pub(crate) fn scope_digest(&self) -> [u8; 32] { + Sha256::digest(self.frame.scope.encode()).into() + } +} + +/// Creation-only metadata control event. The exact temp and intended final +/// basename are both bound BEFORE first write. This is not loader completion. +pub(crate) struct WorkspaceCreatedControl { + fresh: crate::process_lifecycle::FreshLifecycleFile, + final_name: [u8; 255], + final_len: u8, + binding: WorkspaceOutputBinding, + accepted: Option, + hash: Sha256, + receipt: std::cell::Cell>, + scope: WorkspaceOutputLease, +} +/// Fact minted only after the actual private O_EXCL creator's accepted writes +/// and successful atomic publication; no constructor/serde/Clone escape. +pub(crate) struct WorkspaceControlPublication { + binding: WorkspaceOutputBinding, + final_name: [u8; 255], + final_len: u8, + len: u64, + digest: [u8; 32], + receipt: Option, +} +type WorkspaceCreationParts = (WorkspaceOutputLease, WorkspaceOutputBinding, [u8; 255], u8); + +// The fresh creator token stays inline without an extra unaccounted allocation. +#[allow(clippy::large_enum_variant)] +pub(crate) enum WorkspaceControlFile { + Plain(crate::process_lifecycle::FreshLifecycleFile), + Tracked(WorkspaceCreatedControl), +} +impl WorkspaceControlFile { + pub(crate) fn from_created( + fresh: crate::process_lifecycle::FreshLifecycleFile, + final_path: &std::path::Path, + ) -> Self { + let prepared = (|| -> Result, crate::error::NativeError> { + let path = fresh.path(); + if path.parent() != final_path.parent() { + return Ok(None); + } + let Some(scope) = WorkspaceOutputLease::acquire(path)? else { + return Ok(None); + }; + let Some(name) = path.file_name().and_then(|x| x.to_str()) else { + return Ok(None); + }; + let Some(final_name) = final_path.file_name().and_then(|x| x.to_str()) else { + return Ok(None); + }; + if name.is_empty() + || name.len() > 255 + || !name.is_ascii() + || final_name.is_empty() + || final_name.len() > 255 + || !final_name.is_ascii() + { + return Ok(None); + } + let mut operation = [0; 32]; + if getrandom::fill(&mut operation).is_err() + || super::locks::set_created_file_nonce(fresh.file(), &operation).is_err() + { + return Ok(None); + } + let parent = + super::locks::path_identity(path.parent().ok_or_else(invalid_control_io)?)?; + let mut original = [0; 255]; + original[..name.len()].copy_from_slice(name.as_bytes()); + let mut intended = [0; 255]; + intended[..final_name.len()].copy_from_slice(final_name.as_bytes()); + let binding = WorkspaceOutputBinding { + root: scope.binding.scope.root, + coordination: scope.binding.scope.owner, + registration: scope.binding.scope_file, + publication_binding: scope.binding.scope.publication_binding, + incarnation: scope.binding.scope.nonce, + registry: scope.binding.scope.origins, + parent, + output: super::locks::file_identity(fresh.file())?, + operation, + name: original, + name_len: name.len() as u8, + }; + Ok(Some((scope, binding, intended, final_name.len() as u8))) + })(); + match prepared { + Ok(Some((scope, binding, final_name, final_len))) => { + Self::Tracked(WorkspaceCreatedControl { + fresh, + final_name, + final_len, + binding, + accepted: Some(0), + hash: Sha256::new(), + receipt: std::cell::Cell::new(None), + scope, + }) + } + _ => Self::Plain(fresh), + } + } + pub(crate) fn tracking(&self) -> Option<&WorkspaceCreatedControl> { + match self { + Self::Tracked(x) => Some(x), + Self::Plain(_) => None, + } + } + pub(crate) fn publish_recorded( + self, + path: &std::path::Path, + ) -> std::io::Result> { + self.finish_control(Some(path)) + } + pub(crate) fn finish_created(self) -> std::io::Result<()> { + self.finish_created_recorded().map(|_| ()) + } + pub(crate) fn finish_created_recorded( + self, + ) -> std::io::Result> { + self.finish_control(None) + } + fn finish_control( + self, + destination: Option<&std::path::Path>, + ) -> std::io::Result> { + match self { + Self::Plain(fresh) => { + fresh.file().sync_all()?; + let source = fresh.path().to_owned(); + drop(fresh); + if let Some(path) = destination { + std::fs::rename(source, path)?; + } + Ok(None) + } + Self::Tracked(control) => { + let source = control.fresh.path(); + let intended = &control.final_name[..control.final_len as usize]; + let target = destination.unwrap_or(source); + if target.parent() != source.parent() + || target.file_name().map(|x| x.as_encoded_bytes()) != Some(intended) + { + return Err(invalid_control_io()); + } + control + .scope + .revalidate() + .map_err(|_| invalid_control_io())?; + let parent_path = source.parent().ok_or_else(invalid_control_io)?; + let parent = super::locks::RootedDirectory::open_readonly_root( + parent_path, + control.binding.parent, + )?; + let source_name = &control.binding.name[..control.binding.name_len as usize]; + if super::locks::file_identity(&parent.open_metadata_file(source_name)?) + .map_err(|_| invalid_control_io())? + != control.binding.output + { + return Err(invalid_control_io()); + } + control.fresh.file().sync_all()?; + let len = control.accepted; + let valid = len.is_some_and(|len| { + control + .fresh + .file() + .metadata() + .is_ok_and(|m| m.len() == len) + }) && super::locks::created_file_nonce(control.fresh.file())? + == Some(control.binding.operation); + let digest: [u8; 32] = control.hash.clone().finalize().into(); + let fact = valid.then(|| WorkspaceControlPublication { + binding: control.binding, + final_name: control.final_name, + final_len: control.final_len, + len: len.unwrap(), + digest, + receipt: control.receipt.get(), + }); + let WorkspaceCreatedControl { fresh, scope, .. } = control; + let source = fresh.path().to_owned(); + drop(fresh); + if let Some(path) = destination { + std::fs::rename(source, path)?; + } + drop(scope); + Ok(fact) + } + } + } +} +impl std::io::Write for WorkspaceControlFile { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + match self { + Self::Plain(fresh) => { + let mut file = fresh.file(); + std::io::Write::write(&mut file, bytes) + } + Self::Tracked(control) => { + let mut file = control.fresh.file(); + let count = std::io::Write::write(&mut file, bytes)?; + control.hash.update(&bytes[..count]); + control.accepted = control + .accepted + .and_then(|len| len.checked_add(count as u64)); + Ok(count) + } + } + } + fn flush(&mut self) -> std::io::Result<()> { + let mut file = match self { + Self::Plain(fresh) => fresh.file(), + Self::Tracked(control) => control.fresh.file(), + }; + std::io::Write::flush(&mut file) + } +} +impl WorkspaceCreatedControl { + pub(crate) fn origin_binding(&self) -> WorkspaceOutputBinding { + self.binding + } + pub(crate) fn intended_name(&self) -> &[u8] { + &self.final_name[..self.final_len as usize] + } + pub(crate) fn create_origin( + &self, + ) -> Result, crate::error::NativeError> { + if super::locks::file_identity(self.fresh.file())? != self.binding.output + || super::locks::created_file_nonce(self.fresh.file())? != Some(self.binding.operation) + { + return Err(invalid_control_io().into()); + } + let origin = create_workspace_origin(&self.scope, self.binding, true)?; + self.receipt.set(Some(origin.identity())); + Ok(origin) + } +} + +fn validate_metadata_creators( + root: &std::path::Path, + id: super::locks::FileIdentity, + creators: &[Option; 3], +) -> Result<(), crate::error::NativeError> { + let directory = super::locks::RootedDirectory::open_readonly_root(root, id)?; + for (name, creator) in ROOT_METADATA_NAMES.into_iter().zip(creators) { + match (directory.open_readonly_file(name), creator) { + (Ok(file), Some(creator)) => { + let stamp = super::locks::FixedFileStamp::capture( + &file, + super::locks::FixedControlKind::File, + ) + .map_err(|_| invalid_control_io())?; + if stamp.identity() != creator.identity + || stamp.len() != creator.len + || super::locks::created_file_nonce(&file)? != Some(creator.operation) + { + return Err(invalid_control_io().into()); + } + let mut hash = Sha256::new(); + let mut bytes = [0; 8192]; + let mut offset = 0; + while offset < creator.len { + let len = (creator.len - offset).min(bytes.len() as u64) as usize; + super::locks::read_fixed_range(&file, offset, &mut bytes[..len])?; + hash.update(&bytes[..len]); + offset += len as u64; + } + stamp.recheck(&file).map_err(|_| invalid_control_io())?; + if <[u8; 32]>::from(hash.finalize()) != creator.digest { + return Err(invalid_control_io().into()); + } + } + (Err(error), None) if error.kind() == std::io::ErrorKind::NotFound => {} + (Err(error), _) => return Err(error.into()), + _ => return Err(invalid_control_io().into()), + } + } + Ok(()) +} +impl WorkspaceRegistration { + pub(crate) fn record_metadata( + &self, + owner: &super::locks::LockedFile, + mutation: &super::maintenance_ownership::ReferenceMutation<'_>, + publication: &WorkspaceControlPublication, + ) -> Result<(), crate::error::NativeError> { + if publication.binding.root != self.scope.root + || publication.binding.parent != self.scope.root + || publication.binding.registration != self.scope_file + || publication.binding.incarnation != self.scope.nonce + { + return Err(invalid_control_io().into()); + } + let name = &publication.final_name[..publication.final_len as usize]; + let Some(index) = ROOT_METADATA_NAMES.iter().position(|x| *x == name) else { + return Ok(()); + }; + self.update( + owner, + mutation, + super::locks::LockMode::Shared, + &mut |control| { + if !matches!( + control.phase, + WorkspacePhase::Building | WorkspacePhase::NeedsRecovery + ) { + return Err(invalid_control_io().into()); + } + control.metadata_creators[index] = Some(WorkspaceMetadataCreator { + identity: publication.binding.output, + operation: publication.binding.operation, + len: publication.len, + digest: publication.digest, + receipt: publication.receipt, + }); + control.source_epoch = control + .source_epoch + .checked_add(1) + .ok_or_else(invalid_control_io)?; + control.admission_epoch = control + .admission_epoch + .checked_add(1) + .ok_or_else(invalid_control_io)?; + Ok(()) + }, + ) + } +} + +pub(crate) fn workspace_receipt_nonce( + root: [u8; 32], + parent: super::locks::FileIdentity, + output: super::locks::FileIdentity, + operation: [u8; 32], +) -> [u8; 32] { + let mut hash = Sha256::new(); + hash.update(b"CGW-RECEIPT-01"); + hash.update(root); + for id in [parent, output] { + let (a, b) = id.fixed_words(); + hash.update(a.to_le_bytes()); + hash.update(b.to_le_bytes()); + } + hash.update(operation); + hash.finalize().into() +} +#[derive(Clone, Copy)] +pub(crate) struct WorkspaceMetadataProof { + pub(crate) identity: super::locks::FileIdentity, + pub(crate) operation: [u8; 32], + pub(crate) receipt: Option, +} +impl PreparedTerminalWorkspace { + pub(crate) fn metadata_proofs(&self) -> [Option; 3] { + self.frame.control.metadata_creators.map(|c| { + c.map(|c| WorkspaceMetadataProof { + identity: c.identity, + operation: c.operation, + receipt: c.receipt, + }) + }) + } +} + +/// Private hardlink name is bound to the actual random creation operation. +/// This pin is special FIFO metadata, never a second link to a payload file. +pub(crate) fn workspace_fifo_pin_name(binding: &WorkspaceOutputBinding) -> [u8; 69] { + let mut name = [0; 69]; + name[..5].copy_from_slice(b"fifo-"); + const HEX: &[u8; 16] = b"0123456789abcdef"; + for (i, byte) in binding.operation.iter().enumerate() { + name[5 + i * 2] = HEX[(byte >> 4) as usize]; + name[6 + i * 2] = HEX[(byte & 15) as usize]; + } + name +} +/// Actual mkfifo event, retaining producer exclusion through durable origin. +/// No conversion from an existing FIFO, serialized record or raw file exists. +#[cfg(unix)] +pub(crate) struct WorkspaceCreatedFifo { + created: super::locks::CreatedContainmentFifo, + pin: Option, + parent: super::locks::RootedDirectory, + binding: WorkspaceOutputBinding, + scope: WorkspaceOutputLease, +} +#[cfg(unix)] +impl WorkspaceCreatedFifo { + pub(crate) fn create_new( + path: &std::path::Path, + ) -> Result, crate::error::NativeError> { + if path.file_name().and_then(|x| x.to_str()) != Some("writers.fifo") { + return Ok(None); + } + let Some(scope) = WorkspaceOutputLease::acquire(path)? else { + return Ok(None); + }; + let relative = path + .parent() + .ok_or_else(invalid_control_io)? + .strip_prefix(&scope.binding.root) + .map_err(|_| invalid_control_io())?; + let mut parent = + super::locks::RootedDirectory::open(&scope.binding.root, scope.binding.scope.root)?; + for part in relative.components() { + let std::path::Component::Normal(name) = part else { + return Err(invalid_control_io().into()); + }; + parent = parent.open_directory(name.as_encoded_bytes())?; + } + let mut operation = [0; 32]; + getrandom::fill(&mut operation).map_err(|_| invalid_control_io())?; + let created = parent.create_containment_fifo()?; + let mut name = [0; 255]; + name[..12].copy_from_slice(b"writers.fifo"); + let binding = WorkspaceOutputBinding { + root: scope.binding.scope.root, + coordination: scope.binding.scope.owner, + registration: scope.binding.scope_file, + publication_binding: scope.binding.scope.publication_binding, + incarnation: scope.binding.scope.nonce, + registry: scope.binding.scope.origins, + parent: parent.identity(), + output: super::locks::file_identity(created.file())?, + operation, + name, + name_len: 12, + }; + // Pin/origin tracking is optional cleanup metadata. A pin failure + // preserves the real FIFO for the primary build; create_origin below + // refuses to attest it unless both actual names/links validate. + let pin = parent + .pin_created_containment_fifo( + &created, + &scope.origins, + &workspace_fifo_pin_name(&binding), + ) + .ok(); + Ok(Some(Self { + created, + pin, + parent, + binding, + scope, + })) + } + pub(crate) fn file(&self) -> &std::fs::File { + self.created.file() + } + pub(crate) fn origin_binding(&self) -> WorkspaceOutputBinding { + self.binding + } + pub(crate) fn create_origin( + &self, + ) -> Result, crate::error::NativeError> { + self.scope.revalidate()?; + let pin = self.pin.as_ref().ok_or_else(invalid_control_io)?; + if pin.identity()? != self.binding.output { + return Err(invalid_control_io().into()); + } + self.parent + .open_pinned_fifo(b"writers.fifo", self.binding.output, 2)?; + self.scope.origins.open_pinned_fifo( + &workspace_fifo_pin_name(&self.binding), + self.binding.output, + 2, + )?; + create_workspace_origin(&self.scope, self.binding, true) + } +} diff --git a/src/storage/maintenance_generation_engine.rs b/src/storage/maintenance_generation_engine.rs new file mode 100644 index 00000000..8e8253b2 --- /dev/null +++ b/src/storage/maintenance_generation_engine.rs @@ -0,0 +1,1064 @@ +//! Durable orchestration for genuinely published, superseded generations. +//! The private control selects work; the generation role reacquires every +//! actual writer, reader and native-family exclusion before changing a name. +use super::inventory::MaintenanceTarget; +use super::locks::{self, FixedControlError, FixedControlProblem, RootedDirectory}; +use super::maintenance::{CursorAddress, MaintenanceCursor, WorkCategory, CURSOR_BYTES}; +use super::maintenance_admission::{ + self as admission, PreparedControlUpdate, PreparedPrivateMetadata, PreparedTargetBinding, + RootOriginBinding, +}; +use super::maintenance_budget::{CleanupMemory, MemoryBox, MutationGrant}; +use super::maintenance_candidate::{ + self as candidate, CandidateReport, PreparedGenerationCompletion, +}; +use super::maintenance_generation_gate::{self as gate, Origin}; +use super::maintenance_generations::{ + self as generations, GenerationError, PreparedGeneration, PreparedGenerationRootRemoval, + GENERATION_PROOF_BYTES, +}; +use super::maintenance_ownership::{ReferenceDomain, ReferenceEpoch}; + +const MAGIC: &[u8; 8] = b"CGGENE01"; +const CONTROL_BYTES: usize = 224 + CURSOR_BYTES; +const PROOF_INTENT: u8 = 0; +const LEAF_INTENT: u8 = 1; +const DETACHED: u8 = 2; +const RESET_INTENT: u8 = 3; +const PAYLOAD_NAMES: [&[u8]; 12] = [ + b"graph.ldb", + b"manifest.json", + b"metadata.json", + b"READY", + b"graph.ldb.wal", + b"graph.ldb.tmp", + b"graph.ldb.search.meta.json", + b"graph.ldb.search.lexicon.bin", + b"graph.ldb.search.postings.bin", + b"graph.ldb.search.doc_lengths.bin", + b"graph.ldb.search.documents.bin", + b"graph.ldb.search.document_offsets.bin", +]; + +fn changed() -> FixedControlError { + FixedControlError::Invalid(FixedControlProblem::Changed) +} +fn generation_error(error: GenerationError) -> FixedControlError { + match error { + GenerationError::Io(kind) => std::io::Error::from(kind).into(), + GenerationError::ResourceLimit => { + FixedControlError::Resource(super::maintenance_budget::ResourceLimit::CeilingExceeded) + } + GenerationError::Busy => FixedControlError::Invalid(FixedControlProblem::Busy), + _ => changed(), + } +} +fn candidate_error(error: candidate::CandidateError) -> FixedControlError { + match error { + candidate::CandidateError::Io(kind) => std::io::Error::from(kind).into(), + candidate::CandidateError::ResourceLimit => { + FixedControlError::Resource(super::maintenance_budget::ResourceLimit::CeilingExceeded) + } + _ => changed(), + } +} + +/// A checksum is an integrity fact only. Authority comes from fresh role +/// admission plus the original complete producer-bound cohort in the stream. +struct Control { + stage: u8, + selected: u8, + source_epoch: u64, + operation: [u8; 32], + proof_digest: [u8; 32], + generation: [u8; 128], + generation_len: u8, + cursor: MaintenanceCursor, +} +impl Control { + fn encode(&self) -> [u8; CONTROL_BYTES] { + let mut b = [0; CONTROL_BYTES]; + b[..8].copy_from_slice(MAGIC); + b[8] = self.stage; + b[9] = self.selected; + b[10] = self.generation_len; + b[16..24].copy_from_slice(&self.source_epoch.to_le_bytes()); + b[24..56].copy_from_slice(&self.operation); + b[56..88].copy_from_slice(&self.proof_digest); + b[88..216].copy_from_slice(&self.generation); + b[224..].copy_from_slice(&self.cursor.encode()); + b + } + fn decode(b: &[u8], scope: RootOriginBinding) -> Result { + if b.len() != CONTROL_BYTES + || &b[..8] != MAGIC + || b[8] > RESET_INTENT + || b[9] > 12 + || b[11..16].iter().chain(b[216..224].iter()).any(|v| *v != 0) + || b[24..56].iter().all(|v| *v == 0) + || b[56..88].iter().all(|v| *v == 0) + { + return Err(changed()); + } + let generation: [u8; 128] = b[88..216].try_into().unwrap(); + let len = usize::from(b[10]); + if len == 0 + || len > 128 + || !gate::component(&generation[..len]) + || generation[len..].iter().any(|v| *v != 0) + { + return Err(changed()); + } + Ok(Self { + stage: b[8], + selected: b[9], + generation_len: b[10], + source_epoch: u64::from_le_bytes(b[16..24].try_into().unwrap()), + operation: b[24..56].try_into().unwrap(), + proof_digest: b[56..88].try_into().unwrap(), + generation, + cursor: MaintenanceCursor::decode(b[224..].try_into().unwrap(), scope.incarnation) + .map_err(|_| changed())?, + }) + } + fn tombstone(&self) -> [u8; 73] { + let mut b = [0; 73]; + b[..9].copy_from_slice(b".retired-"); + let mut operation = self.operation; + operation[31] ^= self.selected; + for (i, value) in operation.iter().enumerate() { + b[9 + i * 2] = b"0123456789abcdef"[usize::from(value >> 4)]; + b[10 + i * 2] = b"0123456789abcdef"[usize::from(value & 15)]; + } + b + } +} + +pub(crate) fn is_generation_payload(payload: &[u8]) -> bool { + payload.starts_with(MAGIC) || payload.starts_with(b"CGGENDON") +} +#[derive(Default)] +pub(crate) struct GenerationPreparationState { + observed: Option, +} +impl GenerationPreparationState { + pub(crate) fn clear(&mut self) { + self.observed = None; + } + pub(crate) fn take_observation(&mut self) -> Option { + self.observed.take() + } + pub(crate) fn has_parked_payloads(&self) -> bool { + false + } +} + +enum Action { + Metadata { + metadata: MemoryBox, + update: MemoryBox, + append: Option>, + reset: bool, + }, + Leaf { + prepared: MemoryBox, + metadata: MemoryBox, + update: MemoryBox, + tombstone: [u8; 73], + detached: bool, + }, + Root { + prepared: MemoryBox, + metadata: MemoryBox, + completion: PreparedGenerationCompletion, + }, +} +pub(crate) struct GenerationWork { + action: Action, + report: CandidateReport, +} + +fn boxed(value: T, memory: &CleanupMemory) -> Result, FixedControlError> { + MemoryBox::new(value, memory).map_err(FixedControlError::Resource) +} +fn update( + metadata: &PreparedPrivateMetadata, + bytes: &[u8], + memory: &CleanupMemory, +) -> Result, FixedControlError> { + boxed( + admission::prepare_metadata_control( + &metadata.origin_scope(), + metadata.committed().ok_or_else(changed)?, + bytes, + memory, + )?, + memory, + ) +} +fn metadata_work( + metadata: MemoryBox, + bytes: &[u8], + append: Option>, + reset: bool, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let update = update(&metadata, bytes, memory)?; + boxed( + GenerationWork { + action: Action::Metadata { + metadata, + update, + append, + reset, + }, + report: pending(), + }, + memory, + ) + .map(Some) +} +fn pending() -> CandidateReport { + CandidateReport { + pending: true, + reasons: 1 << 7, + ..CandidateReport::default() + } +} + +fn read_proof( + metadata: &PreparedPrivateMetadata, + digest: [u8; 32], + memory: &CleanupMemory, +) -> Result, FixedControlError> { + if metadata.stream_len() != GENERATION_PROOF_BYTES as u64 { + return Err(changed()); + } + let mut proof = boxed([0; GENERATION_PROOF_BYTES], memory)?; + metadata.read_stream_exact(0, &mut *proof)?; + if gate::digest(&*proof) != digest + || &proof[..8] != b"CGGPR001" + || gate::digest(&proof[..4064]) != proof[4064..] + { + return Err(changed()); + } + Ok(proof) +} +fn proof_origin(proof: &[u8; GENERATION_PROOF_BYTES]) -> Result { + Origin::decode(proof[32..2080].try_into().unwrap()).ok_or_else(changed) +} + +#[inline(never)] +fn prepare_scope( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let canonical = match admission::prepare_canonical(target, domain, expected, memory) { + Ok(canonical) => canonical, + Err(FixedControlError::Resource(error)) => return Err(FixedControlError::Resource(error)), + Err(_) => return Ok(None), + }; + let scope = canonical.origin_scope(); + drop(canonical); + Ok(Some(scope)) +} +#[inline(never)] +fn prepare_generation_metadata( + target: &MaintenanceTarget, + scope: &RootOriginBinding, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let metadata = match admission::prepare_private_metadata(target, scope, memory) { + Ok(metadata) => metadata, + Err(FixedControlError::Resource(error)) => return Err(FixedControlError::Resource(error)), + Err(_) => return Ok(None), + }; + boxed(metadata, memory).map(Some) +} + +#[inline(never)] +pub(crate) fn prepare_work( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut GenerationPreparationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + state.observed = None; + if cancel() { + return Err(changed()); + } + if binding.is_legacy() || target.direct_pair.is_some() { + return Ok(None); + } + let Some(scope) = prepare_scope(target, domain, expected, memory)? else { + return Ok(None); + }; + let Some(metadata) = prepare_generation_metadata(target, &scope, memory)? else { + return Ok(None); + }; + if let Some(recovery) = admission::prepare_metadata_recovery(&metadata, memory)? { + // Recovery belongs to whichever role actually committed the intent. + if !metadata.payload().is_some_and(is_generation_payload) { + return Ok(None); + } + return boxed( + GenerationWork { + action: Action::Metadata { + metadata, + update: boxed(recovery, memory)?, + append: None, + reset: false, + }, + report: pending(), + }, + memory, + ) + .map(Some); + } + let payload = metadata.payload().ok_or_else(changed)?; + if payload.starts_with(b"CGGENDON") { + return prepare_completed(target, domain, metadata, memory, cancel); + } + if !payload.starts_with(MAGIC) { + return prepare_selection( + target, domain, expected, state, metadata, scope, memory, cancel, + ); + } + prepare_retained_generation_work(target, domain, expected, metadata, scope, memory, cancel) +} +#[inline(never)] +fn prepare_retained_generation_work( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + metadata: MemoryBox, + scope: RootOriginBinding, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let payload = metadata.payload().ok_or_else(changed)?; + let mut control = Control::decode(payload, scope)?; + if control.stage == RESET_INTENT { + return prepare_reset(metadata, control, memory); + } + if control.stage == PROOF_INTENT { + return prepare_proof_intent(target, domain, expected, metadata, control, memory, cancel); + } + let proof = read_proof(&metadata, control.proof_digest, memory)?; + let origin = proof_origin(&proof)?; + if origin.generation != control.generation + || origin.generation_len != control.generation_len + || u64::from_le_bytes(proof[8..16].try_into().unwrap()) != control.source_epoch + { + return Err(changed()); + } + while control.selected < 12 && origin.leaves[usize::from(control.selected)].is_none() { + control.selected += 1; + } + if control.selected == 12 { + return prepare_root( + target, domain, metadata, scope, control, proof, origin, memory, cancel, + ); + } + prepare_leaf( + target, domain, expected, metadata, control, proof, origin, memory, cancel, + ) +} +#[inline(never)] +// Keep immutable epoch, prepared metadata and source ownership explicit across this bounded off-permit stage. +#[allow(clippy::too_many_arguments)] +fn prepare_selection( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut GenerationPreparationState, + metadata: MemoryBox, + scope: RootOriginBinding, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let payload = metadata.payload().ok_or_else(changed)?; + + let Some(cursor) = candidate::idle_control_cursor(payload, scope).map_err(candidate_error)? + else { + return Ok(None); + }; + if metadata.stream_len() != 0 { + return Err(changed()); + } + let mut selection = cursor.selection(); + let observed = generations::discover_generations(target, memory, cancel, &mut |id| { + let mut address = [0; 128]; + address[..id.len()].copy_from_slice(id); + selection.observe(WorkCategory::Generation, CursorAddress(address)); + Ok(()) + }) + .map_err(generation_error)?; + state.observed = Some(CandidateReport { + checked_categories: 1 << (WorkCategory::Generation as u8), + inspected_entries: observed.inspected, + unknown: observed.unknown != 0, + reasons: u64::from(observed.unknown != 0), + ..CandidateReport::default() + }); + let Some((_, address)) = selection.selected() else { + return Ok(None); + }; + let len = address.0.iter().position(|v| *v == 0).unwrap_or(128); + let prepared = generations::prepare_exact_generation( + target, + domain, + expected, + &address.0[..len], + 0, + memory, + cancel, + ) + .map_err(generation_error)?; + finish_generation_selection( + metadata, cursor, prepared, address, expected, observed, memory, + ) +} +#[inline(never)] +fn finish_generation_selection( + metadata: MemoryBox, + mut cursor: MaintenanceCursor, + prepared: MemoryBox, + address: CursorAddress, + expected: &ReferenceEpoch, + observed: generations::GenerationReport, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let len = address.0.iter().position(|byte| *byte == 0).unwrap_or(128); + let proof = prepared.inventory().encode_proof(); + cursor.inspected(WorkCategory::Generation, address); + let mut operation = [0; 32]; + getrandom::fill(&mut operation).map_err(|_| changed())?; + if operation == [0; 32] { + return Err(changed()); + } + let control = Control { + stage: PROOF_INTENT, + selected: 0, + source_epoch: expected.counter(), + operation, + proof_digest: gate::digest(&proof), + generation: address.0, + generation_len: len as u8, + cursor, + }; + let mut work = metadata_work(metadata, &control.encode(), None, false, memory)?.unwrap(); + work.report.inspected_entries = observed.inspected; + work.report.unknown = observed.unknown != 0; + Ok(Some(work)) +} +#[inline(never)] +fn prepare_reset( + metadata: MemoryBox, + control: Control, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + // The reset intent is published only after real durable cohort Done. + // Preserve any nonmatching/foreign tail rather than truncate it. + if metadata.stream_len() == 0 { + return metadata_work(metadata, &control.cursor.encode(), None, false, memory); + } + let _proof = read_proof(&metadata, control.proof_digest, memory)?; + metadata_work(metadata, &control.cursor.encode(), None, true, memory) +} +#[inline(never)] +fn prepare_proof_intent( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + metadata: MemoryBox, + mut control: Control, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let prepared = generations::prepare_exact_generation( + target, + domain, + expected, + &control.generation[..usize::from(control.generation_len)], + 0, + memory, + cancel, + ) + .map_err(generation_error)?; + let mut proof = boxed(prepared.inventory().encode_proof(), memory)?; + // Current scoped admission may have advanced while the immutable old + // bundle stayed closed. Reconstruct the exact *original* committed + // SourceE image; equality to its durable digest is required below. + proof[8..16].copy_from_slice(&control.source_epoch.to_le_bytes()); + let checksum = gate::digest(&proof[..4064]); + proof[4064..].copy_from_slice(&checksum); + if gate::digest(&*proof) != control.proof_digest + || metadata.stream_len() > GENERATION_PROOF_BYTES as u64 + { + return Err(changed()); + } + control.stage = LEAF_INTENT; + metadata_work(metadata, &control.encode(), Some(proof), false, memory) +} +#[inline(never)] +// Keep immutable epoch, prepared metadata and source ownership explicit across this bounded off-permit stage. +#[allow(clippy::too_many_arguments)] +fn prepare_root( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + metadata: MemoryBox, + scope: RootOriginBinding, + control: Control, + proof: MemoryBox<[u8; GENERATION_PROOF_BYTES]>, + origin: Origin, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let prepared = + generations::prepare_generation_root_removal(target, domain, &proof, memory, cancel) + .map_err(generation_error)?; + let completion = candidate::prepare_generation_completion( + &scope, + metadata.committed().ok_or_else(changed)?, + origin.managed, + origin.directory, + control.proof_digest, + &control.cursor, + memory, + ) + .map_err(candidate_error)?; + boxed( + GenerationWork { + action: Action::Root { + prepared, + metadata, + completion, + }, + report: pending(), + }, + memory, + ) + .map(Some) +} +#[inline(never)] +// Keep immutable epoch, prepared metadata and source ownership explicit across this bounded off-permit stage. +#[allow(clippy::too_many_arguments)] +fn prepare_leaf( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + metadata: MemoryBox, + mut control: Control, + proof: MemoryBox<[u8; GENERATION_PROOF_BYTES]>, + origin: Origin, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let tombstone = control.tombstone(); + let root = + super::maintenance_projection::project_binding(target, memory).map_err(|_| changed())?; + let managed = RootedDirectory::open_readonly_root(&root.storage, origin.managed)?; + let detached = match managed.open_readonly_file(&tombstone[..]) { + Ok(_) => true, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => false, + Err(e) => return Err(e.into()), + }; + if control.stage == DETACHED && !detached { + // A durable observation of our exact detached incarnation precedes + // unlink. A crash after unlink can therefore advance only this item. + let source = gate::open_generation(&managed, &origin.generation, origin.generation_len) + .map_err(|_| changed())?; + if source.identity() != origin.directory { + return Err(changed()); + } + match source.open_readonly_file(PAYLOAD_NAMES[usize::from(control.selected)]) { + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + _ => return Err(changed()), + } + control.stage = LEAF_INTENT; + control.selected += 1; + return metadata_work(metadata, &control.encode(), None, false, memory); + } + let prepared = if detached { + generations::prepare_generation_detached( + target, + domain, + expected, + &proof, + usize::from(control.selected), + tombstone, + memory, + cancel, + ) + } else { + generations::prepare_generation_resume( + target, + domain, + expected, + &proof, + usize::from(control.selected), + memory, + cancel, + ) + } + .map_err(generation_error)?; + if detached { + if control.stage == LEAF_INTENT { + // Recover the rename-before-control cut. Validate the one actual + // tombstone before recording its disposition; do not unlink yet. + control.stage = DETACHED; + return metadata_work(metadata, &control.encode(), None, false, memory); + } + control.selected += 1; + control.stage = LEAF_INTENT; + } else { + control.stage = DETACHED; + } + let next = update(&metadata, &control.encode(), memory)?; + boxed( + GenerationWork { + action: Action::Leaf { + prepared, + metadata, + update: next, + tombstone, + detached, + }, + report: pending(), + }, + memory, + ) + .map(Some) +} + +fn prepare_completed( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + metadata: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let scope = metadata.origin_scope(); + let bytes = metadata.payload().ok_or_else(changed)?; + if bytes.len() != 72 + CURSOR_BYTES { + return Err(changed()); + } + let id = |offset| { + locks::FileIdentity::from_fixed_words( + u64::from_le_bytes(bytes[offset..offset + 8].try_into().unwrap()), + u64::from_le_bytes(bytes[offset + 8..offset + 16].try_into().unwrap()), + ) + }; + let managed = id(8); + let removed = id(24); + let proof_digest = bytes[40..72].try_into().unwrap(); + let cursor = MaintenanceCursor::decode(bytes[72..].try_into().unwrap(), scope.incarnation) + .map_err(|_| changed())?; + let proof = read_proof(&metadata, proof_digest, memory)?; + let origin = proof_origin(&proof)?; + if origin.managed != managed + || origin.directory != removed + || RootOriginBinding::from(origin.scope) != scope + { + return Err(changed()); + } + let prepared = + generations::prepare_generation_root_removal(target, domain, &proof, memory, cancel) + .map_err(generation_error)?; + if prepared.observed_completed().map_err(generation_error)? { + // A genuine durable completion and a completed outside-root gate are + // both required before the own stream can be reset. + let mut operation = [0; 32]; + getrandom::fill(&mut operation).map_err(|_| changed())?; + if operation == [0; 32] { + return Err(changed()); + } + let control = Control { + stage: RESET_INTENT, + selected: 12, + source_epoch: u64::from_le_bytes(proof[8..16].try_into().unwrap()), + operation, + proof_digest, + generation: origin.generation, + generation_len: origin.generation_len, + cursor, + }; + metadata_work(metadata, &control.encode(), None, false, memory) + } else { + // Callback publication can precede the gate's final Idle transport. + // Resume it against the same complete cohort after interruption. + let completion = candidate::prepare_generation_completion( + &scope, + metadata.committed().ok_or_else(changed)?, + managed, + removed, + proof_digest, + &cursor, + memory, + ) + .map_err(candidate_error)?; + boxed( + GenerationWork { + action: Action::Root { + prepared, + metadata, + completion, + }, + report: pending(), + }, + memory, + ) + .map(Some) + } +} + +impl GenerationWork { + pub(crate) fn requires_legacy_confirmation(&self) -> bool { + false + } + pub(crate) fn advance( + &mut self, + grant: &mut MutationGrant, + _confirmation: Option<&super::maintenance_scheduler::LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + let mut report = self.report; + match &mut self.action { + Action::Metadata { + metadata, + update, + append, + reset, + } => { + let effects = update.effects() + + if append.is_some() { 3 } else { 0 } + + if *reset { 2 } else { 0 }; + admission::with_private_metadata( + metadata, + grant, + effects, + &mut |mutation, grant| { + if let Some(proof) = append { + mutation.append_frame(0, &**proof, grant)?; + } + if *reset { + mutation.reset_stream(GENERATION_PROOF_BYTES as u64, grant)?; + } + mutation.publish_control(update, grant) + }, + )?; + report.mutated_entries += 1; + } + Action::Leaf { + prepared, + metadata, + update, + tombstone, + detached, + } => { + let stamp = prepared.leaf().stamp(); + let effects = 32; + #[cfg(test)] + let fault_root = metadata.origin_scope().root; + prepared.with_admitted(grant, effects, &mut |mutation, grant| { + let leaf = mutation.leaf(); + let root = mutation.managed_root(); + grant.check_time()?; + if *detached { + stamp.recheck(&root.open_readonly_file(&tombstone[..])?)?; + root.unlink_file(&tombstone[..], leaf.payload)?; + root.sync_namespace()?; + #[cfg(test)] + fault::reach(fault_root, fault::Cut::AfterUnlink); + } else { + stamp + .recheck(&mutation.source_parent().open_readonly_file(leaf.name())?)?; + mutation.source_parent().rename_file_no_replace( + leaf.name(), + root, + &tombstone[..], + leaf.payload, + )?; + mutation.source_parent().sync_namespace()?; + root.sync_namespace()?; + #[cfg(test)] + fault::reach(fault_root, fault::Cut::AfterDetach); + } + admission::with_reserved_private_metadata( + metadata, + grant, + update.effects(), + &mut |metadata, grant| metadata.publish_control(update, grant), + ) + })?; + report.mutated_entries += 1; + if *detached { + report.reclaimed_entries += 1; + report.reclaimed_bytes += prepared.leaf().byte_len; + } + } + Action::Root { + prepared, + metadata, + completion, + } => { + #[cfg(test)] + let fault_root = metadata.origin_scope().root; + prepared.advance(grant, completion.effects(), &mut |_outcome, grant| { + #[cfg(test)] + fault::reach(fault_root, fault::Cut::AfterRootRemoval); + let mut done = None; + admission::with_reserved_private_metadata( + metadata, + grant, + completion.effects(), + &mut |mutation, grant| { + done = Some(completion.publish(mutation, grant)?); + Ok(()) + }, + )?; + #[cfg(test)] + fault::reach(fault_root, fault::Cut::AfterDurableDone); + done.ok_or_else(changed) + })?; + report.mutated_entries += 1; + report.reclaimed_entries += 1; + } + } + Ok(report) + } +} +pub(crate) fn finish_work(_work: &mut GenerationWork, _state: &mut GenerationPreparationState) {} + +#[cfg(test)] +mod fault { + use super::locks::FileIdentity; + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + // Fault names identify exact post-effect interruption boundaries. + #[allow(clippy::enum_variant_names)] + pub(super) enum Cut { + AfterDetach, + AfterUnlink, + AfterRootRemoval, + AfterDurableDone, + } + thread_local! { static CUT: std::cell::Cell> = const { std::cell::Cell::new(None) }; } + pub(super) struct Guard; + impl Drop for Guard { + fn drop(&mut self) { + CUT.with(|slot| slot.set(None)); + } + } + pub(super) fn arm(root: FileIdentity, cut: Cut) -> Guard { + CUT.with(|slot| slot.set(Some((root, cut)))); + Guard + } + pub(super) fn reach(root: FileIdentity, cut: Cut) { + CUT.with(|slot| { + if slot.get() == Some((root, cut)) { + slot.set(None); + panic!("actual generation durable cut: {cut:?}"); + } + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + fn observed_epoch(domain: &ReferenceDomain) -> ReferenceEpoch { + match super::super::maintenance_ownership::observe_reference_epoch(domain).unwrap() { + super::super::maintenance_ownership::EpochObservation::Verified(epoch) => epoch, + other => panic!("actual epoch unavailable: {other:?}"), + } + } + #[test] + fn generation_role_control_rejects_foreign_or_noncanonical_fields() { + let scope = RootOriginBinding { + root: locks::FileIdentity::from_fixed_words(1, 2), + coordination: locks::FileIdentity::from_fixed_words(3, 4), + cohort: locks::FileIdentity::from_fixed_words(5, 6), + publication_binding: locks::FileIdentity::from_fixed_words(7, 8), + incarnation: [9; 32], + }; + let mut generation = [0; 128]; + generation[..3].copy_from_slice(b"old"); + let control = Control { + stage: LEAF_INTENT, + selected: 0, + source_epoch: 3, + operation: [1; 32], + proof_digest: [2; 32], + generation, + generation_len: 3, + cursor: MaintenanceCursor::new(scope.incarnation), + }; + let wire = control.encode(); + assert!(Control::decode(&wire, scope).is_ok()); + let mut corrupt = wire; + corrupt[9] = 13; + assert!(Control::decode(&corrupt, scope).is_err()); + let mut corrupt = wire; + corrupt[91] = 1; + assert!(Control::decode(&corrupt, scope).is_err()); + let first = control.tombstone(); + assert_eq!(&first[..9], b".retired-"); + assert!(first[9..].iter().all(|b| b.is_ascii_hexdigit())); + let mut next = Control::decode(&wire, scope).unwrap(); + next.selected = 1; + assert_ne!(first, next.tombstone()); + } + + #[test] + fn dc_generation_engine_actual_cold_progress_preserves_both_reader_generations() { + use crate::api::{execute_materialization, MaterializeOptions}; + let mut nonce = [0; 16]; + getrandom::fill(&mut nonce).unwrap(); + let base = std::env::temp_dir().join(format!( + "dc-generation-engine-{:032x}", + u128::from_le_bytes(nonce) + )); + std::fs::create_dir_all(base.join("src")).unwrap(); + let base = std::fs::canonicalize(base).unwrap(); + let storage = base.join(".codebaseGraph/storage"); + std::fs::write(base.join("src/lib.rs"), "pub fn original_generation() {}\n").unwrap(); + let options = MaterializeOptions { + source_root: Some(base.clone()), + storage_root: Some(storage.clone()), + mode: "full".into(), + parallel: false, + use_git: false, + ..MaterializeOptions::default() + }; + let (_, first) = execute_materialization(&options).unwrap(); + let first = first.active_generation.unwrap(); + let layout = super::super::layout::ManagedLayout::new(&storage); + let store = super::super::managed::ManagedStore::new(layout.clone()); + let old_reader = store.open_read().unwrap(); + std::fs::write( + base.join("src/lib.rs"), + "pub fn replacement_generation() { let _value = 2; }\n", + ) + .unwrap(); + let (_, second) = execute_materialization(&options).unwrap(); + let second = second.active_generation.unwrap(); + assert_ne!(first, second); + std::fs::write( + base.join(".codebaseGraph/config.json"), + serde_json::to_vec(&serde_json::json!({"schema_version":3,"storage_root":storage})) + .unwrap(), + ) + .unwrap(); + let target = MaintenanceTarget { + repository_root: base.clone(), + state_root: base.join(".codebaseGraph"), + storage_root: Some(storage.clone()), + direct_pair: None, + config_path: None, + }; + let domain = ReferenceDomain::managed(&layout).unwrap(); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let mut blocked = false; + for _ in 0..8 { + let binding = admission::prepare_target_binding(&target, &memory).unwrap(); + let epoch = observed_epoch(&domain); + let mut state = GenerationPreparationState::default(); + let mut work = prepare_work( + &target, + &binding, + &domain, + &epoch, + &mut state, + &memory, + &|| false, + ) + .unwrap() + .expect("actual final generation work"); + match work.advance(&mut MutationGrant::start(), None) { + Err(FixedControlError::Invalid(FixedControlProblem::Busy)) => { + blocked = true; + break; + } + Ok(_) => finish_work(&mut work, &mut state), + Err(error) => { + panic!("generation preparation failed before reader exclusion: {error:?}") + } + } + } + assert!(blocked, "old reader must block the actual role detach"); + assert!(layout.generation(&first).unwrap().db_path().exists()); + drop(old_reader); + // The protected current generation remains actually open throughout + // all old-generation effects, including native-family lock retirement. + let current_reader = store.open_read().unwrap(); + assert_eq!(current_reader.generation_id, second); + let mut completed = false; + let cuts = [ + fault::Cut::AfterDetach, + fault::Cut::AfterUnlink, + fault::Cut::AfterRootRemoval, + fault::Cut::AfterDurableDone, + ]; + let fault_root = locks::path_identity(domain.artifact_root()).unwrap(); + let mut cut_index = 0; + for _ in 0..128 { + let binding = admission::prepare_target_binding(&target, &memory).unwrap(); + let epoch = observed_epoch(&domain); + // Recreate every warm owner on purpose: only actual durable + // control+cohort facts and reacquired role guards can make progress. + let mut state = GenerationPreparationState::default(); + let Some(mut work) = prepare_work( + &target, + &binding, + &domain, + &epoch, + &mut state, + &memory, + &|| false, + ) + .unwrap() else { + completed = true; + break; + }; + let arm = cuts.get(cut_index).map(|cut| fault::arm(fault_root, *cut)); + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + work.advance(&mut MutationGrant::start(), None) + })) { + Ok(Ok(_)) => finish_work(&mut work, &mut state), + Ok(Err(FixedControlError::Grant(_))) => {} + Ok(Err(error)) => panic!("cold generation progress failed: {error:?}"), + Err(payload) => { + let is_cut = payload.downcast_ref::().is_some_and(|message| { + message.starts_with("actual generation durable cut:") + }); + if !is_cut { + std::panic::resume_unwind(payload); + } + cut_index += 1; + } + } + drop(arm); + } + assert!( + completed, + "bounded cold attempts must complete the original cohort and reset only its stream" + ); + assert_eq!( + cut_index, + cuts.len(), + "all actual durable generation cut points must be replayed" + ); + assert!(!layout.generation(&first).unwrap().root().exists()); + assert!(layout.generation(&second).unwrap().db_path().exists()); + assert_eq!(store.open_read().unwrap().generation_id, second); + assert!(memory.peak() <= 8 * 1024 * 1024); + drop(current_reader); + drop(store); + std::fs::remove_dir_all(base).unwrap(); + } +} diff --git a/src/storage/maintenance_generation_gate.rs b/src/storage/maintenance_generation_gate.rs new file mode 100644 index 00000000..e5429ddb --- /dev/null +++ b/src/storage/maintenance_generation_gate.rs @@ -0,0 +1,1635 @@ +//! Generation lifecycle controls shared by native startup and managed reads. +//! This module imports no API/native-verified token or legacy association job. +use super::layout::{GenerationPaths, ManagedLayout}; +use super::locks::{ + self, FileIdentity, FixedControlKind, FixedFileStamp, FixedLockedFile, LockMode, + RootedDirectory, +}; +use super::maintenance_budget::{CleanupMemory, MemoryBox, MutationGrant}; +use super::maintenance_controls as controls; +use crate::error::NativeError; +use serde::Deserialize; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::path::{Path, PathBuf}; +pub(crate) const ORIGIN_BYTES: usize = 2048; +pub(crate) const FINAL_BYTES: usize = 512; +pub(crate) const ORIGIN_NAME: &[u8] = b".generation-origin"; +pub(crate) const FINAL_NAME: &[u8] = b".generation-final"; +pub(crate) const PAYLOADS: usize = 12; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct GenerationOriginScope { + pub(crate) root: FileIdentity, + pub(crate) coordination: FileIdentity, + pub(crate) cohort: FileIdentity, + pub(crate) publication_binding: FileIdentity, + pub(crate) incarnation: [u8; 32], +} +pub(crate) fn invalid(reason: &str) -> NativeError { + NativeError::InvalidInput(reason.into()) +} + +pub(crate) fn stamp(file: &File) -> Result { + FixedFileStamp::capture(file, FixedControlKind::File) + .map_err(|_| invalid("generation_source_changed_or_aliased")) +} +pub(crate) fn recheck(before: FixedFileStamp, file: &File) -> Result<(), NativeError> { + before + .recheck(file) + .map_err(|_| invalid("generation_source_changed")) +} +pub(crate) fn id_put(b: &mut [u8], id: FileIdentity) { + let (a, c) = id.fixed_words(); + b[..8].copy_from_slice(&a.to_le_bytes()); + b[8..16].copy_from_slice(&c.to_le_bytes()); +} +pub(crate) fn id_get(b: &[u8]) -> FileIdentity { + FileIdentity::from_fixed_words( + u64::from_le_bytes(b[..8].try_into().unwrap()), + u64::from_le_bytes(b[8..16].try_into().unwrap()), + ) +} +pub(crate) fn digest(bytes: &[u8]) -> [u8; 32] { + Sha256::digest(bytes).into() +} +pub(crate) fn component(bytes: &[u8]) -> bool { + !bytes.is_empty() + && bytes.len() <= 128 + && bytes + .iter() + .all(|b| b.is_ascii_alphanumeric() || matches!(*b, b'-' | b'_')) +} +pub(crate) fn fixed_id(text: &str) -> Result<([u8; 128], u8), NativeError> { + if !component(text.as_bytes()) { + return Err(invalid("invalid_generation_id")); + } + let mut out = [0; 128]; + out[..text.len()].copy_from_slice(text.as_bytes()); + Ok((out, text.len() as u8)) +} +pub(crate) fn gen_name(id: &[u8; 128], len: u8) -> Result<([u8; 132], usize), NativeError> { + let len = usize::from(len); + if len == 0 || len > id.len() { + return Err(invalid("invalid_generation_component")); + } + if !component(&id[..len]) || id[len..].iter().any(|b| *b != 0) { + return Err(invalid("invalid_generation_component")); + } + let mut name = [0; 132]; + name[..4].copy_from_slice(b"gen-"); + name[4..4 + len].copy_from_slice(&id[..len]); + Ok((name, len + 4)) +} +pub(crate) fn resource_name() -> [u8; 83] { + *b".database-use-97ad9f2c7f86b10f58a6f136bf8579cf8d878c8c33a2dd9acbc5217e639a11c6.lock" +} +pub(crate) fn resource_name_for(basename: &[u8]) -> [u8; 83] { + let hash = digest(basename); + let mut name = [0; 83]; + name[..14].copy_from_slice(b".database-use-"); + for (i, b) in hash.iter().enumerate() { + name[14 + i * 2] = b"0123456789abcdef"[(b >> 4) as usize]; + name[15 + i * 2] = b"0123456789abcdef"[(b & 15) as usize]; + } + name[78..].copy_from_slice(b".lock"); + name +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum GenerationDisposition { + Untracked, + Published, + Final, + Unknown, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct BoundLeaf { + pub(crate) id: FileIdentity, + pub(crate) len: u64, + pub(crate) hash: [u8; 32], + pub(crate) stamp_digest: [u8; 32], +} +#[derive(Clone, Copy)] +pub(crate) struct Origin { + pub(crate) generation: [u8; 128], + pub(crate) generation_len: u8, + pub(crate) scope: GenerationOriginScope, + pub(crate) managed: FileIdentity, + pub(crate) writer: FileIdentity, + pub(crate) directory: FileIdentity, + pub(crate) receipt: FileIdentity, + pub(crate) operation: [u8; 32], + pub(crate) lease: FileIdentity, + pub(crate) family: FileIdentity, + pub(crate) resource: FileIdentity, + pub(crate) manifest_resource: FileIdentity, + pub(crate) native_lock: Option, + pub(crate) leaves: [Option; PAYLOADS], +} +impl Origin { + pub(crate) fn encode(self) -> [u8; ORIGIN_BYTES] { + let mut b = [0; ORIGIN_BYTES]; + b[..8].copy_from_slice(b"CGGEN001"); + b[8..16].copy_from_slice(&2u64.to_le_bytes()); + b[16] = self.generation_len; + b[32..160].copy_from_slice(&self.generation); + for (o, id) in [ + (160, self.scope.root), + (176, self.scope.coordination), + (192, self.scope.cohort), + (208, self.scope.publication_binding), + (256, self.managed), + (272, self.writer), + (288, self.directory), + (304, self.receipt), + (352, self.lease), + (368, self.family), + (384, self.resource), + ] { + id_put(&mut b[o..o + 16], id); + } + b[224..256].copy_from_slice(&self.scope.incarnation); + b[320..352].copy_from_slice(&self.operation); + for (i, leaf) in self.leaves.iter().enumerate() { + if let Some(leaf) = leaf { + let at = 400 + i * 112; + b[at] = 1; + id_put(&mut b[at + 8..at + 24], leaf.id); + b[at + 24..at + 32].copy_from_slice(&leaf.len.to_le_bytes()); + b[at + 32..at + 64].copy_from_slice(&leaf.hash); + b[at + 64..at + 96].copy_from_slice(&leaf.stamp_digest); + } + } + id_put(&mut b[1744..1760], self.manifest_resource); + if let Some(id) = self.native_lock { + b[1760] = 1; + id_put(&mut b[1768..1784], id); + } + let hash = digest(&b[..2016]); + b[2016..].copy_from_slice(&hash); + b + } + pub(crate) fn decode(b: &[u8; ORIGIN_BYTES]) -> Option { + if &b[..8] != b"CGGEN001" + || u64::from_le_bytes(b[8..16].try_into().ok()?) != 2 + || b[1760] > 1 + || b[1761..1768].iter().any(|v| *v != 0) + || (b[1760] == 0 && b[1768..1784].iter().any(|v| *v != 0)) + || b[17..32] + .iter() + .chain(b[1784..2016].iter()) + .any(|v| *v != 0) + || digest(&b[..2016]) != b[2016..] + { + return None; + } + if b[224..256].iter().all(|v| *v == 0) || b[320..352].iter().all(|v| *v == 0) { + return None; + } + let generation = b[32..160].try_into().ok()?; + let generation_len = b[16]; + gen_name(&generation, generation_len).ok()?; + let mut leaves = [None; PAYLOADS]; + for (i, leaf) in leaves.iter_mut().enumerate() { + let at = 400 + i * 112; + if b[at] > 1 + || b[at + 1..at + 8] + .iter() + .chain(b[at + 96..at + 112].iter()) + .any(|v| *v != 0) + { + return None; + } + if b[at] == 0 { + if b[at..at + 112].iter().any(|v| *v != 0) { + return None; + } + } else { + *leaf = Some(BoundLeaf { + id: id_get(&b[at + 8..at + 24]), + len: u64::from_le_bytes(b[at + 24..at + 32].try_into().ok()?), + hash: b[at + 32..at + 64].try_into().ok()?, + stamp_digest: b[at + 64..at + 96].try_into().ok()?, + }); + } + } + if leaves[..4].iter().any(Option::is_none) { + return None; + } + let search = leaves[6..].iter().filter(|v| v.is_some()).count(); + if search != 0 && search != 6 { + return None; + } + Some(Self { + generation, + generation_len, + scope: GenerationOriginScope { + root: id_get(&b[160..176]), + coordination: id_get(&b[176..192]), + cohort: id_get(&b[192..208]), + publication_binding: id_get(&b[208..224]), + incarnation: b[224..256].try_into().ok()?, + }, + managed: id_get(&b[256..272]), + writer: id_get(&b[272..288]), + directory: id_get(&b[288..304]), + receipt: id_get(&b[304..320]), + operation: b[320..352].try_into().ok()?, + lease: id_get(&b[352..368]), + family: id_get(&b[368..384]), + resource: id_get(&b[384..400]), + manifest_resource: id_get(&b[1744..1760]), + native_lock: (b[1760] == 1).then(|| id_get(&b[1768..1784])), + leaves, + }) + } +} +#[derive(Clone, Copy)] +pub(crate) struct Final { + pub(crate) generation: [u8; 128], + pub(crate) generation_len: u8, + pub(crate) managed: FileIdentity, + pub(crate) directory: FileIdentity, + pub(crate) origin: FileIdentity, + pub(crate) origin_digest: [u8; 32], + pub(crate) receipt: FileIdentity, + pub(crate) successor: FileIdentity, + pub(crate) successor_origin: FileIdentity, + pub(crate) successor_id: [u8; 128], + pub(crate) successor_len: u8, + pub(crate) successor_digest: [u8; 32], +} +impl Final { + pub(crate) fn encode(self) -> [u8; FINAL_BYTES] { + let mut b = [0; FINAL_BYTES]; + b[..8].copy_from_slice(b"CGFINAL1"); + b[8..16].copy_from_slice(&1u64.to_le_bytes()); + b[16] = self.generation_len; + b[32..160].copy_from_slice(&self.generation); + for (o, id) in [ + (160, self.managed), + (176, self.directory), + (192, self.origin), + (240, self.receipt), + (256, self.successor), + (272, self.successor_origin), + ] { + id_put(&mut b[o..o + 16], id); + } + b[208..240].copy_from_slice(&self.origin_digest); + b[288] = self.successor_len; + b[296..424].copy_from_slice(&self.successor_id); + b[424..456].copy_from_slice(&self.successor_digest); + let hash = digest(&b[..480]); + b[480..].copy_from_slice(&hash); + b + } + pub(crate) fn decode(b: &[u8; FINAL_BYTES]) -> Option { + if &b[..8] != b"CGFINAL1" + || u64::from_le_bytes(b[8..16].try_into().ok()?) != 1 + || b[17..32] + .iter() + .chain(b[289..296].iter()) + .chain(b[456..480].iter()) + .any(|v| *v != 0) + || digest(&b[..480]) != b[480..] + { + return None; + } + let generation = b[32..160].try_into().ok()?; + gen_name(&generation, b[16]).ok()?; + let successor_id = b[296..424].try_into().ok()?; + gen_name(&successor_id, b[288]).ok()?; + if generation == successor_id { + return None; + } + Some(Self { + generation, + generation_len: b[16], + managed: id_get(&b[160..176]), + directory: id_get(&b[176..192]), + origin: id_get(&b[192..208]), + origin_digest: b[208..240].try_into().ok()?, + receipt: id_get(&b[240..256]), + successor: id_get(&b[256..272]), + successor_origin: id_get(&b[272..288]), + successor_id, + successor_len: b[288], + successor_digest: b[424..456].try_into().ok()?, + }) + } +} +pub(crate) fn exact(file: &File) -> Result<[u8; N], NativeError> { + let before = stamp(file)?; + if before.len() != N as u64 { + return Err(invalid("generation_record_size")); + } + let mut cursor = file; + cursor.seek(SeekFrom::Start(0))?; + let mut bytes = [0; N]; + cursor.read_exact(&mut bytes)?; + recheck(before, file)?; + Ok(bytes) +} +pub(crate) fn open_generation( + root: &RootedDirectory, + id: &[u8; 128], + len: u8, +) -> Result { + let (name, n) = gen_name(id, len)?; + Ok(root + .open_directory_readonly(b"generations")? + .open_directory_readonly(&name[..n])?) +} +pub(crate) fn read_origin( + directory: &RootedDirectory, + managed: FileIdentity, +) -> Result<(Origin, [u8; ORIGIN_BYTES], FixedFileStamp), NativeError> { + let file = directory.open_readonly_file(ORIGIN_NAME)?; + let before = stamp(&file)?; + let bytes = exact(&file)?; + let origin = Origin::decode(&bytes).ok_or_else(|| invalid("generation_origin_unknown"))?; + if origin.directory != directory.identity() + || origin.managed != managed + || origin.receipt != before.identity() + { + return Err(invalid("generation_origin_incarnation_changed")); + } + Ok((origin, bytes, before)) +} +pub(crate) fn read_final( + directory: &RootedDirectory, + origin: Origin, + origin_bytes: &[u8; ORIGIN_BYTES], +) -> Result<(Final, [u8; FINAL_BYTES], FixedFileStamp), NativeError> { + let file = directory.open_readonly_file(FINAL_NAME)?; + let before = stamp(&file)?; + let bytes = exact(&file)?; + let final_record = Final::decode(&bytes).ok_or_else(|| invalid("generation_final_unknown"))?; + if final_record.directory != origin.directory + || final_record.managed != origin.managed + || final_record.generation != origin.generation + || final_record.generation_len != origin.generation_len + || final_record.origin != origin.receipt + || final_record.origin_digest != digest(origin_bytes) + || final_record.receipt != before.identity() + { + return Err(invalid("generation_final_incarnation_changed")); + } + Ok((final_record, bytes, before)) +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Current { + schema_version: u64, + generation_id: Text, + published_at: Text, + activated_at_ms: u64, +} +struct Text { + bytes: [u8; 128], + len: u8, +} +impl<'de> Deserialize<'de> for Text { + fn deserialize>(de: D) -> Result { + struct V; + impl<'de> serde::de::Visitor<'de> for V { + type Value = Text; + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("bounded generation control string") + } + fn visit_str(self, v: &str) -> Result { + if v.len() > 128 { + return Err(E::custom("generation control string too long")); + } + let mut bytes = [0; 128]; + bytes[..v.len()].copy_from_slice(v.as_bytes()); + Ok(Text { + bytes, + len: v.len() as u8, + }) + } + } + de.deserialize_str(V) + } +} +pub(crate) fn read_current( + root: &RootedDirectory, +) -> Result<([u8; 128], u8, FixedFileStamp), NativeError> { + let file = root.open_readonly_file(b"active.json")?; + let before = stamp(&file)?; + if before.len() > 1024 { + return Err(invalid("generation_current_size")); + } + let mut bytes = [0; 1024]; + let mut cursor = &file; + cursor.read_exact(&mut bytes[..before.len() as usize])?; + let current: Current = serde_json::from_slice(&bytes[..before.len() as usize])?; + let _ = (current.published_at, current.activated_at_ms); + if current.schema_version != 2 + || !component(¤t.generation_id.bytes[..usize::from(current.generation_id.len)]) + { + return Err(invalid("generation_current_invalid")); + } + recheck(before, &file)?; + Ok(( + current.generation_id.bytes, + current.generation_id.len, + before, + )) +} + +pub(crate) fn observe_generation_disposition( + layout: &ManagedLayout, + paths: &GenerationPaths, + memory: &CleanupMemory, +) -> Result { + let _floor = memory + .reserve(ORIGIN_BYTES * 3 + FINAL_BYTES * 3 + 8192 + std::mem::size_of::() * 2) + .map_err(|_| invalid("generation_memory_limit"))?; + let root = RootedDirectory::open_readonly_root( + layout.storage_root(), + locks::path_identity(layout.storage_root())?, + )?; + let (id, len) = fixed_id(paths.generation_id())?; + let directory = open_generation(&root, &id, len)?; + let (origin, bytes, _) = match read_origin(&directory, root.identity()) { + Ok(value) => value, + Err(NativeError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { + return Ok(match read_legacy_final(&directory, root.identity()) { + Ok((closed, _, _)) + if closed.generation == id + && closed.generation_len == len + && current_parent_scope(&root, memory) + .is_ok_and(|scope| scope == closed.scope) => + { + GenerationDisposition::Final + } + Err(NativeError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => { + GenerationDisposition::Untracked + } + _ => GenerationDisposition::Unknown, + }) + } + Err(_) => return Ok(GenerationDisposition::Unknown), + }; + if origin.generation != id + || origin.generation_len != len + || !current_parent_scope(&root, memory).is_ok_and(|scope| scope == origin.scope) + { + return Ok(GenerationDisposition::Unknown); + } + match read_final(&directory, origin, &bytes) { + Ok(_) => Ok(GenerationDisposition::Final), + Err(NativeError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { + Ok(GenerationDisposition::Published) + } + Err(_) => Ok(GenerationDisposition::Unknown), + } +} + +const START_GATE: &[u8] = b".generation-startup.lock"; +const START_BINDING: &[u8] = b".generation-startup.binding"; +const START_DATA: [&[u8]; 2] = [b".generation-startup.data0", b".generation-startup.data1"]; +const START_SELECTOR: &[u8] = b".generation-startup.selector"; +#[derive(Clone, Copy)] +pub(super) struct GateBinding { + pub(super) managed: FileIdentity, + pub(super) generations: FileIdentity, + pub(super) writer: FileIdentity, + pub(super) gate: FileIdentity, + pub(super) binding: FileIdentity, + pub(super) data: [FileIdentity; 2], + pub(super) selector: FileIdentity, + pub(super) parent: GenerationOriginScope, + pub(super) nonce: [u8; 32], +} +impl GateBinding { + pub(super) fn encode(self) -> [u8; 512] { + let mut b = [0; 512]; + b[..8].copy_from_slice(b"CGGGATE1"); + b[8..16].copy_from_slice(&1u64.to_le_bytes()); + for (o, id) in [ + (16, self.managed), + (32, self.gate), + (48, self.data[0]), + (64, self.data[1]), + (80, self.selector), + (96, self.binding), + (112, self.parent.root), + (128, self.parent.coordination), + (144, self.parent.cohort), + (160, self.parent.publication_binding), + (240, self.generations), + (256, self.writer), + ] { + id_put(&mut b[o..o + 16], id); + } + b[176..208].copy_from_slice(&self.parent.incarnation); + b[208..240].copy_from_slice(&self.nonce); + let hash = digest(&b[..480]); + b[480..].copy_from_slice(&hash); + b + } + fn decode(b: &[u8; 512]) -> Option { + if &b[..8] != b"CGGGATE1" + || u64::from_le_bytes(b[8..16].try_into().ok()?) != 1 + || b[272..480].iter().any(|v| *v != 0) + || digest(&b[..480]) != b[480..] + || b[208..240].iter().all(|v| *v == 0) + { + return None; + } + Some(Self { + managed: id_get(&b[16..32]), + gate: id_get(&b[32..48]), + data: [id_get(&b[48..64]), id_get(&b[64..80])], + selector: id_get(&b[80..96]), + binding: id_get(&b[96..112]), + parent: GenerationOriginScope { + root: id_get(&b[112..128]), + coordination: id_get(&b[128..144]), + cohort: id_get(&b[144..160]), + publication_binding: id_get(&b[160..176]), + incarnation: b[176..208].try_into().ok()?, + }, + nonce: b[208..240].try_into().ok()?, + generations: id_get(&b[240..256]), + writer: id_get(&b[256..272]), + }) + } +} +#[derive(Clone, Copy, PartialEq, Eq)] +// This Copy control image has a fixed accounted layout; boxing would add allocation and change the verified admission handoff. +#[allow(clippy::large_enum_variant)] +enum GateState { + Idle, + Removing { + generation: [u8; 128], + len: u8, + directory: FileIdentity, + origin: FileIdentity, + final_record: FileIdentity, + proof_digest: [u8; 32], + }, +} +impl GateState { + fn encode(self) -> [u8; 256] { + let mut b = [0; 256]; + b[..8].copy_from_slice(b"CGGRMV01"); + if let Self::Removing { + generation, + len, + directory, + origin, + final_record, + proof_digest, + } = self + { + b[8] = 1; + b[9] = len; + b[16..144].copy_from_slice(&generation); + id_put(&mut b[144..160], directory); + id_put(&mut b[160..176], origin); + id_put(&mut b[176..192], final_record); + b[192..224].copy_from_slice(&proof_digest); + } + let hash = digest(&b[..224]); + b[224..].copy_from_slice(&hash); + b + } + fn decode(b: &[u8]) -> Option { + if b.len() != 256 + || &b[..8] != b"CGGRMV01" + || b[10..16].iter().any(|v| *v != 0) + || digest(&b[..224]) != b[224..] + { + return None; + } + match b[8] { + 0 if b[9..224].iter().all(|v| *v == 0) => Some(Self::Idle), + 1 => { + let generation = b[16..144].try_into().ok()?; + gen_name(&generation, b[9]).ok()?; + Some(Self::Removing { + generation, + len: b[9], + directory: id_get(&b[144..160]), + origin: id_get(&b[160..176]), + final_record: id_get(&b[176..192]), + proof_digest: b[192..224].try_into().ok()?, + }) + } + _ => None, + } + } +} +struct FreshGateFiles<'a> { + root: &'a RootedDirectory, + files: [Option<(&'static [u8], FileIdentity)>; 5], + active: bool, +} +impl FreshGateFiles<'_> { + fn create(&mut self, name: &'static [u8], bytes: &[u8]) -> Result { + let mut file = self.root.create_new(name)?; + let id = locks::file_identity(&file)?; + let slot = self + .files + .iter_mut() + .find(|v| v.is_none()) + .ok_or_else(|| invalid("generation_gate_bootstrap_capacity"))?; + *slot = Some((name, id)); + file.write_all(bytes)?; + file.sync_all()?; + Ok(id) + } +} +impl Drop for FreshGateFiles<'_> { + fn drop(&mut self) { + if !self.active { + for (name, id) in self.files.iter().rev().flatten() { + let _ = self.root.unlink_file(name, *id); + } + let _ = self.root.sync_namespace(); + } + } +} +pub(super) fn legacy_bootstrap_idle_image() -> [u8; 256] { + GateState::Idle.encode() +} + +/// Sole genuine fresh-reference-root factory. No ordinary acquisition, old +/// directory, serialized IDs or read-only observation can enroll this protocol. +pub(crate) fn initialize_fresh_generation_gate( + fresh: &super::layout::FreshReferenceRoot, + writer: &locks::WriterLease, + creation: &super::maintenance_ownership::RootCreationAuthority<'_>, +) -> Result<(), NativeError> { + let layout = ManagedLayout::new(fresh.path()); + if !writer.verifies(&layout.writer_lock_path(), LockMode::Exclusive)? + || locks::path_identity(fresh.path())? != fresh.identity() + { + return Err(invalid("generation_gate_fresh_writer_unverified")); + } + let root = RootedDirectory::open(fresh.path(), fresh.identity())?; + let generations = root.open_directory_readonly(b"generations")?; + let mut names = [0; 4096]; + let mut empty = true; + generations.visit_entries(&mut names, &mut |_| { + empty = false; + Ok(()) + })?; + if !empty { + return Err(invalid("generation_gate_fresh_namespace_not_empty")); + } + let mut nonce = [0; 32]; + getrandom::fill(&mut nonce).map_err(|_| invalid("generation_gate_entropy_unavailable"))?; + let mut created = FreshGateFiles { + root: &root, + files: [None; 5], + active: false, + }; + let gate = created.create(START_GATE, &locks::encode_coordination_incarnation(nonce))?; + let mut slot = [0; controls::SLOT_BYTES]; + let first = controls::encode_slot(&mut slot, &nonce, 0, &GateState::Idle.encode()) + .map_err(|_| invalid("generation_gate_codec"))?; + let data0 = created.create(START_DATA[0], &slot)?; + controls::encode_slot(&mut slot, &nonce, 0, b"") + .map_err(|_| invalid("generation_gate_codec"))?; + let data1 = created.create(START_DATA[1], &slot)?; + let mut selector = [0; controls::SELECTOR_BYTES]; + controls::encode_commit( + &mut selector[..controls::COMMIT_BYTES], + &nonce, + controls::CommitFact { + generation: 0, + active: 0, + slot_digest: first.digest, + }, + ) + .map_err(|_| invalid("generation_gate_codec"))?; + let selector_id = created.create(START_SELECTOR, &selector)?; + let binding_id = created.create(START_BINDING, b"")?; + let mut binding_file = root.open_existing(START_BINDING)?; + let binding = GateBinding { + managed: root.identity(), + generations: generations.identity(), + writer: writer.identity()?, + gate, + binding: binding_id, + data: [data0, data1], + selector: selector_id, + parent: GenerationOriginScope { + root: creation.root_identity(), + coordination: creation.coordination_identity()?, + cohort: creation.cohort_identity()?, + publication_binding: creation.publication_binding_identity()?, + incarnation: creation.incarnation()?, + }, + nonce, + }; + // Persist every actual namespace/inode BEFORE publishing activation bytes. + // Unsupported directory durability leaves an unactivated finite bootstrap; + // ordinary native use may stay untracked, while root removal stays disabled. + root.sync_namespace()?; + binding_file.write_all(&binding.encode())?; + binding_file.sync_all()?; + created.active = true; + Ok(()) +} +fn current_parent_scope( + root: &RootedDirectory, + memory: &CleanupMemory, +) -> Result { + let artifacts = root.open_directory_readonly(b"artifacts")?; + let scope = super::maintenance_origin::observe_artifact_root_scope(&artifacts, memory)? + .ok_or_else(|| invalid("generation_gate_parent_unverified"))?; + Ok(GenerationOriginScope { + root: scope.root, + coordination: scope.coordination, + cohort: scope.cohort, + publication_binding: scope.publication_binding, + incarnation: scope.incarnation, + }) +} +fn load_gate( + root: &RootedDirectory, + memory: &CleanupMemory, + bootstrap_path: Option<&Path>, +) -> Result, NativeError> { + let staged = || -> Result { + let Some(path) = bootstrap_path else { + return Ok(false); + }; + let domain = + super::maintenance_ownership::ReferenceDomain::managed(&ManagedLayout::new(path))?; + domain + .staged_generation_gate(root) + .map_err(|_| invalid("generation_gate_bootstrap_unverified")) + }; + let file = match root.open_readonly_file(START_BINDING) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + match root.open_readonly_file(START_GATE) { + Ok(_) if staged()? => return Ok(None), + Ok(_) => return Err(invalid("generation_gate_incomplete")), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + for name in [START_DATA[0], START_DATA[1], START_SELECTOR] { + match root.open_readonly_file(name) { + Ok(_) => return Err(invalid("generation_gate_incomplete")), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(e.into()), + } + } + } + Err(e) => return Err(e.into()), + } + return Ok(None); + } + Err(error) => return Err(error.into()), + }; + let image = exact(&file); + let binding = match image.as_ref().ok().and_then(GateBinding::decode) { + Some(binding) => binding, + None if staged()? => return Ok(None), + None => return Err(invalid("generation_gate_binding_unknown")), + }; + if root.identity() != binding.managed + || locks::file_identity(&file)? != binding.binding + || root.open_directory_readonly(b"generations")?.identity() != binding.generations + || current_parent_scope(root, memory)? != binding.parent + { + return Err(invalid("generation_gate_binding_changed")); + } + Ok(Some(binding)) +} +fn read_gate_state( + root: &RootedDirectory, + binding: GateBinding, +) -> Result<(controls::CommitFact, GateState), NativeError> { + let selector = root.open_readonly_file(START_SELECTOR)?; + if locks::file_identity(&selector)? != binding.selector { + return Err(invalid("generation_gate_selector_changed")); + } + let image = exact::<{ controls::SELECTOR_BYTES }>(&selector)?; + let mut slots = [None; 2]; + let mut payloads = [[0; controls::SLOT_BYTES]; 2]; + for i in 0..2 { + let file = root.open_readonly_file(START_DATA[i])?; + if locks::file_identity(&file)? != binding.data[i] { + return Err(invalid("generation_gate_slot_changed")); + } + payloads[i] = exact(&file)?; + slots[i] = controls::decode_slot(&payloads[i], &binding.nonce).ok(); + } + let commit = controls::decode_commit(&image[..controls::COMMIT_BYTES], &binding.nonce).ok(); + let intent = controls::decode_intent(&image[controls::COMMIT_BYTES..], &binding.nonce).ok(); + let commit = match controls::recovery_step(commit, intent, slots) { + controls::RecoveryStep::Committed(commit) => commit, + controls::RecoveryStep::RewriteIntendedSlot(i) + | controls::RecoveryStep::CommitIntendedSlot(i) => { + // A valid complete Intent names the closed directory even when its + // target slot/commit still needs recovery. Other generations remain + // usable; no startup mutates or guesses the highest slot. + let intended = &image + [controls::COMMIT_BYTES + 96..controls::COMMIT_BYTES + 96 + controls::SLOT_BYTES]; + let fact = controls::decode_slot(intended, &binding.nonce) + .map_err(|_| invalid("generation_gate_unknown_intent"))?; + let state = GateState::decode(&intended[56..56 + fact.payload_len]) + .ok_or_else(|| invalid("generation_gate_unknown_state"))?; + return Ok(( + controls::CommitFact { + generation: i.after, + active: i.target, + slot_digest: i.slot_digest, + }, + state, + )); + } + controls::RecoveryStep::Unknown => return Err(invalid("generation_gate_recovery_unknown")), + }; + let fact = slots[commit.active].ok_or_else(|| invalid("generation_gate_unknown_slot"))?; + let state = GateState::decode(&payloads[commit.active][56..56 + fact.payload_len]) + .ok_or_else(|| invalid("generation_gate_unknown_state"))?; + Ok((commit, state)) +} +pub(crate) struct GenerationStartupGuard { + root: RootedDirectory, + gate: FixedLockedFile, + binding: GateBinding, + parent: FileIdentity, + parent_path: PathBuf, +} +fn locate_startup( + database: &Path, +) -> Result, NativeError> { + let absolute = if database.is_absolute() { + database.to_path_buf() + } else { + std::env::current_dir()?.join(database) + }; + let parent = std::fs::canonicalize( + absolute + .parent() + .ok_or_else(|| invalid("generation_parent_missing"))?, + )?; + let Some(generations_path) = parent.parent() else { + return Ok(None); + }; + let Some(managed_path) = generations_path.parent() else { + return Ok(None); + }; + let root = + RootedDirectory::open_readonly_root(managed_path, locks::path_identity(managed_path)?)?; + let memory = CleanupMemory::new(64 * 1024).map_err(|_| invalid("generation_startup_memory"))?; + let Some(binding) = load_gate(&root, &memory, Some(managed_path))? else { + let parent_id = locks::path_identity(&parent)?; + let directory = RootedDirectory::open_readonly_root(&parent, parent_id)?; + if let Ok((origin, image, _)) = read_origin(&directory, root.identity()) { + if current_parent_scope(&root, &memory).is_ok_and(|scope| scope == origin.scope) + && read_final(&directory, origin, &image).is_ok() + { + return Err(invalid("managed_generation_retired_rebuild_required")); + } + } + if let Ok((closed, _, _)) = read_legacy_final(&directory, root.identity()) { + if current_parent_scope(&root, &memory).is_ok_and(|scope| scope == closed.scope) { + return Err(invalid("managed_generation_retired_rebuild_required")); + } + } + return Ok(None); + }; + if locks::path_identity(generations_path)? != binding.generations { + return Ok(None); + } + let parent_id = locks::path_identity(&parent)?; + Ok(Some((root, binding, parent, parent_id))) +} +fn check_startup( + root: &RootedDirectory, + binding: GateBinding, + parent_path: &Path, + parent: FileIdentity, +) -> Result<(), NativeError> { + if locks::path_identity(parent_path)? != parent { + return Err(invalid("generation_startup_parent_changed")); + } + let directory = RootedDirectory::open_readonly_root(parent_path, parent)?; + let (_, state) = read_gate_state(root, binding)?; + if let GateState::Removing { + generation, + len, + directory: closed, + .. + } = state + { + let (name, n) = gen_name(&generation, len)?; + let current = root + .open_directory_readonly(b"generations")? + .open_directory_readonly(&name[..n]); + if closed == parent || current.as_ref().is_ok_and(|dir| dir.identity() == parent) { + return Err(invalid("managed_generation_retired_rebuild_required")); + } + } + // A genuine FINAL alone closes future births while existing family users + // retain their actual leases. Unknown optional provenance is not authority. + if let Ok((origin, image, _)) = read_origin(&directory, root.identity()) { + if origin.scope == binding.parent && read_final(&directory, origin, &image).is_ok() { + return Err(invalid("managed_generation_retired_rebuild_required")); + } + } + if let Ok((closed, _, _)) = read_legacy_final(&directory, root.identity()) { + if closed.scope == binding.parent { + return Err(invalid("managed_generation_retired_rebuild_required")); + } + } + Ok(()) +} +pub(crate) fn before_native_open( + database: &Path, +) -> Result, NativeError> { + let Some((root, binding, parent_path, parent)) = locate_startup(database)? else { + return Ok(None); + }; + let file = root.open_readonly_file(START_GATE)?; + if locks::file_identity(&file)? != binding.gate { + return Err(invalid("generation_startup_gate_changed")); + } + let gate = FixedLockedFile::try_lock(file, LockMode::Shared) + .map_err(|_| invalid("generation_startup_gate_unavailable"))? + .ok_or_else(|| invalid("generation_startup_gate_busy"))?; + if locks::read_coordination_incarnation(gate.file())? != Some(binding.nonce) { + return Err(invalid("generation_startup_incarnation_changed")); + } + check_startup(&root, binding, &parent_path, parent)?; + Ok(Some(GenerationStartupGuard { + root, + gate, + binding, + parent, + parent_path, + })) +} +pub(crate) fn recheck_before_native( + guard: Option<&GenerationStartupGuard>, + database: &Path, +) -> Result<(), NativeError> { + if let Some(guard) = guard { + let memory = + CleanupMemory::new(64 * 1024).map_err(|_| invalid("generation_startup_memory"))?; + let current = load_gate(&guard.root, &memory, None)? + .ok_or_else(|| invalid("generation_startup_gate_missing"))?; + if current.binding != guard.binding.binding + || current.gate != guard.binding.gate + || locks::file_identity(&guard.root.open_readonly_file(START_GATE)?)? + != locks::file_identity(guard.gate.file())? + { + return Err(invalid("generation_startup_gate_replaced")); + } + check_startup(&guard.root, guard.binding, &guard.parent_path, guard.parent) + } else { + let new = before_native_open(database)?; + drop(new); + Ok(()) + } +} + +/// Fixed transport images and recovery decisions are prepared and charged +/// before actual exclusion. Admission compares exact source stamps only. +#[derive(Clone, Copy)] +struct TransportStep { + file: u8, + offset: u64, + len: usize, + bytes: [u8; controls::INTENT_BYTES], +} +pub(crate) struct RemovalScratch { + binding: GateBinding, + gate_header: [u8; 72], + source: [FixedFileStamp; 4], + initial: GateState, + removing: GateState, + origin_image: [u8; ORIGIN_BYTES], + final_image: [u8; FINAL_BYTES], + resource: [u8; 83], + manifest: [u8; 83], + steps: [TransportStep; 8], + count: usize, + position: usize, + recovery_end: usize, + close_end: usize, + readback: [u8; controls::INTENT_BYTES], + names: [u8; 4096], + #[cfg(test)] + fail_after: Option, +} +impl RemovalScratch { + pub(crate) fn new( + root: &RootedDirectory, + origin: Origin, + final_record: Final, + proof_digest: [u8; 32], + memory: &CleanupMemory, + ) -> Result, NativeError> { + let _temporary = memory + .reserve( + std::mem::size_of::() * 2 + + controls::SELECTOR_BYTES + + controls::SLOT_BYTES * 3 + + controls::INTENT_BYTES + + controls::COMMIT_BYTES + + 8192, + ) + .map_err(|_| invalid("generation_removal_memory"))?; + let binding_file = root.open_readonly_file(START_BINDING)?; + let binding_stamp = stamp(&binding_file)?; + let binding = GateBinding::decode(&exact(&binding_file)?) + .ok_or_else(|| invalid("generation_gate_unknown"))?; + if binding.managed != origin.managed + || binding.parent != origin.scope + || binding.writer != origin.writer + || binding.binding != binding_stamp.identity() + || root.open_directory_readonly(b"generations")?.identity() != binding.generations + { + return Err(invalid("generation_gate_origin_mismatch")); + } + let selector = root.open_readonly_file(START_SELECTOR)?; + let selector_stamp = stamp(&selector)?; + let image = exact::<{ controls::SELECTOR_BYTES }>(&selector)?; + if selector_stamp.identity() != binding.selector { + return Err(invalid("generation_gate_selector_changed")); + } + let mut stamps = [selector_stamp; 2]; + let mut images = [[0; controls::SLOT_BYTES]; 2]; + let mut slots = [None; 2]; + for i in 0..2 { + let file = root.open_readonly_file(START_DATA[i])?; + stamps[i] = stamp(&file)?; + if stamps[i].identity() != binding.data[i] { + return Err(invalid("generation_gate_slot_changed")); + } + images[i] = exact(&file)?; + slots[i] = controls::decode_slot(&images[i], &binding.nonce).ok(); + } + let commit = controls::decode_commit(&image[..controls::COMMIT_BYTES], &binding.nonce).ok(); + let intent = controls::decode_intent(&image[controls::COMMIT_BYTES..], &binding.nonce).ok(); + let step = controls::recovery_step(commit, intent, slots); + let mut allocation = MemoryBox::::new_uninit(memory) + .map_err(|_| invalid("generation_removal_memory"))?; + let pointer = allocation.as_mut_ptr(); + // SAFETY: the charged allocation has exact Self layout. Every field is + // initialized once, and the zeroed transport entries contain only + // integer fields and byte arrays. No fallible operation runs until the + // fully initialized value acquires its ordinary RAII owner below. + unsafe { + macro_rules! field { + ($name:ident, $value:expr) => { + std::ptr::addr_of_mut!((*pointer).$name).write($value) + }; + } + field!(binding, binding); + field!( + gate_header, + locks::encode_coordination_incarnation(binding.nonce) + ); + field!( + source, + [binding_stamp, selector_stamp, stamps[0], stamps[1]] + ); + field!(initial, GateState::Idle); + field!(origin_image, origin.encode()); + field!(final_image, final_record.encode()); + field!(resource, resource_name()); + field!(manifest, resource_name_for(b"manifest.json")); + field!( + removing, + GateState::Removing { + generation: origin.generation, + len: origin.generation_len, + directory: origin.directory, + origin: origin.receipt, + final_record: final_record.receipt, + proof_digest, + } + ); + std::ptr::addr_of_mut!((*pointer).steps).write_bytes(0, 1); + field!(count, 0); + field!(position, 0); + field!(recovery_end, 0); + field!(close_end, 0); + std::ptr::addr_of_mut!((*pointer).readback).write_bytes(0, 1); + std::ptr::addr_of_mut!((*pointer).names).write_bytes(0, 1); + #[cfg(test)] + field!(fail_after, None); + } + // SAFETY: the block above initialized every field without an unwind. + let mut prepared = unsafe { allocation.assume_init() }; + let current = match step { + controls::RecoveryStep::Committed(c) => c, + controls::RecoveryStep::RewriteIntendedSlot(i) + | controls::RecoveryStep::CommitIntendedSlot(i) => { + let intended: &[u8; controls::SLOT_BYTES] = image[controls::COMMIT_BYTES + 96 + ..controls::COMMIT_BYTES + 96 + controls::SLOT_BYTES] + .try_into() + .unwrap(); + if matches!(step, controls::RecoveryStep::RewriteIntendedSlot(_)) { + prepared.push(1 + i.target as u8, 0, intended)?; + } + images[i.target] = *intended; + let next = controls::CommitFact { + generation: i.after, + active: i.target, + slot_digest: i.slot_digest, + }; + let mut bytes = [0; controls::COMMIT_BYTES]; + controls::encode_commit(&mut bytes, &binding.nonce, next) + .map_err(|_| invalid("generation_gate_codec"))?; + prepared.push(0, 0, &bytes)?; + next + } + controls::RecoveryStep::Unknown => { + return Err(invalid("generation_gate_recovery_unknown")) + } + }; + prepared.recovery_end = prepared.count; + let slot = controls::decode_slot(&images[current.active], &binding.nonce) + .map_err(|_| invalid("generation_gate_unknown_slot"))?; + prepared.initial = GateState::decode(&images[current.active][56..56 + slot.payload_len]) + .ok_or_else(|| invalid("generation_gate_unknown_state"))?; + if prepared.initial != GateState::Idle && prepared.initial != prepared.removing { + return Err(invalid("generation_other_removal_pending")); + } + let removing = prepared.removing; + let closing = if prepared.initial == GateState::Idle { + prepared.transition(current, removing)? + } else { + current + }; + prepared.close_end = prepared.count; + prepared.transition(closing, GateState::Idle)?; + recheck(binding_stamp, &binding_file)?; + recheck(selector_stamp, &selector)?; + for i in 0..2 { + recheck(stamps[i], &root.open_readonly_file(START_DATA[i])?)?; + } + Ok(prepared) + } + /// Advisory observation only: no retirement/removal capability is minted. + pub(crate) fn observed_committed_idle(&self) -> bool { + self.initial == GateState::Idle && self.recovery_end == 0 + } + #[cfg(test)] + pub(crate) fn fail_after_transport_step(&mut self, step: usize) { + self.fail_after = Some(step); + } + fn push(&mut self, file: u8, offset: u64, bytes: &[u8]) -> Result<(), NativeError> { + if self.count == self.steps.len() || bytes.len() > controls::INTENT_BYTES { + return Err(invalid("generation_gate_transport_capacity")); + } + let step = &mut self.steps[self.count]; + step.file = file; + step.offset = offset; + step.len = bytes.len(); + step.bytes[..bytes.len()].copy_from_slice(bytes); + self.count += 1; + Ok(()) + } + fn transition( + &mut self, + current: controls::CommitFact, + state: GateState, + ) -> Result { + let generation = current + .generation + .checked_add(1) + .ok_or_else(|| invalid("generation_gate_sequence_overflow"))?; + let mut slot = [0; controls::SLOT_BYTES]; + let fact = + controls::encode_slot(&mut slot, &self.binding.nonce, generation, &state.encode()) + .map_err(|_| invalid("generation_gate_codec"))?; + let mut intent = [0; controls::INTENT_BYTES]; + let target = controls::encode_intent(&mut intent, &self.binding.nonce, current, &slot) + .map_err(|_| invalid("generation_gate_codec"))? + .target; + let next = controls::CommitFact { + generation, + active: target, + slot_digest: fact.digest, + }; + let mut commit = [0; controls::COMMIT_BYTES]; + controls::encode_commit(&mut commit, &self.binding.nonce, next) + .map_err(|_| invalid("generation_gate_codec"))?; + self.push(0, controls::COMMIT_BYTES as u64, &intent)?; + self.push(1 + target as u8, 0, &slot)?; + self.push(0, 0, &commit)?; + Ok(next) + } + fn recheck(&self, root: &RootedDirectory) -> Result<(), NativeError> { + if self.position != 0 { + return Err(invalid("generation_gate_preparation_consumed")); + } + for (i, name) in [START_BINDING, START_SELECTOR, START_DATA[0], START_DATA[1]] + .into_iter() + .enumerate() + { + recheck(self.source[i], &root.open_readonly_file(name)?)?; + } + Ok(()) + } + fn run( + &mut self, + root: &RootedDirectory, + end: usize, + grant: &mut MutationGrant, + ) -> Result<(), NativeError> { + while self.position < end { + #[cfg(test)] + if self.fail_after == Some(self.position) { + return Err(invalid("injected_generation_transport_fault")); + } + grant.check_time().map_err(grant_error)?; + let step = &self.steps[self.position]; + let (name, id) = match step.file { + 0 => (START_SELECTOR, self.binding.selector), + 1 => (START_DATA[0], self.binding.data[0]), + 2 => (START_DATA[1], self.binding.data[1]), + _ => return Err(invalid("generation_gate_transport_unknown")), + }; + let file = root.open_existing(name)?; + if locks::file_identity(&file)? != id { + return Err(invalid("generation_gate_control_replaced")); + } + let mut cursor = &file; + cursor.seek(SeekFrom::Start(step.offset))?; + cursor.write_all(&step.bytes[..step.len])?; + file.sync_all()?; + grant.check_time().map_err(grant_error)?; + cursor.seek(SeekFrom::Start(step.offset))?; + cursor.read_exact(&mut self.readback[..step.len])?; + if self.readback[..step.len] != step.bytes[..step.len] { + return Err(invalid("generation_gate_write_changed")); + } + self.position += 1; + } + Ok(()) + } +} +fn grant_error(_: super::maintenance_budget::GrantExhausted) -> NativeError { + invalid("generation_mutation_grant_exhausted") +} +pub(crate) struct GenerationRetirementGate { + _lock: FixedLockedFile, + binding: GateBinding, +} +pub(crate) fn admit_retirement_gate( + root: &RootedDirectory, + origin: Origin, + scratch: &RemovalScratch, + grant: &mut MutationGrant, +) -> Result { + grant.take_entries(9).map_err(grant_error)?; + scratch.recheck(root)?; + let binding = scratch.binding; + if binding.managed != origin.managed + || binding.parent != origin.scope + || binding.writer != origin.writer + { + return Err(invalid("generation_gate_origin_mismatch")); + } + let file = root.open_readonly_file(START_GATE)?; + if locks::file_identity(&file)? != binding.gate { + return Err(invalid("generation_gate_replaced")); + } + let lock = FixedLockedFile::try_lock(file, LockMode::Exclusive) + .map_err(|_| invalid("generation_gate_lock_invalid"))? + .ok_or_else(|| invalid("generation_gate_busy"))?; + if exact::<72>(lock.file())? != scratch.gate_header { + return Err(invalid("generation_gate_incarnation_changed")); + } + scratch.recheck(root)?; + Ok(GenerationRetirementGate { + _lock: lock, + binding, + }) +} +/// Actual empty-only removal effect; missing-name recovery is a distinct result. +pub(crate) struct RemovedGeneration { + _directory: FileIdentity, +} +impl GenerationRetirementGate { + // Root/directory identities, immutable origin/final proofs and the reserved grant stay separate inputs. + #[allow(clippy::too_many_arguments)] + pub(crate) fn remove_closed_root( + &self, + root: &RootedDirectory, + directory: &RootedDirectory, + origin: Origin, + final_record: Final, + proof_digest: [u8; 32], + scratch: &mut RemovalScratch, + grant: &mut MutationGrant, + ) -> Result { + grant.check_time().map_err(grant_error)?; + let removing = GateState::Removing { + generation: origin.generation, + len: origin.generation_len, + directory: origin.directory, + origin: origin.receipt, + final_record: final_record.receipt, + proof_digest, + }; + if root.identity() != self.binding.managed + || directory.identity() != origin.directory + || final_record.directory != origin.directory + || scratch.removing != removing + { + return Err(invalid("generation_removal_scope_changed")); + } + if scratch.initial == GateState::Idle + && (exact::(&directory.open_readonly_file(ORIGIN_NAME)?)? + != scratch.origin_image + || exact::(&directory.open_readonly_file(FINAL_NAME)?)? + != scratch.final_image) + { + return Err(invalid("generation_removal_receipt_changed")); + } + let resource = scratch.resource; + let manifest = scratch.manifest; + let names: [(&[u8], Option); 7] = [ + (ORIGIN_NAME, Some(origin.receipt)), + (FINAL_NAME, Some(final_record.receipt)), + (b"lease.lock", Some(origin.lease)), + (b".database-use-family.lock", Some(origin.family)), + (&resource, Some(origin.resource)), + (&manifest, Some(origin.manifest_resource)), + (b"graph.ldb.lock", origin.native_lock), + ]; + let mut seen = [false; 7]; + directory.visit_entries(&mut scratch.names, &mut |entry| { + grant + .check_time() + .map_err(|_| std::io::Error::from(std::io::ErrorKind::TimedOut))?; + let Some(i) = names.iter().position(|(name, _)| *name == entry) else { + return Err(locks::directory_not_empty_error()); + }; + let Some(id) = names[i].1 else { + return Err(std::io::ErrorKind::InvalidData.into()); + }; + if locks::file_identity(&directory.open_readonly_file(entry)?) + .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))? + != id + { + return Err(std::io::ErrorKind::InvalidData.into()); + } + seen[i] = true; + Ok(()) + })?; + if scratch.initial == GateState::Idle + && seen + .iter() + .zip(names) + .any(|(seen, (_, id))| id.is_some() && !seen) + { + return Err(invalid("generation_removal_control_missing")); + } + let mut held: [Option; 5] = std::array::from_fn(|_| None); + for i in 2..7 { + if seen[i] { + grant.check_time().map_err(grant_error)?; + held[i - 2] = Some( + FixedLockedFile::try_lock( + directory.open_readonly_file(names[i].0)?, + LockMode::Exclusive, + ) + .map_err(|_| invalid("generation_removal_lock_invalid"))? + .ok_or_else(|| invalid("generation_removal_lease_busy"))?, + ); + } + } + scratch.run(root, scratch.close_end, grant)?; + for i in 0..7 { + if seen[i] { + grant.check_time().map_err(grant_error)?; + directory.unlink_file(names[i].0, names[i].1.unwrap())?; + } + } + // Names are gone under the retained writer/startup gate. Windows must + // release descendant handles before removing their containing directory. + drop(held); + grant.check_time().map_err(grant_error)?; + directory.sync_namespace()?; + let generations = root.open_directory(b"generations")?; + if generations.identity() != self.binding.generations { + return Err(invalid("generation_namespace_replaced")); + } + let (name, n) = gen_name(&origin.generation, origin.generation_len)?; + grant.check_time().map_err(grant_error)?; + generations.remove_empty_directory(&name[..n], origin.directory)?; + generations.sync_namespace()?; + Ok(RemovedGeneration { + _directory: origin.directory, + }) + } + pub(crate) fn complete_missing( + &self, + root: &RootedDirectory, + origin: Origin, + final_record: Final, + proof_digest: [u8; 32], + scratch: &mut RemovalScratch, + grant: &mut MutationGrant, + ) -> Result<(), NativeError> { + if scratch.removing + != (GateState::Removing { + generation: origin.generation, + len: origin.generation_len, + directory: origin.directory, + origin: origin.receipt, + final_record: final_record.receipt, + proof_digest, + }) + || (scratch.initial == GateState::Idle && scratch.position < scratch.close_end) + { + return Err(invalid("generation_missing_without_removal_intent")); + } + scratch.run(root, scratch.recovery_end, grant)?; + let generations = root.open_directory(b"generations")?; + if generations.identity() != self.binding.generations { + return Err(invalid("generation_namespace_changed")); + } + let (name, n) = gen_name(&origin.generation, origin.generation_len)?; + match generations.open_directory_readonly(&name[..n]) { + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), + _ => Err(invalid("generation_removal_name_reappeared")), + } + } + pub(crate) fn finish_removed( + &self, + root: &RootedDirectory, + origin: Origin, + final_record: Final, + proof_digest: [u8; 32], + scratch: &mut RemovalScratch, + grant: &mut MutationGrant, + ) -> Result<(), NativeError> { + self.complete_missing(root, origin, final_record, proof_digest, scratch, grant)?; + scratch.run(root, scratch.count, grant) + } +} + +/// Genuine successor disposition of an unreceipted predecessor. This certifies +/// closure only; payload origin still requires complete controlled association. +pub(crate) const LEGACY_FINAL_NAME: &[u8] = b".generation-legacy-final"; +#[derive(Clone, Copy)] +pub(crate) struct LegacyClosedFile { + pub(crate) id: FileIdentity, + pub(crate) len: u64, + pub(crate) stamp_digest: [u8; 32], +} +#[derive(Clone, Copy)] +pub(crate) struct LegacyFinal { + pub(crate) generation: [u8; 128], + pub(crate) generation_len: u8, + pub(crate) successor_id: [u8; 128], + pub(crate) successor_len: u8, + pub(crate) scope: GenerationOriginScope, + pub(crate) managed: FileIdentity, + pub(crate) writer: FileIdentity, + pub(crate) directory: FileIdentity, + pub(crate) receipt: FileIdentity, + pub(crate) lease: FileIdentity, + pub(crate) family: FileIdentity, + pub(crate) resource: FileIdentity, + pub(crate) manifest_resource: FileIdentity, + pub(crate) database: LegacyClosedFile, + pub(crate) manifest: LegacyClosedFile, + pub(crate) successor: FileIdentity, + pub(crate) successor_origin: FileIdentity, + pub(crate) successor_digest: [u8; 32], + pub(crate) operation: [u8; 32], + pub(crate) native_lock: Option, +} +impl LegacyFinal { + pub(crate) fn encode(self) -> [u8; 1024] { + let mut b = [0; 1024]; + b[..8].copy_from_slice(b"CGLEGF01"); + b[8..16].copy_from_slice(&1u64.to_le_bytes()); + b[16] = self.generation_len; + b[17] = self.successor_len; + b[32..160].copy_from_slice(&self.generation); + b[160..288].copy_from_slice(&self.successor_id); + for (o, id) in [ + (288, self.scope.root), + (304, self.scope.coordination), + (320, self.scope.cohort), + (336, self.scope.publication_binding), + (384, self.managed), + (400, self.writer), + (416, self.directory), + (432, self.receipt), + (448, self.lease), + (464, self.family), + (480, self.resource), + (496, self.manifest_resource), + (512, self.database.id), + (568, self.manifest.id), + (624, self.successor), + (640, self.successor_origin), + ] { + id_put(&mut b[o..o + 16], id); + } + b[352..384].copy_from_slice(&self.scope.incarnation); + b[528..536].copy_from_slice(&self.database.len.to_le_bytes()); + b[536..568].copy_from_slice(&self.database.stamp_digest); + b[584..592].copy_from_slice(&self.manifest.len.to_le_bytes()); + b[592..624].copy_from_slice(&self.manifest.stamp_digest); + b[656..688].copy_from_slice(&self.successor_digest); + b[688..720].copy_from_slice(&self.operation); + if let Some(id) = self.native_lock { + b[720] = 1; + id_put(&mut b[728..744], id); + } + let hash = digest(&b[..992]); + b[992..].copy_from_slice(&hash); + b + } + pub(crate) fn decode(b: &[u8; 1024]) -> Option { + if &b[..8] != b"CGLEGF01" + || u64::from_le_bytes(b[8..16].try_into().ok()?) != 1 + || b[18..32] + .iter() + .chain(b[721..728].iter()) + .chain(b[744..992].iter()) + .any(|v| *v != 0) + || b[720] > 1 + || (b[720] == 0 && b[728..744].iter().any(|v| *v != 0)) + || digest(&b[..992]) != b[992..] + || b[352..384].iter().all(|v| *v == 0) + || b[688..720].iter().all(|v| *v == 0) + { + return None; + } + let generation = b[32..160].try_into().ok()?; + let successor_id = b[160..288].try_into().ok()?; + gen_name(&generation, b[16]).ok()?; + gen_name(&successor_id, b[17]).ok()?; + if generation == successor_id { + return None; + } + Some(Self { + generation, + generation_len: b[16], + successor_id, + successor_len: b[17], + scope: GenerationOriginScope { + root: id_get(&b[288..304]), + coordination: id_get(&b[304..320]), + cohort: id_get(&b[320..336]), + publication_binding: id_get(&b[336..352]), + incarnation: b[352..384].try_into().ok()?, + }, + managed: id_get(&b[384..400]), + writer: id_get(&b[400..416]), + directory: id_get(&b[416..432]), + receipt: id_get(&b[432..448]), + lease: id_get(&b[448..464]), + family: id_get(&b[464..480]), + resource: id_get(&b[480..496]), + manifest_resource: id_get(&b[496..512]), + database: LegacyClosedFile { + id: id_get(&b[512..528]), + len: u64::from_le_bytes(b[528..536].try_into().ok()?), + stamp_digest: b[536..568].try_into().ok()?, + }, + manifest: LegacyClosedFile { + id: id_get(&b[568..584]), + len: u64::from_le_bytes(b[584..592].try_into().ok()?), + stamp_digest: b[592..624].try_into().ok()?, + }, + successor: id_get(&b[624..640]), + successor_origin: id_get(&b[640..656]), + successor_digest: b[656..688].try_into().ok()?, + operation: b[688..720].try_into().ok()?, + native_lock: (b[720] == 1).then(|| id_get(&b[728..744])), + }) + } +} +pub(crate) fn read_legacy_final( + directory: &RootedDirectory, + managed: FileIdentity, +) -> Result<(LegacyFinal, [u8; 1024], FixedFileStamp), NativeError> { + let file = directory.open_readonly_file(LEGACY_FINAL_NAME)?; + let before = stamp(&file)?; + let bytes = exact(&file)?; + let fact = + LegacyFinal::decode(&bytes).ok_or_else(|| invalid("legacy_generation_final_unknown"))?; + if fact.managed != managed + || fact.directory != directory.identity() + || fact.receipt != before.identity() + { + return Err(invalid("legacy_generation_final_changed")); + } + Ok((fact, bytes, before)) +} diff --git a/src/storage/maintenance_generations.rs b/src/storage/maintenance_generations.rs new file mode 100644 index 00000000..466203de --- /dev/null +++ b/src/storage/maintenance_generations.rs @@ -0,0 +1,1883 @@ +//! Superseded generation facts and role-scoped mutation admission. Genuine +//! publication and FINAL disposition are distinct from READY or file hashes. +use super::inventory::MaintenanceTarget; +use super::layout::ManagedLayout; +use super::locks::{ + self, FileIdentity, FixedFileStamp, FixedLockedFile, LockMode, RootedDirectory, +}; +use super::maintenance_admission::{ + self, AdmissionError, EpochAdvance, PreparedScopedInvalidation, PreparedTargetBinding, + RootOriginBinding, +}; +use super::maintenance_budget::{CleanupMemory, MemoryBox, MemoryReservation, MutationGrant}; +use super::maintenance_ownership::{ReferenceDomain, ReferenceEpoch, ReferenceMutation}; +use crate::error::NativeError; +use std::io::Write; + +#[cfg(test)] +use super::maintenance_generation_gate::gen_name; +use super::maintenance_generation_gate::{ + self as gate, component, digest, exact, fixed_id, invalid, open_generation, read_current, + read_final, read_origin, recheck, resource_name, stamp, BoundLeaf, Final, + GenerationOriginScope, Origin, FINAL_BYTES, FINAL_NAME, ORIGIN_BYTES, ORIGIN_NAME, PAYLOADS, +}; +#[cfg(test)] +pub(crate) use super::maintenance_generation_gate::{ + observe_generation_disposition, GenerationDisposition, +}; +impl From for GenerationOriginScope { + fn from(s: RootOriginBinding) -> Self { + Self { + root: s.root, + coordination: s.coordination, + cohort: s.cohort, + publication_binding: s.publication_binding, + incarnation: s.incarnation, + } + } +} +impl From for RootOriginBinding { + fn from(s: GenerationOriginScope) -> Self { + Self { + root: s.root, + coordination: s.coordination, + cohort: s.cohort, + publication_binding: s.publication_binding, + incarnation: s.incarnation, + } + } +} + +fn admission_invalid() -> AdmissionError { + AdmissionError::Invalid(locks::FixedControlProblem::Changed) +} +pub(crate) const GENERATION_PROOF_BYTES: usize = 4096; +pub(crate) const MAX_GENERATION_LEAVES: usize = 14; +const NAMES: [&[u8]; MAX_GENERATION_LEAVES] = [ + b"graph.ldb", + b"manifest.json", + b"metadata.json", + b"READY", + b"graph.ldb.wal", + b"graph.ldb.tmp", + b"graph.ldb.search.meta.json", + b"graph.ldb.search.lexicon.bin", + b"graph.ldb.search.postings.bin", + b"graph.ldb.search.doc_lengths.bin", + b"graph.ldb.search.documents.bin", + b"graph.ldb.search.document_offsets.bin", + ORIGIN_NAME, + FINAL_NAME, +]; +/// The API owns this unforgeable token's construction after full native +/// verification, required recovery and a fresh CURRENT check, under its actual +/// retained writer and actual new-generation read lease. +pub(crate) fn seal_verified_generation( + verified: &crate::api::NativeVerifiedGeneration<'_>, +) -> Result<(), NativeError> { + let layout = verified.layout(); + let writer = verified.writer(); + if !writer.verifies(&layout.writer_lock_path(), LockMode::Exclusive)? { + return Err(invalid("generation_writer_changed")); + } + let domain = ReferenceDomain::managed(layout)?; + let mutation = ReferenceMutation::begin_in_domain(&domain)?; + let mut capability = mutation + .artifact_root_producer_capability()? + .ok_or_else(|| invalid("generation_producer_scope_untracked"))?; + let lease = super::maintenance_controls::try_begin_root_publication(&mut capability)? + .ok_or_else(|| invalid("generation_publication_scope_busy"))?; + let root = RootedDirectory::open( + layout.storage_root(), + locks::path_identity(layout.storage_root())?, + )?; + let (id, len) = fixed_id(verified.generation_id())?; + let directory = open_generation(&root, &id, len)?; + let current = read_current(&root)?; + if current.0 != id || current.1 != len { + return Err(invalid("generation_current_changed")); + } + let origin = match read_origin(&directory, root.identity()) { + Ok((origin, bytes, _)) => { + let expected_scope = RootOriginBinding { + root: lease.binding().root, + coordination: lease.binding().coordination, + cohort: lease.root_cohort_identity(), + publication_binding: lease.publication_binding_identity(), + incarnation: lease.binding().incarnation, + }; + if origin.generation != id + || origin.generation_len != len + || origin.scope != expected_scope.into() + || origin.writer != locks::path_identity(&layout.writer_lock_path())? + { + return Err(invalid("generation_existing_origin_changed")); + } + (origin, bytes) + } + Err(NativeError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { + let record_file = directory.create_new(ORIGIN_NAME)?; + let receipt = locks::file_identity(&record_file)?; + let mut operation = [0; 32]; + getrandom::fill(&mut operation) + .map_err(|_| invalid("generation_origin_entropy_unavailable"))?; + let mut origin = Origin { + generation: id, + generation_len: len, + scope: GenerationOriginScope { + root: lease.binding().root, + coordination: lease.binding().coordination, + cohort: lease.root_cohort_identity(), + publication_binding: lease.publication_binding_identity(), + incarnation: lease.binding().incarnation, + }, + managed: root.identity(), + writer: locks::path_identity(&layout.writer_lock_path())?, + directory: directory.identity(), + receipt, + operation, + lease: locks::file_identity(&directory.open_readonly_file(b"lease.lock")?)?, + family: locks::file_identity( + &directory.open_readonly_file(b".database-use-family.lock")?, + )?, + resource: locks::file_identity(&directory.open_readonly_file(&resource_name())?)?, + manifest_resource: locks::file_identity(&directory.open_readonly_file( + &super::maintenance_generation_gate::resource_name_for(b"manifest.json"), + )?)?, + native_lock: match directory.open_readonly_file(b"graph.ldb.lock") { + Ok(f) => Some(locks::file_identity(&f)?), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e.into()), + }, + leaves: [None; PAYLOADS], + }; + for (i, name) in NAMES[..PAYLOADS].iter().enumerate() { + let file = match directory.open_readonly_file(name) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound && i >= 4 => { + continue + } + Err(error) => return Err(error.into()), + }; + let before = stamp(&file)?; + let (n, hash) = super::maintenance_origin::hash_payload(&file)?; + recheck(before, &file)?; + origin.leaves[i] = Some(BoundLeaf { + id: before.identity(), + len: n, + hash, + stamp_digest: before.binding_digest(), + }); + } + let bytes = origin.encode(); + if Origin::decode(&bytes).is_none() { + return Err(invalid("generation_bundle_incomplete")); + } + let mut cursor = &record_file; + cursor.write_all(&bytes)?; + record_file.sync_all()?; + directory.sync_namespace()?; + (origin, bytes) + } + Err(error) => return Err(error), + }; + // Only a prior genuine published receipt can acquire FINAL. An old READY + // bundle without such evidence is preserved for controlled association. + if let Some(previous) = verified.previous_generation_id() { + let (old_id, old_len) = fixed_id(previous)?; + if old_id == id { + return Err(invalid("generation_self_retirement")); + } + let old = open_generation(&root, &old_id, old_len)?; + match read_origin(&old, root.identity()) { + Ok((prior, prior_bytes, _)) => { + if prior.generation != old_id + || prior.generation_len != old_len + || prior.scope != origin.0.scope + || prior.writer != origin.0.writer + { + return Err(invalid("generation_predecessor_changed")); + } + match old.open_readonly_file(FINAL_NAME) { + Ok(_) => { + read_final(&old, prior, &prior_bytes)?; + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + let file = old.create_new(FINAL_NAME)?; + let record = Final { + generation: old_id, + generation_len: old_len, + managed: root.identity(), + directory: old.identity(), + origin: prior.receipt, + origin_digest: digest(&prior_bytes), + receipt: locks::file_identity(&file)?, + successor: origin.0.directory, + successor_origin: origin.0.receipt, + successor_id: id, + successor_len: len, + successor_digest: digest(&origin.1), + }; + let mut cursor = &file; + cursor.write_all(&record.encode())?; + file.sync_all()?; + old.sync_namespace()?; + } + Err(error) => return Err(error.into()), + } + } + Err(NativeError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { + match old.open_readonly_file(gate::LEGACY_FINAL_NAME) { + Ok(_) => { + let (closed, _, _) = gate::read_legacy_final(&old, root.identity())?; + if closed.scope != origin.0.scope + || closed.generation != old_id + || closed.generation_len != old_len + { + return Err(invalid("legacy_generation_final_scope_changed")); + } + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + let database = stamp(&old.open_readonly_file(b"graph.ldb")?)?; + let manifest = stamp(&old.open_readonly_file(b"manifest.json")?)?; + let file = old.create_new(gate::LEGACY_FINAL_NAME)?; + let mut operation = [0; 32]; + getrandom::fill(&mut operation) + .map_err(|_| invalid("legacy_final_entropy_unavailable"))?; + let closed = gate::LegacyFinal { + generation: old_id, + generation_len: old_len, + successor_id: id, + successor_len: len, + scope: origin.0.scope, + managed: root.identity(), + writer: origin.0.writer, + directory: old.identity(), + receipt: locks::file_identity(&file)?, + lease: locks::file_identity(&old.open_readonly_file(b"lease.lock")?)?, + family: locks::file_identity( + &old.open_readonly_file(b".database-use-family.lock")?, + )?, + resource: locks::file_identity( + &old.open_readonly_file(&resource_name())?, + )?, + manifest_resource: locks::file_identity( + &old.open_readonly_file(&gate::resource_name_for( + b"manifest.json", + ))?, + )?, + database: gate::LegacyClosedFile { + id: database.identity(), + len: database.len(), + stamp_digest: database.binding_digest(), + }, + manifest: gate::LegacyClosedFile { + id: manifest.identity(), + len: manifest.len(), + stamp_digest: manifest.binding_digest(), + }, + successor: origin.0.directory, + successor_origin: origin.0.receipt, + successor_digest: digest(&origin.1), + operation, + native_lock: match old.open_readonly_file(b"graph.ldb.lock") { + Ok(f) => Some(locks::file_identity(&f)?), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e.into()), + }, + }; + let mut cursor = &file; + cursor.write_all(&closed.encode())?; + file.sync_all()?; + old.sync_namespace()?; + } + Err(e) => return Err(e.into()), + } + } + Err(error) => return Err(error), + } + } + if read_current(&root)?.2 != current.2 || !lease.revalidate_existing()? { + return Err(invalid("generation_verified_publication_changed")); + } + Ok(()) +} + +#[derive(Clone, Copy, Debug)] +pub(crate) struct GenerationLeaf { + pub(crate) index: u8, + + pub(crate) payload: FileIdentity, + pub(crate) byte_len: u64, + + stamp: FixedFileStamp, +} +impl GenerationLeaf { + pub(crate) fn stamp(&self) -> FixedFileStamp { + self.stamp + } + pub(crate) fn name(&self) -> &'static [u8] { + NAMES[usize::from(self.index)] + } +} +#[derive(Clone, Copy)] +pub(crate) struct GenerationInventory { + pub(crate) source_epoch: u64, + pub(crate) managed: FileIdentity, + pub(crate) directory: FileIdentity, + pub(crate) generation_id: [u8; 128], + pub(crate) generation_len: u8, + pub(crate) leaves: [Option; MAX_GENERATION_LEAVES], + origin: Origin, + origin_image: [u8; ORIGIN_BYTES], + final_image: [u8; FINAL_BYTES], +} +impl GenerationInventory { + /// Facts to persist as a complete, digest-bound cohort prefix BEFORE the + /// first leaf detaches. These bytes carry no mutation authority themselves. + pub(crate) fn encode_proof(&self) -> [u8; GENERATION_PROOF_BYTES] { + let mut bytes = [0; GENERATION_PROOF_BYTES]; + bytes[..8].copy_from_slice(b"CGGPR001"); + bytes[8..16].copy_from_slice(&self.source_epoch.to_le_bytes()); + bytes[32..32 + ORIGIN_BYTES].copy_from_slice(&self.origin_image); + bytes[2080..2080 + FINAL_BYTES].copy_from_slice(&self.final_image); + let hash = digest(&bytes[..GENERATION_PROOF_BYTES - 32]); + bytes[GENERATION_PROOF_BYTES - 32..].copy_from_slice(&hash); + bytes + } +} +#[derive(Debug, Clone, Copy, Default)] +pub(crate) struct GenerationReport { + pub(crate) inspected: u64, + pub(crate) final_generations: u64, + pub(crate) unknown: u64, + pub(crate) active: u64, + pub(crate) category_complete: bool, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum GenerationError { + Io(std::io::ErrorKind), + Unknown, + Changed, + Busy, + Cancelled, + ResourceLimit, +} +fn generation_error(error: NativeError) -> GenerationError { + match error { + NativeError::Io(error) => GenerationError::Io(error.kind()), + _ => GenerationError::Unknown, + } +} +fn generation_admission(error: AdmissionError) -> GenerationError { + match error { + AdmissionError::Io(error) => GenerationError::Io(error.kind()), + AdmissionError::Resource(_) => GenerationError::ResourceLimit, + AdmissionError::Invalid(locks::FixedControlProblem::Busy) => GenerationError::Busy, + _ => GenerationError::Changed, + } +} +/// Advisory owners are Send; no directory/lock authority survives preparation. +pub(crate) struct PreparedGeneration { + binding: MemoryBox, + scoped: MemoryBox, + layout: ManagedLayout, + inventory: MemoryBox, + selected: usize, + current: FixedFileStamp, + origin_stamp: Option, + final_stamp: Option, + detached: Option<[u8; 73]>, + _floor: MemoryReservation, +} +pub(crate) struct GenerationMutation<'a> { + managed: &'a RootedDirectory, + parent: &'a RootedDirectory, + leaf: &'a GenerationLeaf, +} +impl GenerationMutation<'_> { + pub(crate) fn managed_root(&self) -> &RootedDirectory { + self.managed + } + pub(crate) fn source_parent(&self) -> &RootedDirectory { + self.parent + } + pub(crate) fn leaf(&self) -> &GenerationLeaf { + self.leaf + } +} +fn observe_scope_matches(origin: Origin, scope: RootOriginBinding) -> bool { + origin.scope == scope.into() +} +fn current_origin_scope( + layout: &ManagedLayout, + memory: &CleanupMemory, +) -> Result { + let path = layout.artifacts_root(); + let root = RootedDirectory::open_readonly_root( + &path, + locks::path_identity(&path).map_err(generation_error)?, + ) + .map_err(|e| GenerationError::Io(e.kind()))?; + let scope = super::maintenance_origin::observe_artifact_root_scope(&root, memory) + .map_err(generation_error)? + .ok_or(GenerationError::Unknown)?; + Ok(RootOriginBinding { + root: scope.root, + coordination: scope.coordination, + cohort: scope.cohort, + publication_binding: scope.publication_binding, + incarnation: scope.incarnation, + }) +} +fn capture_leaf( + directory: &RootedDirectory, + index: usize, + + bound: BoundLeaf, +) -> Result { + let file = directory + .open_readonly_file(NAMES[index]) + .map_err(|e| GenerationError::Io(e.kind()))?; + let before = stamp(&file).map_err(generation_error)?; + if before.identity() != bound.id + || before.len() != bound.len + || before.binding_digest() != bound.stamp_digest + { + return Err(GenerationError::Changed); + } + Ok(GenerationLeaf { + index: index as u8, + payload: bound.id, + byte_len: bound.len, + stamp: before, + }) +} +fn hash_leaf( + directory: &RootedDirectory, + index: usize, + + bound: BoundLeaf, +) -> Result { + let file = directory + .open_readonly_file(NAMES[index]) + .map_err(|e| GenerationError::Io(e.kind()))?; + let before = stamp(&file).map_err(generation_error)?; + if before.identity() != bound.id + || before.len() != bound.len + || before.binding_digest() != bound.stamp_digest + { + return Err(GenerationError::Changed); + } + let (len, hash) = super::maintenance_origin::hash_payload(&file).map_err(generation_error)?; + recheck(before, &file).map_err(generation_error)?; + if len != bound.len || hash != bound.hash { + return Err(GenerationError::Changed); + } + Ok(GenerationLeaf { + index: index as u8, + payload: bound.id, + byte_len: len, + stamp: before, + }) +} +fn control_leaf( + directory: &RootedDirectory, + index: usize, + + expected: &[u8], +) -> Result { + let file = directory + .open_readonly_file(NAMES[index]) + .map_err(|e| GenerationError::Io(e.kind()))?; + let before = stamp(&file).map_err(generation_error)?; + let (len, hash) = super::maintenance_origin::hash_payload(&file).map_err(generation_error)?; + if len != expected.len() as u64 || hash != digest(expected) { + return Err(GenerationError::Changed); + } + Ok(GenerationLeaf { + index: index as u8, + payload: before.identity(), + byte_len: len, + stamp: before, + }) +} +// Large prepared admission values finish construction in their own frame and +// remain charged on the heap through every callback. They carry no authority. +#[inline(never)] +fn boxed_generation_binding( + target: &MaintenanceTarget, + memory: &CleanupMemory, +) -> Result, GenerationError> { + let binding = maintenance_admission::prepare_target_binding(target, memory) + .map_err(generation_admission)?; + MemoryBox::new(binding, memory).map_err(|_| GenerationError::ResourceLimit) +} +#[inline(never)] +fn boxed_generation_invalidation( + domain: &ReferenceDomain, + memory: &CleanupMemory, +) -> Result, GenerationError> { + let scoped = maintenance_admission::prepare_scoped_invalidation(domain, memory) + .map_err(generation_admission)?; + MemoryBox::new(scoped, memory).map_err(|_| GenerationError::ResourceLimit) +} +#[inline(never)] +fn common( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + memory: &CleanupMemory, +) -> Result< + ( + ManagedLayout, + MemoryBox, + MemoryReservation, + ), + GenerationError, +> { + if target.direct_pair.is_some() { + return Err(GenerationError::Unknown); + } + let projected = super::maintenance_projection::project_binding(target, memory) + .map_err(|_| GenerationError::Changed)?; + if projected.direct.is_some() { + return Err(GenerationError::Unknown); + } + let storage = &projected.storage; + #[cfg(windows)] + const PATH_BYTES: usize = 65536; + #[cfg(not(windows))] + const PATH_BYTES: usize = 4096; + let floor = memory + .reserve( + std::mem::size_of::() + + std::mem::size_of::() * 4 + + 8 * PATH_BYTES + + 24576, + ) + .map_err(|_| GenerationError::ResourceLimit)?; + let layout = ManagedLayout::new(storage); + let actual = ReferenceDomain::managed(&layout).map_err(generation_error)?; + if !actual.same_scope(domain) || !domain.revalidate().map_err(generation_error)? { + return Err(GenerationError::Changed); + } + let binding = boxed_generation_binding(target, memory)?; + Ok((layout, binding, floor)) +} +/// Metadata-only fair discovery. IDs are emitted only after exact genuine +/// Origin+FINAL records validate; emission is advisory and owns no authority. +/// No payload hash, native open or exclusive family/generation probe occurs. +pub(crate) fn discover_generations( + target: &MaintenanceTarget, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + emit: &mut impl FnMut(&[u8]) -> Result<(), GenerationError>, +) -> Result { + if cancel() { + return Err(GenerationError::Cancelled); + } + #[cfg(windows)] + const DISCOVERY_PATH_BYTES: usize = 65536; + #[cfg(not(windows))] + const DISCOVERY_PATH_BYTES: usize = 4096; + let _floor = memory + .reserve( + 8 * DISCOVERY_PATH_BYTES + + 65536 + + std::mem::size_of::() * 3 + + ORIGIN_BYTES * 3 + + FINAL_BYTES * 3, + ) + .map_err(|_| GenerationError::ResourceLimit)?; + let projected = super::maintenance_projection::project_binding(target, memory) + .map_err(|_| GenerationError::Changed)?; + if projected.direct.is_some() { + return Ok(GenerationReport { + category_complete: true, + ..GenerationReport::default() + }); + } + let layout = ManagedLayout::new(&projected.storage); + let origin_scope = current_origin_scope(&layout, memory)?; + let mut binding = maintenance_admission::prepare_target_binding(target, memory) + .map_err(generation_admission)?; + let root = RootedDirectory::open_readonly_root( + layout.storage_root(), + locks::path_identity(layout.storage_root()).map_err(generation_error)?, + ) + .map_err(|e| GenerationError::Io(e.kind()))?; + let current = read_current(&root).map_err(generation_error)?; + let generations = root + .open_directory_readonly(b"generations") + .map_err(|e| GenerationError::Io(e.kind()))?; + let mut report = GenerationReport { + category_complete: true, + ..GenerationReport::default() + }; + let mut names = [0; 4096]; + let mut failure = None; + let visited = generations.visit_entries(&mut names, &mut |name| { + if cancel() { + failure = Some(GenerationError::Cancelled); + return Err(std::io::ErrorKind::Interrupted.into()); + } + report.inspected = report + .inspected + .checked_add(1) + .ok_or(std::io::ErrorKind::InvalidData)?; + let Some(id) = name.strip_prefix(b"gen-") else { + report.unknown = report.unknown.saturating_add(1); + report.category_complete = false; + return Ok(()); + }; + if !component(id) { + report.unknown = report.unknown.saturating_add(1); + report.category_complete = false; + return Ok(()); + } + let mut gen = [0; 128]; + gen[..id.len()].copy_from_slice(id); + if gen == current.0 && id.len() == usize::from(current.1) { + report.active = report.active.saturating_add(1); + return Ok(()); + } + let observed = (|| -> Result<(), GenerationError> { + let directory = generations + .open_directory_readonly(name) + .map_err(|e| GenerationError::Io(e.kind()))?; + let (origin, bytes, _) = + read_origin(&directory, root.identity()).map_err(generation_error)?; + if origin.generation != gen + || usize::from(origin.generation_len) != id.len() + || !observe_scope_matches(origin, origin_scope) + { + return Err(GenerationError::Changed); + } + read_final(&directory, origin, &bytes).map_err(generation_error)?; + let mut child_names = [0; 4096]; + directory + .visit_entries(&mut child_names, &mut |child| { + if !NAMES.contains(&child) + && child != b"lease.lock" + && child != b".database-use-family.lock" + && child != resource_name().as_slice() + && child != b"graph.ldb.lock" + { + report.unknown = report.unknown.saturating_add(1); + report.category_complete = false; + } + Ok(()) + }) + .map_err(|e| GenerationError::Io(e.kind()))?; + report.final_generations = report.final_generations.saturating_add(1); + emit(id)?; + Ok(()) + })(); + if let Err(error) = observed { + if matches!( + error, + GenerationError::Cancelled | GenerationError::ResourceLimit + ) { + failure = Some(error); + return Err(std::io::ErrorKind::Interrupted.into()); + } + report.unknown = report.unknown.saturating_add(1); + report.category_complete = false; + } + Ok(()) + }); + if let Some(error) = failure { + return Err(error); + } + visited.map_err(|e| GenerationError::Io(e.kind()))?; + if cancel() { + return Err(GenerationError::Cancelled); + } + if read_current(&root).map_err(generation_error)?.2 != current.2 + || !binding.is_current().map_err(generation_admission)? + { + return Err(GenerationError::Changed); + } + Ok(report) +} +/// One charged inventory is initialized once and remains behind its owner +/// through every admitted callback. Keeping this construction in its own small +/// frame avoids debug builds multiplying whole-cohort copies on the worker. +#[inline(never)] +fn allocate_inventory( + source_epoch: u64, + origin: &Origin, + origin_image: &[u8; ORIGIN_BYTES], + final_image: &[u8; FINAL_BYTES], + memory: &CleanupMemory, +) -> Result, GenerationError> { + MemoryBox::new( + GenerationInventory { + source_epoch, + managed: origin.managed, + directory: origin.directory, + generation_id: origin.generation, + generation_len: origin.generation_len, + leaves: [None; MAX_GENERATION_LEAVES], + origin: *origin, + origin_image: *origin_image, + final_image: *final_image, + }, + memory, + ) + .map_err(|_| GenerationError::ResourceLimit) +} + +/// Validate one exact selected payload, after fair metadata discovery selected +/// this category. Other cohort leaves get fixed producer-bound stamp checks. +#[inline(never)] +pub(crate) fn prepare_exact_generation( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + generation_id: &[u8], + selected: u8, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, GenerationError> { + if cancel() { + return Err(GenerationError::Cancelled); + } + if !component(generation_id) || usize::from(selected) >= PAYLOADS { + return Err(GenerationError::Unknown); + } + let (layout, mut binding, floor) = common(target, domain, memory)?; + let origin_scope = current_origin_scope(&layout, memory)?; + let root = RootedDirectory::open_readonly_root( + layout.storage_root(), + locks::path_identity(layout.storage_root()).map_err(generation_error)?, + ) + .map_err(|e| GenerationError::Io(e.kind()))?; + let current = read_current(&root).map_err(generation_error)?; + let mut id = [0; 128]; + id[..generation_id.len()].copy_from_slice(generation_id); + let len = generation_id.len() as u8; + if current.0 == id && current.1 == len { + return Err(GenerationError::Busy); + } + let directory = open_generation(&root, &id, len).map_err(generation_error)?; + let (origin, origin_image, _) = + read_origin(&directory, root.identity()).map_err(generation_error)?; + if origin.generation != id + || origin.generation_len != len + || !observe_scope_matches(origin, origin_scope) + { + return Err(GenerationError::Changed); + } + let (_, final_image, _) = + read_final(&directory, origin, &origin_image).map_err(generation_error)?; + let mut inventory = allocate_inventory( + expected.counter(), + &origin, + &origin_image, + &final_image, + memory, + )?; + for i in 0..PAYLOADS { + if cancel() { + return Err(GenerationError::Cancelled); + } + if let Some(bound) = origin.leaves[i] { + inventory.leaves[i] = Some(capture_leaf(&directory, i, bound)?); + } + } + inventory.leaves[usize::from(selected)] = Some(hash_leaf( + &directory, + usize::from(selected), + origin.leaves[usize::from(selected)].ok_or(GenerationError::Unknown)?, + )?); + inventory.leaves[12] = Some(control_leaf(&directory, 12, &origin_image)?); + inventory.leaves[13] = Some(control_leaf(&directory, 13, &final_image)?); + if cancel() { + return Err(GenerationError::Cancelled); + } + if read_current(&root).map_err(generation_error)?.2 != current.2 + || !binding.is_current().map_err(generation_admission)? + { + return Err(GenerationError::Changed); + } + let origin_stamp = inventory.leaves[12].map(|l| l.stamp); + let final_stamp = inventory.leaves[13].map(|l| l.stamp); + let prepared = PreparedGeneration { + binding, + scoped: boxed_generation_invalidation(domain, memory)?, + layout, + inventory, + selected: usize::from(selected), + current: current.2, + origin_stamp, + final_stamp, + detached: None, + _floor: floor, + }; + MemoryBox::new(prepared, memory).map_err(|_| GenerationError::ResourceLimit) +} +/// Resume only from the complete cohort prefix already authenticated by the +/// caller's admitted private stream. This consumes no native SDK or SQL and +/// never reconstructs origin from the remaining file names after DB removal. +pub(crate) fn prepare_generation_resume( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + _expected: &ReferenceEpoch, + proof: &[u8; GENERATION_PROOF_BYTES], + selected: usize, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, GenerationError> { + prepare_generation_resume_inner( + target, domain, _expected, proof, selected, None, memory, cancel, + ) +} +// Keep source selection, immutable closure proof and the shared memory account explicit in this role adapter. +#[allow(clippy::too_many_arguments)] +pub(crate) fn prepare_generation_detached( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + proof: &[u8; GENERATION_PROOF_BYTES], + selected: usize, + tombstone: [u8; 73], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, GenerationError> { + if &tombstone[..9] != b".retired-" + || !tombstone[9..] + .iter() + .all(|c| c.is_ascii_digit() || (b'a'..=b'f').contains(c)) + { + return Err(GenerationError::Unknown); + } + prepare_generation_resume_inner( + target, + domain, + expected, + proof, + selected, + Some(tombstone), + memory, + cancel, + ) +} +// Keep source selection, immutable closure proof and the shared memory account explicit in this role adapter. +#[allow(clippy::too_many_arguments)] +fn prepare_generation_resume_inner( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + _expected: &ReferenceEpoch, + proof: &[u8; GENERATION_PROOF_BYTES], + selected: usize, + detached: Option<[u8; 73]>, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, GenerationError> { + if cancel() { + return Err(GenerationError::Cancelled); + } + if selected >= MAX_GENERATION_LEAVES + || &proof[..8] != b"CGGPR001" + || proof[16..32] + .iter() + .chain(proof[2592..GENERATION_PROOF_BYTES - 32].iter()) + .any(|v| *v != 0) + || digest(&proof[..GENERATION_PROOF_BYTES - 32]) != proof[GENERATION_PROOF_BYTES - 32..] + { + return Err(GenerationError::Unknown); + } + let (layout, mut binding, floor) = common(target, domain, memory)?; + let origin_scope = current_origin_scope(&layout, memory)?; + let origin_image = proof[32..2080].try_into().unwrap(); + let final_image = proof[2080..2592].try_into().unwrap(); + let origin = Origin::decode(&origin_image).ok_or(GenerationError::Unknown)?; + let final_record = Final::decode(&final_image).ok_or(GenerationError::Unknown)?; + let original_epoch = u64::from_le_bytes(proof[8..16].try_into().unwrap()); + if final_record.origin_digest != digest(&origin_image) + || final_record.directory != origin.directory + || final_record.origin != origin.receipt + || final_record.generation != origin.generation + || !observe_scope_matches(origin, origin_scope) + { + return Err(GenerationError::Changed); + } + let root = RootedDirectory::open_readonly_root(layout.storage_root(), origin.managed) + .map_err(|e| GenerationError::Io(e.kind()))?; + let current = read_current(&root).map_err(generation_error)?; + if current.0 == origin.generation && current.1 == origin.generation_len { + return Err(GenerationError::Changed); + } + let directory = open_generation(&root, &origin.generation, origin.generation_len) + .map_err(generation_error)?; + if directory.identity() != origin.directory { + return Err(GenerationError::Changed); + } + // Origin may be detached only after every payload. FINAL remains the last + // public control and is matched to the durable original cohort bytes. + let origin_stamp = match directory.open_readonly_file(ORIGIN_NAME) { + Ok(file) => { + let s = stamp(&file).map_err(generation_error)?; + if exact::(&file).map_err(generation_error)? != origin_image + || s.identity() != origin.receipt + { + return Err(GenerationError::Changed); + } + Some(s) + } + Err(error) + if error.kind() == std::io::ErrorKind::NotFound + && (selected == 13 || (selected == 12 && detached.is_some())) => + { + None + } + Err(error) => return Err(GenerationError::Io(error.kind())), + }; + let final_stamp = match directory.open_readonly_file(FINAL_NAME) { + Ok(file) => { + let actual = stamp(&file).map_err(generation_error)?; + if exact::(&file).map_err(generation_error)? != final_image + || actual.identity() != final_record.receipt + { + return Err(GenerationError::Changed); + } + Some(actual) + } + Err(error) + if error.kind() == std::io::ErrorKind::NotFound + && selected == 13 + && detached.is_some() => + { + None + } + Err(error) => return Err(GenerationError::Io(error.kind())), + }; + let leaf = if let Some(name) = detached { + let file = root + .open_readonly_file(&name) + .map_err(|e| GenerationError::Io(e.kind()))?; + let before = stamp(&file).map_err(generation_error)?; + let (byte_len, hash) = + super::maintenance_origin::hash_payload(&file).map_err(generation_error)?; + let (wanted_id, wanted_len, wanted_hash) = if selected < PAYLOADS { + let bound = origin.leaves[selected].ok_or(GenerationError::Unknown)?; + (bound.id, bound.len, bound.hash) + } else if selected == 12 { + (origin.receipt, ORIGIN_BYTES as u64, digest(&origin_image)) + } else { + ( + final_record.receipt, + FINAL_BYTES as u64, + digest(&final_image), + ) + }; + if before.identity() != wanted_id || byte_len != wanted_len || hash != wanted_hash { + return Err(GenerationError::Changed); + } + recheck(before, &file).map_err(generation_error)?; + GenerationLeaf { + index: selected as u8, + payload: wanted_id, + byte_len, + + stamp: before, + } + } else if selected < PAYLOADS { + hash_leaf( + &directory, + selected, + origin.leaves[selected].ok_or(GenerationError::Unknown)?, + )? + } else { + control_leaf( + &directory, + selected, + if selected == 12 { + &origin_image[..] + } else { + &final_image[..] + }, + )? + }; + let mut inventory = + allocate_inventory(original_epoch, &origin, &origin_image, &final_image, memory)?; + inventory.leaves[selected] = Some(leaf); + if cancel() { + return Err(GenerationError::Cancelled); + } + if !binding.is_current().map_err(generation_admission)? { + return Err(GenerationError::Changed); + } + MemoryBox::new( + PreparedGeneration { + binding, + scoped: boxed_generation_invalidation(domain, memory)?, + layout, + inventory, + selected, + current: current.2, + origin_stamp, + final_stamp, + detached, + _floor: floor, + }, + memory, + ) + .map_err(|_| GenerationError::ResourceLimit) +} +impl PreparedGeneration { + pub(crate) fn inventory(&self) -> &GenerationInventory { + &self.inventory + } + pub(crate) fn leaf(&self) -> &GenerationLeaf { + self.inventory.leaves[self.selected].as_ref().unwrap() + } + #[cfg(test)] + pub(crate) fn source_epoch_counter(&self) -> u64 { + self.inventory.source_epoch + } + /// Local validation/opens are charged separately before E; caller supplies + /// the complete bounded journal/rename/unlink transition effect count. + pub(crate) fn effect_entries(&self) -> usize { + 24 + } + pub(crate) fn with_admitted( + &mut self, + grant: &mut MutationGrant, + effect_entries: usize, + apply: &mut impl FnMut( + &GenerationMutation<'_>, + &mut MutationGrant, + ) -> Result<(), AdmissionError>, + ) -> Result { + grant.take_entries(self.effect_entries())?; + let root = RootedDirectory::open(self.layout.storage_root(), self.inventory.managed)?; + grant.check_time()?; + let writer_file = root.open_readonly_file(b"writer.lock")?; + if locks::file_identity(&writer_file).map_err(|_| admission_invalid())? + != self.inventory.origin.writer + { + return Err(admission_invalid()); + } + let _writer = FixedLockedFile::try_lock(writer_file, LockMode::Exclusive)? + .ok_or(AdmissionError::Invalid(locks::FixedControlProblem::Busy))?; + grant.check_time()?; + let directory = open_generation( + &root, + &self.inventory.generation_id, + self.inventory.generation_len, + ) + .map_err(|_| admission_invalid())?; + if directory.identity() != self.inventory.directory { + return Err(admission_invalid()); + } + let mut held: [Option; 3] = std::array::from_fn(|_| None); + for (i, name, id) in [ + (0, b"lease.lock".as_slice(), self.inventory.origin.lease), + ( + 1, + b".database-use-family.lock".as_slice(), + self.inventory.origin.family, + ), + ( + 2, + resource_name().as_slice(), + self.inventory.origin.resource, + ), + ] { + grant.check_time()?; + let file = directory.open_readonly_file(name)?; + if locks::file_identity(&file).map_err(|_| admission_invalid())? != id { + return Err(admission_invalid()); + } + held[i] = Some( + FixedLockedFile::try_lock(file, LockMode::Exclusive)? + .ok_or(AdmissionError::Invalid(locks::FixedControlProblem::Busy))?, + ); + } + let leaf = *self.leaf(); + let binding = &mut self.binding; + let current = self.current; + let origin_stamp = self.origin_stamp; + let final_stamp = self.final_stamp; + let detached = self.detached; + let inventory = &self.inventory; + maintenance_admission::with_scoped_reference_invalidation( + &mut self.scoped, + grant, + effect_entries, + &mut |_, grant| { + binding.recheck(grant)?; + grant.check_time()?; + if open_generation(&root, &inventory.generation_id, inventory.generation_len) + .map_err(|_| admission_invalid())? + .identity() + != inventory.directory + { + return Err(admission_invalid()); + } + grant.check_time()?; + let file = root.open_readonly_file(b"active.json")?; + current.recheck(&file)?; + for (name, expected) in [(ORIGIN_NAME, origin_stamp), (FINAL_NAME, final_stamp)] { + if let Some(expected) = expected { + grant.check_time()?; + let file = directory.open_readonly_file(name)?; + expected.recheck(&file)?; + } else if directory.open_readonly_file(name).is_ok() { + return Err(admission_invalid()); + } + } + grant.check_time()?; + let file = if let Some(name) = detached { + root.open_readonly_file(&name)? + } else { + directory.open_readonly_file(leaf.name())? + }; + leaf.stamp.recheck(&file)?; + for (i, name) in [ + b"lease.lock".as_slice(), + b".database-use-family.lock".as_slice(), + resource_name().as_slice(), + ] + .into_iter() + .enumerate() + { + grant.check_time()?; + let file = directory.open_readonly_file(name)?; + if locks::file_identity(&file).map_err(|_| admission_invalid())? + != locks::file_identity(held[i].as_ref().unwrap().file()) + .map_err(|_| admission_invalid())? + { + return Err(admission_invalid()); + } + } + Ok(()) + }, + &mut |_epoch, grant| { + apply( + &GenerationMutation { + managed: &root, + parent: &directory, + leaf: &leaf, + }, + grant, + ) + }, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::api::{execute_materialization, MaterializeOptions}; + fn epoch(domain: &ReferenceDomain) -> ReferenceEpoch { + match super::super::maintenance_ownership::observe_reference_epoch(domain).unwrap() { + super::super::maintenance_ownership::EpochObservation::Verified(epoch) => epoch, + other => panic!("actual reference epoch unavailable: {other:?}"), + } + } + #[test] + fn generation_preparation_keeps_admission_images_out_of_fixed_owner_layout() { + // The worker remains 256 KiB on every platform. These fixed owner + // envelopes must carry charged handles, not embed/copy large admission + // scratch images above configuration, inventory and payload validation. + assert!(std::mem::size_of::() <= 1024); + assert!(std::mem::size_of::() <= 4096); + assert!( + std::mem::size_of::() + < std::mem::size_of::() + ); + assert!( + std::mem::size_of::() + < std::mem::size_of::() + ); + } + + #[test] + fn dc_generation_actual_publication_closed_role_lease_stale_and_resume_after_db_detach() { + let base = std::env::temp_dir().join(format!( + "dc-gen-public-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + std::fs::create_dir_all(base.join("src")).unwrap(); + let base = std::fs::canonicalize(base).unwrap(); + std::fs::write(base.join("src/lib.rs"), "pub fn first_version() {}\n").unwrap(); + let storage = base.join(".codebaseGraph/storage"); + let options = MaterializeOptions { + source_root: Some(base.clone()), + storage_root: Some(storage.clone()), + mode: "full".into(), + parallel: false, + use_git: false, + ..MaterializeOptions::default() + }; + let (_, first) = + execute_materialization(&options).expect("actual first managed public producer"); + let first_id = first.active_generation.unwrap(); + let layout = ManagedLayout::new(&storage); + let store = super::super::managed::ManagedStore::new(layout.clone()); + let old_reader = store.open_read().unwrap(); + assert_eq!(old_reader.generation_id, first_id); + let memory = CleanupMemory::new(16 * 1024 * 1024).unwrap(); + let first_paths = layout.generation(&first_id).unwrap(); + assert_eq!( + observe_generation_disposition(&layout, &first_paths, &memory).unwrap(), + GenerationDisposition::Published + ); + std::fs::write( + base.join("src/lib.rs"), + "pub fn second_version() { let _value = 2; }\n", + ) + .unwrap(); + let (_, second) = execute_materialization(&options) + .expect("actual verified successor managed public producer"); + let second_id = second.active_generation.unwrap(); + assert_ne!(first_id, second_id); + assert_eq!( + observe_generation_disposition(&layout, &first_paths, &memory).unwrap(), + GenerationDisposition::Final + ); + std::fs::write( + base.join(".codebaseGraph/config.json"), + serde_json::to_vec(&serde_json::json!({"schema_version":3,"storage_root":storage})) + .unwrap(), + ) + .unwrap(); + let target = MaintenanceTarget { + repository_root: base.clone(), + state_root: base.join(".codebaseGraph"), + storage_root: Some(storage), + direct_pair: None, + config_path: None, + }; + let domain = ReferenceDomain::managed(&layout).unwrap(); + let original = epoch(&domain); + let mut discovered = Vec::new(); + let report = discover_generations(&target, &memory, &|| false, &mut |id| { + discovered.push(id.to_vec()); + Ok(()) + }) + .unwrap(); + assert_eq!(report.final_generations, 1); + assert_eq!(discovered, vec![first_id.as_bytes().to_vec()]); + let mut prepared = prepare_exact_generation( + &target, + &domain, + &original, + first_id.as_bytes(), + 0, + &memory, + &|| false, + ) + .unwrap(); + let mut zero = MutationGrant::for_test(0, std::time::Duration::from_secs(1)); + let mut ran = false; + assert!(prepared + .with_admitted(&mut zero, 2, &mut |_, _| { + ran = true; + Ok(()) + }) + .is_err()); + assert!(!ran); + assert_eq!(epoch(&domain), original); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + assert!(matches!( + prepared.with_admitted(&mut grant, 2, &mut |_, _| { + ran = true; + Ok(()) + }), + Err(AdmissionError::Invalid(locks::FixedControlProblem::Busy)) + )); + assert!(!ran); + assert_eq!(epoch(&domain), original); + drop(old_reader); + drop(prepared); + std::fs::write( + first_paths.root().join("foreign-neighbor"), + b"foreign remains at its public address", + ) + .unwrap(); + let neighbor = locks::path_identity(&first_paths.root().join("foreign-neighbor")).unwrap(); + let pointer = std::fs::read(layout.active_pointer_path()).unwrap(); + let mut stale = prepare_exact_generation( + &target, + &domain, + &original, + first_id.as_bytes(), + 0, + &memory, + &|| false, + ) + .unwrap(); + std::fs::write(layout.active_pointer_path(), &pointer).unwrap(); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + assert!(stale + .with_admitted(&mut grant, 2, &mut |_, _| panic!( + "stale CURRENT must not admit" + )) + .is_err()); + assert_eq!(epoch(&domain), original); + drop(stale); + let before_memory = memory.used(); + let mut prepared = std::thread::Builder::new() + .name("generation-preparation-proof".into()) + .stack_size(256 * 1024) + .spawn({ + let target = target.clone(); + let domain = domain.clone(); + let original = original.clone(); + let memory = memory.clone(); + let first_id = first_id.clone(); + move || { + prepare_exact_generation( + &target, + &domain, + &original, + first_id.as_bytes(), + 0, + &memory, + &|| false, + ) + .unwrap() + } + }) + .unwrap() + .join() + .unwrap(); + let proof = prepared.inventory().encode_proof(); + let selected = *prepared.leaf(); + // The production candidate owns journal staging. This unit exercises the + // admitted exact leaf operation and restoration-free resume boundary; + // its cohort comes from the actual producer, never a fabricated record. + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + let advance = prepared + .with_admitted(&mut grant, 3, &mut |mutation, grant| { + grant.check_time()?; + mutation.source_parent().rename_file_no_replace( + mutation.leaf().name(), + mutation.source_parent(), + b".test-detached-database", + mutation.leaf().payload, + )?; + grant.check_time()?; + mutation.source_parent().sync_namespace()?; + Ok(()) + }) + .unwrap(); + assert!(advance.after > advance.before); + assert_eq!(prepared.source_epoch_counter(), original.counter()); + assert!(!first_paths.db_path().exists()); + assert_eq!( + locks::path_identity(&first_paths.root().join(".test-detached-database")).unwrap(), + selected.payload + ); + drop(prepared); + let current_epoch = epoch(&domain); + let resumed = prepare_generation_resume( + &target, + &domain, + ¤t_epoch, + &proof, + 1, + &memory, + &|| false, + ) + .expect("full recorded cohort resumes without reopening missing native DB"); + assert_eq!(resumed.source_epoch_counter(), original.counter()); + assert_eq!(resumed.leaf().name(), b"manifest.json"); + drop(resumed); + assert_eq!( + locks::path_identity(&first_paths.root().join("foreign-neighbor")).unwrap(), + neighbor + ); + assert_eq!( + std::fs::read(first_paths.root().join("foreign-neighbor")).unwrap(), + b"foreign remains at its public address" + ); + assert_eq!(store.open_read().unwrap().generation_id, second_id); + assert!( + super::super::maintenance_ownership::DatabaseUseLease::acquire_shared( + &first_paths.db_path() + ) + .is_err(), + "actual FINAL blocks late native birth" + ); + // Dispose every recorded remaining payload through the same admitted + // role. The independent foreign names are kept at their public address. + let old_dir = RootedDirectory::open( + first_paths.root(), + locks::path_identity(first_paths.root()).unwrap(), + ) + .unwrap(); + old_dir + .unlink_file(b".test-detached-database", selected.payload) + .unwrap(); + for (index, control_name) in NAMES.iter().enumerate().take(PAYLOADS).skip(1) { + if old_dir.open_readonly_file(control_name).is_err() { + continue; + } + let e = epoch(&domain); + let mut leaf = + prepare_generation_resume(&target, &domain, &e, &proof, index, &memory, &|| false) + .unwrap(); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + leaf.with_admitted(&mut grant, 2, &mut |m, g| { + g.check_time()?; + m.source_parent() + .unlink_file(m.leaf().name(), m.leaf().payload)?; + m.source_parent().sync_namespace()?; + Ok(()) + }) + .unwrap(); + } + let mut close = + prepare_generation_root_removal(&target, &domain, &proof, &memory, &|| false).unwrap(); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + assert!(close + .advance(&mut grant, 0, &mut |_, _| panic!( + "foreign sibling must prevent completion" + )) + .is_err()); + assert!(first_paths.root().join(".generation-origin").exists()); + assert_eq!( + locks::path_identity(&first_paths.root().join("foreign-neighbor")).unwrap(), + neighbor + ); + drop(close); + old_dir.unlink_file(b"foreign-neighbor", neighbor).unwrap(); + drop(old_dir); + let mut interrupted = + prepare_generation_root_removal(&target, &domain, &proof, &memory, &|| false).unwrap(); + interrupted.scratch.fail_after_transport_step(1); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + assert!(interrupted + .advance(&mut grant, 0, &mut |_, _| panic!( + "partial Intent must not claim root removal" + )) + .is_err()); + assert!(first_paths.root().join(".generation-origin").exists()); + // The complete durable Intent excludes only the named old root while + // current native startup remains usable during required recovery. + let current_paths = layout.generation(&second_id).unwrap(); + drop( + super::super::maintenance_ownership::DatabaseUseLease::acquire_shared( + ¤t_paths.db_path(), + ) + .unwrap(), + ); + drop(interrupted); + let mut close = + prepare_generation_root_removal(&target, &domain, &proof, &memory, &|| false).unwrap(); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + let mut actual_removed = false; + assert!(close + .advance(&mut grant, 0, &mut |outcome, _| { + actual_removed = matches!(outcome, GenerationRootOutcome::Removed { .. }); + Err(admission_invalid()) + }) + .is_err()); + assert!( + actual_removed, + "injected completion fault occurs after actual atomic empty rmdir" + ); + assert!(!first_paths.root().exists()); + drop(close); + let mut resume = + prepare_generation_root_removal(&target, &domain, &proof, &memory, &|| false).unwrap(); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + let mut recovered_missing = false; + assert!(resume + .advance(&mut grant, 0, &mut |outcome, _| { + recovered_missing = matches!(outcome, GenerationRootOutcome::RecoveredMissing); + Err(admission_invalid()) + }) + .is_err()); + assert!(recovered_missing,"missing-root recovery remains distinct from actual rmdir; Removing is retained until a real durable completion token"); + drop(resume); + assert!( + super::super::maintenance_ownership::DatabaseUseLease::acquire_shared( + &first_paths.db_path() + ) + .is_err() + ); + assert!( + !first_paths.root().exists(), + "late native use cannot recreate removed managed parent" + ); + assert_eq!(store.open_read().unwrap().generation_id, second_id); + assert!(memory.used() <= before_memory); + std::fs::remove_dir_all(base).unwrap(); + } + #[test] + fn dc_generation_fixed_records_reject_corruption_and_invalid_ids_without_authority() { + let id = FileIdentity::from_fixed_words(1, 2); + let scope = GenerationOriginScope { + root: id, + coordination: id, + cohort: id, + publication_binding: id, + incarnation: [7; 32], + }; + let (mut name, len) = fixed_id("producer-1").unwrap(); + let leaf = BoundLeaf { + id, + len: 1, + hash: [3; 32], + stamp_digest: [4; 32], + }; + let mut leaves = [None; PAYLOADS]; + leaves[..4].fill(Some(leaf)); + let origin = Origin { + generation: name, + generation_len: len, + scope, + managed: id, + writer: id, + directory: id, + receipt: id, + operation: [8; 32], + lease: id, + family: id, + resource: id, + manifest_resource: id, + native_lock: None, + leaves, + }; + let image = origin.encode(); + assert!(Origin::decode(&image).is_some()); + for offset in [0, 8, 16, 32, 160, 256, 304, 320, 352, 400, 2016, 2047] { + let mut bad = image; + bad[offset] ^= 0x80; + assert!(Origin::decode(&bad).is_none()); + } + name[0] = b'/'; + assert!(gen_name(&name, len).is_err()); + assert!(gen_name(&[0; 128], 255).is_err()); + assert!(fixed_id(&"x".repeat(129)).is_err()); + } + #[test] + fn dc_generation_preparation_cancellation_has_no_creating_effects() { + let memory = CleanupMemory::new(128 * 1024).unwrap(); + let path = std::path::PathBuf::from("/does-not-exist-generation-cancel"); + let target = MaintenanceTarget { + repository_root: path.clone(), + state_root: path.clone(), + storage_root: Some(path), + direct_pair: None, + config_path: None, + }; + assert!(matches!( + discover_generations(&target, &memory, &|| true, &mut |_| panic!( + "cancelled discovery must not emit" + )), + Err(GenerationError::Cancelled) + )); + } +} + +/// Final cohort operation. Payload leaves have already been retired through the +/// caller's durable cohort journal; receipt/lock/root removal is indivisible in +/// authority and restartable through the outside-generation closed intent. +pub(crate) struct PreparedGenerationRootRemoval { + binding: MemoryBox, + scoped: MemoryBox, + layout: ManagedLayout, + origin: Origin, + final_record: Final, + current: FixedFileStamp, + proof_digest: [u8; 32], + scratch: MemoryBox, + _floor: MemoryReservation, +} +pub(crate) fn prepare_generation_root_removal( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + proof: &[u8; GENERATION_PROOF_BYTES], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, GenerationError> { + if cancel() { + return Err(GenerationError::Cancelled); + } + if &proof[..8] != b"CGGPR001" + || proof[16..32] + .iter() + .chain(proof[2592..4064].iter()) + .any(|v| *v != 0) + || digest(&proof[..4064]) != proof[4064..] + { + return Err(GenerationError::Unknown); + } + let origin_image: &[u8; 2048] = proof[32..2080].try_into().unwrap(); + let final_image: &[u8; 512] = proof[2080..2592].try_into().unwrap(); + let origin = Origin::decode(origin_image).ok_or(GenerationError::Unknown)?; + let final_record = Final::decode(final_image).ok_or(GenerationError::Unknown)?; + if final_record.origin_digest != digest(origin_image) + || final_record.origin != origin.receipt + || final_record.directory != origin.directory + || final_record.generation != origin.generation + { + return Err(GenerationError::Changed); + } + let (layout, mut binding, floor) = common(target, domain, memory)?; + if !observe_scope_matches(origin, current_origin_scope(&layout, memory)?) { + return Err(GenerationError::Changed); + } + let root = RootedDirectory::open_readonly_root(layout.storage_root(), origin.managed) + .map_err(|e| GenerationError::Io(e.kind()))?; + let current = read_current(&root).map_err(generation_error)?; + if current.0 == origin.generation && current.1 == origin.generation_len { + return Err(GenerationError::Changed); + } + let directory = match open_generation(&root, &origin.generation, origin.generation_len) { + Ok(directory) => Some(directory), + Err(NativeError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(generation_error(e)), + }; + if let Some(directory) = directory { + if directory.identity() != origin.directory { + return Err(GenerationError::Changed); + } + // No payload reads or hashing: every known payload must already be absent. + for name in &NAMES[..PAYLOADS] { + match directory.open_readonly_file(name) { + Ok(_) => return Err(GenerationError::Busy), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(GenerationError::Io(e.kind())), + } + } + } + if !binding.is_current().map_err(generation_admission)? || cancel() { + return Err(GenerationError::Changed); + } + MemoryBox::new( + PreparedGenerationRootRemoval { + binding, + scoped: boxed_generation_invalidation(domain, memory)?, + layout, + origin, + final_record, + current: current.2, + proof_digest: digest(proof), + scratch: gate::RemovalScratch::new(&root, origin, final_record, digest(proof), memory) + .map_err(generation_error)?, + _floor: floor, + }, + memory, + ) + .map_err(|_| GenerationError::ResourceLimit) +} +pub(crate) enum GenerationRootOutcome { + Removed { _removed: gate::RemovedGeneration }, + RecoveredMissing, +} +impl PreparedGenerationRootRemoval { + /// Completion-record replay observation; not a removed-generation token. + /// The caller must also match its durable completion record to this proof. + pub(crate) fn observed_completed(&self) -> Result { + if !self.scratch.observed_committed_idle() { + return Ok(false); + } + let root = + RootedDirectory::open_readonly_root(self.layout.storage_root(), self.origin.managed) + .map_err(|e| GenerationError::Io(e.kind()))?; + match open_generation(&root, &self.origin.generation, self.origin.generation_len) { + Err(NativeError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => Ok(true), + Ok(_) => Ok(false), + Err(e) => Err(generation_error(e)), + } + } + /// Includes fixed admission, complete bounded control transport, lock/name + /// validation, exact unlink, namespace sync and final atomic empty removal. + pub(crate) fn effect_entries(&self) -> usize { + 100 + } + pub(crate) fn advance( + &mut self, + grant: &mut MutationGrant, + caller_callback_entries: usize, + completed: &mut impl FnMut( + &GenerationRootOutcome, + &mut MutationGrant, + ) -> Result< + super::maintenance_candidate::GenerationCohortDone, + AdmissionError, + >, + ) -> Result { + // Reserve the entire job before acquiring any exclusion. The nested + // transport consumes the reserved allowance through this child grant. + if grant.remaining_entries() < self.effect_entries() { + grant.take_entries(self.effect_entries())?; + } + grant.check_time()?; + grant.take_entries(6)?; + let root = RootedDirectory::open(self.layout.storage_root(), self.origin.managed)?; + let writer = root.open_readonly_file(b"writer.lock")?; + if locks::file_identity(&writer).map_err(|_| admission_invalid())? != self.origin.writer { + return Err(admission_invalid()); + } + let _writer = FixedLockedFile::try_lock(writer, LockMode::Exclusive)? + .ok_or(AdmissionError::Invalid(locks::FixedControlProblem::Busy))?; + let gate = gate::admit_retirement_gate(&root, self.origin, &self.scratch, grant) + .map_err(|_| admission_invalid())?; + let directory = + match open_generation(&root, &self.origin.generation, self.origin.generation_len) { + Ok(directory) => Some(directory), + Err(NativeError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(_) => return Err(admission_invalid()), + }; + if directory + .as_ref() + .is_some_and(|directory| directory.identity() != self.origin.directory) + { + return Err(admission_invalid()); + } + let current = self.current; + let origin = self.origin; + let final_record = self.final_record; + let proof = self.proof_digest; + let binding = &mut self.binding; + let scratch = &mut self.scratch; + let mut removed = None; + maintenance_admission::with_scoped_reference_invalidation( + &mut self.scoped, + grant, + 48usize + .checked_add(caller_callback_entries) + .ok_or_else(admission_invalid)?, + &mut |_, grant| { + binding.recheck(grant)?; + grant.check_time()?; + current.recheck(&root.open_readonly_file(b"active.json")?)?; + match ( + directory.as_ref(), + open_generation(&root, &origin.generation, origin.generation_len), + ) { + (Some(_), Ok(current)) if current.identity() == origin.directory => {} + (None, Err(NativeError::Io(e))) if e.kind() == std::io::ErrorKind::NotFound => { + } + _ => return Err(admission_invalid()), + } + Ok(()) + }, + &mut |_, grant| { + removed = Some(if let Some(directory) = directory.as_ref() { + GenerationRootOutcome::Removed { + _removed: gate + .remove_closed_root( + &root, + directory, + origin, + final_record, + proof, + scratch, + grant, + ) + .map_err(|_| admission_invalid())?, + } + } else { + gate.complete_missing(&root, origin, final_record, proof, scratch, grant) + .map_err(|_| admission_invalid())?; + GenerationRootOutcome::RecoveredMissing + }); + let completed = completed(removed.as_ref().unwrap(), grant)?; + if !completed.matches(root.identity(), origin.directory, proof) { + return Err(admission_invalid()); + } + gate.finish_removed(&root, origin, final_record, proof, scratch, grant) + .map_err(|_| admission_invalid())?; + Ok(()) + }, + )?; + removed.ok_or_else(admission_invalid) + } +} + +#[cfg(test)] +mod legacy_final_tests { + use super::*; + use crate::api::{execute_materialization, MaterializeOptions}; + #[test] + fn dc_generation_actual_verified_successor_closes_unreceipted_predecessor_without_minting_origin( + ) { + let base = std::env::temp_dir().join(format!( + "dc-legacy-final-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + std::fs::create_dir_all(base.join("src")).unwrap(); + let base = std::fs::canonicalize(base).unwrap(); + std::fs::write(base.join("src/lib.rs"), "pub fn old() {}\n").unwrap(); + let storage = base.join(".codebaseGraph/storage"); + let options = MaterializeOptions { + source_root: Some(base.clone()), + storage_root: Some(storage.clone()), + mode: "full".into(), + parallel: false, + use_git: false, + ..MaterializeOptions::default() + }; + let (_, first) = execute_materialization(&options).unwrap(); + let first = first.active_generation.unwrap(); + let layout = ManagedLayout::new(&storage); + let paths = layout.generation(&first).unwrap(); + // Model a real pre-receipt producer directory by removing only the + // fixture's exact optional receipt. The successor and FINAL capability + // are still constructed solely through the actual public API pipeline. + let directory = + RootedDirectory::open(paths.root(), locks::path_identity(paths.root()).unwrap()) + .unwrap(); + let origin = directory.open_readonly_file(ORIGIN_NAME).unwrap(); + directory + .unlink_file(ORIGIN_NAME, locks::file_identity(&origin).unwrap()) + .unwrap(); + drop(origin); + drop(directory); + std::fs::write(base.join("src/lib.rs"), "pub fn successor() {let _v=7;}\n").unwrap(); + let (_, second) = execute_materialization(&options).unwrap(); + assert_ne!(second.active_generation.as_ref().unwrap(), &first); + let root = + RootedDirectory::open_readonly_root(&storage, locks::path_identity(&storage).unwrap()) + .unwrap(); + let directory = open_generation( + &root, + &fixed_id(&first).unwrap().0, + fixed_id(&first).unwrap().1, + ) + .unwrap(); + let (closed, bytes, _) = gate::read_legacy_final(&directory, root.identity()).unwrap(); + assert_eq!(closed.generation, fixed_id(&first).unwrap().0); + assert_eq!( + closed.database.stamp_digest, + stamp(&directory.open_readonly_file(b"graph.ldb").unwrap()) + .unwrap() + .binding_digest() + ); + assert!(gate::LegacyFinal::decode(&bytes).is_some()); + assert!(!paths.root().join(".generation-origin").exists()); + assert!(!paths.root().join(".generation-final").exists()); + let memory = CleanupMemory::new(128 * 1024).unwrap(); + assert_eq!( + observe_generation_disposition(&layout, &paths, &memory).unwrap(), + GenerationDisposition::Final + ); + assert!( + super::super::maintenance_ownership::DatabaseUseLease::acquire_shared(&paths.db_path()) + .is_err() + ); + assert!(super::super::managed::ManagedStore::new(layout) + .open_read() + .is_ok()); + std::fs::remove_dir_all(base).unwrap(); + } +} diff --git a/src/storage/maintenance_journal.rs b/src/storage/maintenance_journal.rs new file mode 100644 index 00000000..5a5cb9c6 --- /dev/null +++ b/src/storage/maintenance_journal.rs @@ -0,0 +1,1080 @@ +//! Pure, checksummed facts codecs/reducers. A decoded record or caller-provided +//! observation NEVER grants provenance, lifecycle, publication or deletion +//! authority. No filesystem operations occur in this module. +use sha2::{Digest, Sha256}; + +#[cfg(test)] +pub(crate) const RECEIPT_BYTES: usize = 304; +#[cfg(test)] +pub(crate) const JOURNAL_BYTES: usize = 336; +#[cfg(test)] +pub(crate) const LEAF_BYTES: usize = 224; +#[cfg(test)] +pub(crate) const FRAMES_PER_SEGMENT: u64 = 16; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum CodecIssue { + Corrupt, + Transition, + #[cfg(test)] + Stale, + #[cfg(test)] + Overflow, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +#[cfg(test)] +pub(crate) enum ReceiptPhase { + Intent, + CreationBound, + Writing, + CompleteStaged, + RenameIntent, + BackupMoved, + CanonicalMoved, + Published, + RollbackIntent, + Restored, +} +#[cfg(test)] +impl ReceiptPhase { + fn decode(b: u8) -> Result { + Ok(match b { + 0 => Self::Intent, + 1 => Self::CreationBound, + 2 => Self::Writing, + 3 => Self::CompleteStaged, + 4 => Self::RenameIntent, + 5 => Self::BackupMoved, + 6 => Self::CanonicalMoved, + 7 => Self::Published, + 8 => Self::RollbackIntent, + 9 => Self::Restored, + _ => return Err(CodecIssue::Corrupt), + }) + } +} + +/// Identity fields are opaque serialization facts awaiting actual guarded OS +/// identity verification. usable_claim is a stored CLAIM, not LoaderUsable. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg(test)] +pub(crate) struct ReceiptFacts { + pub(crate) scope: [u8; 32], + pub(crate) directory: [u8; 32], + pub(crate) payload: [u8; 32], + pub(crate) canonical: [u8; 32], + pub(crate) backup: [u8; 32], + pub(crate) record: [u8; 32], + pub(crate) bytes_digest: [u8; 32], + pub(crate) bytes_len: u64, + pub(crate) source_epoch: u64, + pub(crate) stage_generation: u64, + pub(crate) phase: ReceiptPhase, + pub(crate) usable_claim: bool, +} +#[derive(Debug, Clone, Copy)] +#[cfg(test)] +pub(crate) enum ReceiptEvent { + BindCreated { + directory: [u8; 32], + payload: [u8; 32], + }, + StartWriting, + Completed { + len: u64, + digest: [u8; 32], + usable_claim: bool, + }, + PlanRename { + canonical: [u8; 32], + backup: [u8; 32], + }, + BackupObserved { + directory: [u8; 32], + }, + CanonicalObserved { + directory: [u8; 32], + payload: [u8; 32], + }, + PublicationSynced, + BeginRollback, + RestoredObserved { + directory: [u8; 32], + }, +} +#[cfg(test)] +impl ReceiptFacts { + pub(crate) fn reduce(mut self, event: ReceiptEvent) -> Result { + use ReceiptEvent::*; + use ReceiptPhase::*; + match (self.phase, event) { + (Intent, BindCreated { directory, payload }) + if directory != [0; 32] && payload != [0; 32] => + { + self.directory = directory; + self.payload = payload; + self.phase = CreationBound; + } + (CreationBound, StartWriting) => self.phase = Writing, + ( + Writing, + Completed { + len, + digest, + usable_claim, + }, + ) => { + self.bytes_len = len; + self.bytes_digest = digest; + self.usable_claim = usable_claim; + self.phase = CompleteStaged; + } + (CompleteStaged, PlanRename { canonical, backup }) if canonical != [0; 32] => { + self.canonical = canonical; + self.backup = backup; + self.phase = RenameIntent; + } + (RenameIntent, BackupObserved { directory }) + if self.backup != [0; 32] && directory == self.backup => + { + self.phase = BackupMoved + } + (RenameIntent | BackupMoved, CanonicalObserved { directory, payload }) + if directory == self.directory && payload == self.payload => + { + self.phase = CanonicalMoved + } + (CanonicalMoved, PublicationSynced) => self.phase = Published, + (RenameIntent | BackupMoved, BeginRollback) => self.phase = RollbackIntent, + (RollbackIntent, RestoredObserved { directory }) + if self.backup != [0; 32] && directory == self.backup => + { + self.phase = Restored + } + _ => return Err(CodecIssue::Transition), + } + self.stage_generation = self + .stage_generation + .checked_add(1) + .ok_or(CodecIssue::Overflow)?; + Ok(self) + } + pub(crate) fn encode(&self) -> [u8; RECEIPT_BYTES] { + let mut b = [0; RECEIPT_BYTES]; + b[..8].copy_from_slice(b"CGOR0001"); + for (i, v) in [ + self.scope, + self.directory, + self.payload, + self.canonical, + self.backup, + self.record, + self.bytes_digest, + ] + .iter() + .enumerate() + { + b[8 + i * 32..40 + i * 32].copy_from_slice(v); + } + put(&mut b, 232, self.bytes_len); + put(&mut b, 240, self.source_epoch); + put(&mut b, 248, self.stage_generation); + b[256] = self.phase as u8; + b[257] = u8::from(self.usable_claim); + seal(&mut b); + b + } + pub(crate) fn decode(b: &[u8; RECEIPT_BYTES]) -> Result { + check(b, b"CGOR0001")?; + if b[258..272].iter().any(|b| *b != 0) || b[257] > 1 { + return Err(CodecIssue::Corrupt); + } + let a = |i: usize| b[8 + i * 32..40 + i * 32].try_into().unwrap(); + Ok(Self { + scope: a(0), + directory: a(1), + payload: a(2), + canonical: a(3), + backup: a(4), + record: a(5), + bytes_digest: a(6), + bytes_len: get(b, 232), + source_epoch: get(b, 240), + stage_generation: get(b, 248), + phase: ReceiptPhase::decode(b[256])?, + usable_claim: b[257] != 0, + }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +#[cfg(test)] +pub(crate) enum JournalPhase { + Staging, + Complete, + Detaching, + Detached, + Draining, + Done, + Invalidated, +} +#[cfg(test)] +impl JournalPhase { + fn decode(b: u8) -> Result { + Ok(match b { + 0 => Self::Staging, + 1 => Self::Complete, + 2 => Self::Detaching, + 3 => Self::Detached, + 4 => Self::Draining, + 5 => Self::Done, + 6 => Self::Invalidated, + _ => return Err(CodecIssue::Corrupt), + }) + } +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg(test)] +pub(crate) struct JournalFacts { + pub(crate) scope: [u8; 32], + pub(crate) source_incarnation: [u8; 32], + pub(crate) private_name: [u8; 32], + pub(crate) journal_incarnation: [u8; 32], + pub(crate) frame_chain: [u8; 32], + pub(crate) next_address: [u8; 64], + /// Immutable source observation; not relabeled by staging/own invalidation. + pub(crate) source_epoch: u64, + pub(crate) stage_generation: u64, + pub(crate) committed_frames: u64, + pub(crate) disposed_frames: u64, + pub(crate) phase: JournalPhase, + pub(crate) own_epoch_incremented: bool, +} +#[derive(Debug, Clone, Copy)] +#[cfg(test)] +pub(crate) enum JournalEvent { + Append { + ordinal: u64, + frame_digest: [u8; 32], + next_address: [u8; 64], + }, + CompleteObserved { + frames: u64, + source_epoch: u64, + }, + SourceChanged, + OwnEpochAdvanced { + before: u64, + after: u64, + }, + DetachingRecorded, + PrivateObserved { + actual_incarnation: [u8; 32], + }, + Disposed { + ordinal: u64, + }, + Finished { + private_absent: bool, + }, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg(test)] +pub(crate) enum ReplayDisposition { + PreparationOnly, + NeedsReprepare, + NeedsRecovery, + DetachedObservation, + Finished, + Unknown, +} +#[cfg(test)] +impl JournalFacts { + pub(crate) fn reduce(mut self, event: JournalEvent) -> Result { + use JournalEvent::*; + use JournalPhase::*; + if let Append { ordinal, .. } = event { + if ordinal != self.committed_frames { + return Err(CodecIssue::Stale); + } + } + match (self.phase, event) { + ( + Staging, + Append { + ordinal, + frame_digest, + next_address, + }, + ) if ordinal == self.committed_frames => { + let mut h = Sha256::new(); + h.update(self.frame_chain); + h.update(ordinal.to_le_bytes()); + h.update(frame_digest); + self.frame_chain = h.finalize().into(); + self.committed_frames = self + .committed_frames + .checked_add(1) + .ok_or(CodecIssue::Overflow)?; + self.next_address = next_address; + } + ( + Staging, + CompleteObserved { + frames, + source_epoch, + }, + ) if frames == self.committed_frames && source_epoch == self.source_epoch => { + self.phase = Complete + } + (Staging | Complete, SourceChanged) => self.phase = Invalidated, + (Complete, OwnEpochAdvanced { before, after }) + if !self.own_epoch_incremented + && before == self.source_epoch + && before.checked_add(1) == Some(after) => + { + self.own_epoch_incremented = true + } + (Complete, DetachingRecorded) if self.own_epoch_incremented => self.phase = Detaching, + (Detaching, PrivateObserved { actual_incarnation }) + if actual_incarnation == self.source_incarnation + && actual_incarnation != [0; 32] => + { + self.phase = Detached + } + (Detached | Draining, Disposed { ordinal }) + if ordinal == self.disposed_frames && ordinal < self.committed_frames => + { + self.disposed_frames = self + .disposed_frames + .checked_add(1) + .ok_or(CodecIssue::Overflow)?; + self.phase = Draining; + } + ( + Detached | Draining, + Finished { + private_absent: true, + }, + ) if self.disposed_frames == self.committed_frames => self.phase = Done, + _ => return Err(CodecIssue::Transition), + } + self.stage_generation = self + .stage_generation + .checked_add(1) + .ok_or(CodecIssue::Overflow)?; + Ok(self) + } + /// Even an own E increment + Detaching record cannot manufacture detached + /// state when no matching actual private incarnation is observed. + pub(crate) fn replay_disposition( + &self, + private_incarnation: Option<[u8; 32]>, + ) -> ReplayDisposition { + use JournalPhase::*; + match self.phase { + Staging => ReplayDisposition::PreparationOnly, + Invalidated => ReplayDisposition::NeedsReprepare, + Complete if self.own_epoch_incremented => ReplayDisposition::NeedsReprepare, + Complete => ReplayDisposition::PreparationOnly, + Detaching | Detached | Draining + if private_incarnation == Some(self.source_incarnation) + && self.source_incarnation != [0; 32] => + { + ReplayDisposition::DetachedObservation + } + Detaching => ReplayDisposition::NeedsRecovery, + Detached | Draining => ReplayDisposition::Unknown, + Done if private_incarnation.is_none() => ReplayDisposition::Finished, + Done => ReplayDisposition::Unknown, + } + } + pub(crate) fn encode(&self) -> [u8; JOURNAL_BYTES] { + let mut b = [0; JOURNAL_BYTES]; + b[..8].copy_from_slice(b"CGJR0001"); + for (i, v) in [ + self.scope, + self.source_incarnation, + self.private_name, + self.journal_incarnation, + self.frame_chain, + ] + .iter() + .enumerate() + { + b[8 + i * 32..40 + i * 32].copy_from_slice(v); + } + b[168..232].copy_from_slice(&self.next_address); + for (o, v) in [ + (232, self.source_epoch), + (240, self.stage_generation), + (248, self.committed_frames), + (256, self.disposed_frames), + ] { + put(&mut b, o, v); + } + b[264] = self.phase as u8; + b[265] = u8::from(self.own_epoch_incremented); + seal(&mut b); + b + } + pub(crate) fn decode(b: &[u8; JOURNAL_BYTES]) -> Result { + check(b, b"CGJR0001")?; + if b[266..304].iter().any(|b| *b != 0) || b[265] > 1 { + return Err(CodecIssue::Corrupt); + } + let a = |i: usize| b[8 + i * 32..40 + i * 32].try_into().unwrap(); + let result = Self { + scope: a(0), + source_incarnation: a(1), + private_name: a(2), + journal_incarnation: a(3), + frame_chain: a(4), + next_address: b[168..232].try_into().unwrap(), + source_epoch: get(b, 232), + stage_generation: get(b, 240), + committed_frames: get(b, 248), + disposed_frames: get(b, 256), + phase: JournalPhase::decode(b[264])?, + own_epoch_incremented: b[265] != 0, + }; + if result.disposed_frames > result.committed_frames { + return Err(CodecIssue::Corrupt); + } + Ok(result) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg(test)] +pub(crate) struct LeafFacts { + pub(crate) ordinal: u64, + pub(crate) address: [u8; 64], + pub(crate) incarnation: [u8; 32], + pub(crate) origin_claim: [u8; 32], + pub(crate) bytes_digest: [u8; 32], + pub(crate) len: u64, +} +#[cfg(test)] +impl LeafFacts { + pub(crate) fn encode(&self) -> [u8; LEAF_BYTES] { + let mut b = [0; LEAF_BYTES]; + b[..8].copy_from_slice(b"CGLF0001"); + put(&mut b, 8, self.ordinal); + b[16..80].copy_from_slice(&self.address); + b[80..112].copy_from_slice(&self.incarnation); + b[112..144].copy_from_slice(&self.origin_claim); + b[144..176].copy_from_slice(&self.bytes_digest); + put(&mut b, 176, self.len); + seal(&mut b); + b + } + pub(crate) fn decode(b: &[u8; LEAF_BYTES]) -> Result { + check(b, b"CGLF0001")?; + if b[184..192].iter().any(|b| *b != 0) { + return Err(CodecIssue::Corrupt); + } + Ok(Self { + ordinal: get(b, 8), + address: b[16..80].try_into().unwrap(), + incarnation: b[80..112].try_into().unwrap(), + origin_claim: b[112..144].try_into().unwrap(), + bytes_digest: b[144..176].try_into().unwrap(), + len: get(b, 176), + }) + } + pub(crate) fn segment(&self) -> u64 { + self.ordinal / FRAMES_PER_SEGMENT + } +} +#[cfg(test)] +fn put(b: &mut [u8], o: usize, v: u64) { + b[o..o + 8].copy_from_slice(&v.to_le_bytes()); +} +#[cfg(test)] +fn get(b: &[u8], o: usize) -> u64 { + u64::from_le_bytes(b[o..o + 8].try_into().unwrap()) +} +#[cfg(test)] +fn seal(b: &mut [u8]) { + let end = b.len() - 32; + let digest = Sha256::digest(&b[..end]); + b[end..].copy_from_slice(&digest); +} +#[cfg(test)] +fn check(b: &[u8], magic: &[u8; 8]) -> Result<(), CodecIssue> { + let end = b.len() - 32; + if &b[..8] != magic || b[end..] != Sha256::digest(&b[..end])[..] { + return Err(CodecIssue::Corrupt); + } + Ok(()) +} + +/// Runtime journal facts stored in the prebound publication stream and slot. +/// The source epoch is immutable; slot generations are independent progress. +pub(crate) const RETIREMENT_FRAME_BYTES: usize = 512; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub(crate) enum RetirementStage { + AppendIntent = 1, + FrameReady = 2, + Detached = 3, + PayloadGone = 4, + Done = 5, + ResetIntent = 6, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct RetirementLeaf { + pub(crate) stage: RetirementStage, + pub(crate) source_epoch: u64, + pub(crate) stage_generation: u64, + pub(crate) stream_offset: u64, + pub(crate) key: [u8; 32], + pub(crate) directory: super::locks::FileIdentity, + pub(crate) payload: super::locks::FileIdentity, + pub(crate) receipt: super::locks::FileIdentity, + pub(crate) scope: super::maintenance_admission::RootOriginBinding, + pub(crate) operation: [u8; 32], + pub(crate) byte_len: u64, + pub(crate) digest: [u8; 32], + pub(crate) directory_created: bool, + pub(crate) receipt_digest: [u8; 32], + pub(crate) source_kind: u8, + pub(crate) source_name: [u8; 134], +} +impl RetirementLeaf { + pub(crate) fn encode(&self) -> [u8; RETIREMENT_FRAME_BYTES] { + let mut b = [0; RETIREMENT_FRAME_BYTES]; + b[..8].copy_from_slice(b"CGLEAF01"); + b[8] = self.stage as u8; + b[16..24].copy_from_slice(&self.source_epoch.to_le_bytes()); + b[24..32].copy_from_slice(&self.stage_generation.to_le_bytes()); + b[32..40].copy_from_slice(&self.stream_offset.to_le_bytes()); + b[40..72].copy_from_slice(&self.key); + for (i, id) in [ + self.directory, + self.payload, + self.receipt, + self.scope.root, + self.scope.coordination, + self.scope.cohort, + self.scope.publication_binding, + ] + .iter() + .enumerate() + { + let (a, c) = id.fixed_words(); + let o = 72 + i * 16; + b[o..o + 8].copy_from_slice(&a.to_le_bytes()); + b[o + 8..o + 16].copy_from_slice(&c.to_le_bytes()); + } + b[184..216].copy_from_slice(&self.scope.incarnation); + b[216..248].copy_from_slice(&self.operation); + b[248..256].copy_from_slice(&self.byte_len.to_le_bytes()); + b[256..288].copy_from_slice(&self.digest); + b[288] = u8::from(self.directory_created); + b[289..321].copy_from_slice(&self.receipt_digest); + b[321] = self.source_kind; + b[322..456].copy_from_slice(&self.source_name); + let hash = Sha256::digest(&b[..480]); + b[480..].copy_from_slice(&hash); + b + } + pub(crate) fn decode(b: &[u8; RETIREMENT_FRAME_BYTES]) -> Result { + if &b[..8] != b"CGLEAF01" + || b[9..16].iter().any(|x| *x != 0) + || b[456..480].iter().any(|x| *x != 0) + || b[321] > 1 + || (b[321] == 0 && b[322..456].iter().any(|x| *x != 0)) + || b[288] > 1 + || b[480..] != Sha256::digest(&b[..480])[..] + { + return Err(CodecIssue::Corrupt); + } + let stage = match b[8] { + 1 => RetirementStage::AppendIntent, + 2 => RetirementStage::FrameReady, + 3 => RetirementStage::Detached, + 4 => RetirementStage::PayloadGone, + 5 => RetirementStage::Done, + 6 => RetirementStage::ResetIntent, + _ => return Err(CodecIssue::Corrupt), + }; + let u = |o| u64::from_le_bytes(b[o..o + 8].try_into().unwrap()); + let id = |o| super::locks::FileIdentity::from_fixed_words(u(o), u(o + 8)); + if u(24) != (stage as u64) - 1 { + return Err(CodecIssue::Transition); + } + if b[321] == 1 { + let name = &b[322..456]; + if &name[..5] != b".tmp-" + || name[69] != b'-' + || !name[5..69] + .iter() + .chain(name[70..].iter()) + .all(|c| c.is_ascii_digit() || (b'a'..=b'f').contains(c)) + { + return Err(CodecIssue::Corrupt); + } + let digits = b"0123456789abcdef"; + for (i, key) in b[40..72].iter().enumerate() { + if name[5 + i * 2] != digits[(key >> 4) as usize] + || name[6 + i * 2] != digits[(key & 15) as usize] + { + return Err(CodecIssue::Corrupt); + } + } + } + Ok(Self { + stage, + source_epoch: u(16), + stage_generation: u(24), + stream_offset: u(32), + key: b[40..72].try_into().unwrap(), + directory: id(72), + payload: id(88), + receipt: id(104), + scope: super::maintenance_admission::RootOriginBinding { + root: id(120), + coordination: id(136), + cohort: id(152), + publication_binding: id(168), + incarnation: b[184..216].try_into().unwrap(), + }, + operation: b[216..248].try_into().unwrap(), + byte_len: u(248), + digest: b[256..288].try_into().unwrap(), + directory_created: b[288] != 0, + receipt_digest: b[289..321].try_into().unwrap(), + source_kind: b[321], + source_name: b[322..456].try_into().unwrap(), + }) + } + /// Lossless fixed native component. Random operation identity is checked + /// together with the exact expected physical leaf, never by name alone. + pub(crate) fn tombstone(&self) -> [u8; 73] { + let mut name = [0; 73]; + name[..9].copy_from_slice(b".retired-"); + let digits = b"0123456789abcdef"; + for (i, b) in self.operation.iter().enumerate() { + name[9 + i * 2] = digits[(b >> 4) as usize]; + name[10 + i * 2] = digits[(b & 15) as usize]; + } + name + } +} + +#[cfg(test)] +mod tests { + use super::*; + fn receipt() -> ReceiptFacts { + ReceiptFacts { + scope: [1; 32], + directory: [0; 32], + payload: [0; 32], + canonical: [0; 32], + backup: [0; 32], + record: [2; 32], + bytes_digest: [0; 32], + bytes_len: 0, + source_epoch: 23, + stage_generation: 0, + phase: ReceiptPhase::Intent, + usable_claim: false, + } + } + fn journal() -> JournalFacts { + JournalFacts { + scope: [1; 32], + source_incarnation: [2; 32], + private_name: [3; 32], + journal_incarnation: [4; 32], + frame_chain: [0; 32], + next_address: [0; 64], + source_epoch: 23, + stage_generation: 0, + committed_frames: 0, + disposed_frames: 0, + phase: JournalPhase::Staging, + own_epoch_incremented: false, + } + } + #[test] + fn dc_receipt_crash_states_never_publish_intent_or_origin_only() { + let mut r = receipt(); + assert!(r.reduce(ReceiptEvent::PublicationSynced).is_err()); + r = r + .reduce(ReceiptEvent::BindCreated { + directory: [5; 32], + payload: [6; 32], + }) + .unwrap(); + r = r.reduce(ReceiptEvent::StartWriting).unwrap(); + r = r + .reduce(ReceiptEvent::Completed { + len: 900, + digest: [7; 32], + usable_claim: false, + }) + .unwrap(); + assert!(!r.usable_claim); + r = r + .reduce(ReceiptEvent::PlanRename { + canonical: [8; 32], + backup: [9; 32], + }) + .unwrap(); + assert!(r + .reduce(ReceiptEvent::CanonicalObserved { + directory: [99; 32], + payload: [6; 32] + }) + .is_err()); + r = r + .reduce(ReceiptEvent::BackupObserved { directory: [9; 32] }) + .unwrap(); + let rollback = r + .reduce(ReceiptEvent::BeginRollback) + .unwrap() + .reduce(ReceiptEvent::RestoredObserved { directory: [9; 32] }) + .unwrap(); + assert_eq!(rollback.phase, ReceiptPhase::Restored); + r = r + .reduce(ReceiptEvent::CanonicalObserved { + directory: [5; 32], + payload: [6; 32], + }) + .unwrap(); + assert_eq!(r.phase, ReceiptPhase::CanonicalMoved); + r = r.reduce(ReceiptEvent::PublicationSynced).unwrap(); + assert_eq!(r.phase, ReceiptPhase::Published); + assert!(!r.usable_claim); + assert_eq!(r.source_epoch, 23); + assert_eq!(ReceiptFacts::decode(&r.encode()).unwrap(), r); + } + #[test] + fn dc_journal_101_frames_restart_immutable_source_epoch() { + let mut j = journal(); + for ordinal in 0..257 { + let leaf = LeafFacts { + ordinal, + address: [ordinal as u8; 64], + incarnation: [5; 32], + origin_claim: [6; 32], + bytes_digest: [7; 32], + len: 900, + }; + assert_eq!(LeafFacts::decode(&leaf.encode()).unwrap(), leaf); + assert_eq!(leaf.segment(), ordinal / 16); + j = j + .reduce(JournalEvent::Append { + ordinal, + frame_digest: Sha256::digest(leaf.encode()).into(), + next_address: leaf.address, + }) + .unwrap(); + j = JournalFacts::decode(&j.encode()).unwrap(); + assert_eq!(j.source_epoch, 23); + assert_eq!(j.committed_frames, ordinal + 1); + } + assert!(j + .reduce(JournalEvent::Append { + ordinal: 0, + frame_digest: [0; 32], + next_address: [0; 64] + }) + .is_err()); + j = j + .reduce(JournalEvent::CompleteObserved { + frames: 257, + source_epoch: 23, + }) + .unwrap(); + j = j + .reduce(JournalEvent::OwnEpochAdvanced { + before: 23, + after: 24, + }) + .unwrap(); + assert_eq!(j.source_epoch, 23); + assert_eq!( + j.replay_disposition(None), + ReplayDisposition::NeedsReprepare + ); + assert_eq!( + j.replay_disposition(Some([2; 32])), + ReplayDisposition::NeedsReprepare + ); + j = j.reduce(JournalEvent::DetachingRecorded).unwrap(); + assert_eq!(j.replay_disposition(None), ReplayDisposition::NeedsRecovery); + assert!(j + .reduce(JournalEvent::PrivateObserved { + actual_incarnation: [99; 32] + }) + .is_err()); + j = j + .reduce(JournalEvent::PrivateObserved { + actual_incarnation: [2; 32], + }) + .unwrap(); + for ordinal in 0..257 { + j = j.reduce(JournalEvent::Disposed { ordinal }).unwrap(); + j = JournalFacts::decode(&j.encode()).unwrap(); + } + assert_eq!( + j.reduce(JournalEvent::Finished { + private_absent: true + }) + .unwrap() + .phase, + JournalPhase::Done + ); + } + #[test] + fn dc_codecs_reject_torn_corrupt_and_reordered_progress() { + let mut r = receipt().encode(); + r[30] ^= 1; + assert_eq!(ReceiptFacts::decode(&r), Err(CodecIssue::Corrupt)); + let mut j = journal(); + assert!(j + .reduce(JournalEvent::CompleteObserved { + frames: 1, + source_epoch: 23 + }) + .is_err()); + j = j.reduce(JournalEvent::SourceChanged).unwrap(); + assert_eq!(j.source_epoch, 23); + assert!(j + .reduce(JournalEvent::Append { + ordinal: 0, + frame_digest: [0; 32], + next_address: [0; 64] + }) + .is_err()); + let mut encoded = j.encode(); + encoded[248] ^= 1; + assert_eq!(JournalFacts::decode(&encoded), Err(CodecIssue::Corrupt)); + } +} + +#[cfg(test)] +mod runtime_retirement_tests { + use super::*; + fn leaf() -> RetirementLeaf { + let id = super::super::locks::FileIdentity::from_fixed_words(7, 11); + RetirementLeaf { + stage: RetirementStage::AppendIntent, + source_epoch: 91, + stage_generation: 0, + stream_offset: 0, + key: [0xab; 32], + directory: id, + payload: id, + receipt: id, + scope: super::super::maintenance_admission::RootOriginBinding { + root: id, + coordination: id, + cohort: id, + publication_binding: id, + incarnation: [8; 32], + }, + operation: [9; 32], + byte_len: 1_000_000, + digest: [17; 32], + directory_created: true, + receipt_digest: [19; 32], + source_kind: 0, + source_name: [0; 134], + } + } + #[test] + fn runtime_leaf_frames_preserve_original_epoch_across_all_progress() { + let mut l = leaf(); + for stage in [ + RetirementStage::AppendIntent, + RetirementStage::FrameReady, + RetirementStage::Detached, + RetirementStage::PayloadGone, + RetirementStage::Done, + RetirementStage::ResetIntent, + ] { + l.stage = stage; + l.stage_generation = (stage as u64) - 1; + let decoded = RetirementLeaf::decode(&l.encode()).unwrap(); + assert_eq!(decoded, l); + assert_eq!(decoded.source_epoch, 91); + } + l.stage_generation = 0; + assert_eq!( + RetirementLeaf::decode(&l.encode()), + Err(CodecIssue::Transition) + ); + } + #[test] + fn runtime_leaf_temp_address_is_lossless_bound_and_strict() { + let mut l = leaf(); + l.source_kind = 1; + l.source_name[..5].copy_from_slice(b".tmp-"); + l.source_name[5..69].copy_from_slice("ab".repeat(32).as_bytes()); + l.source_name[69] = b'-'; + l.source_name[70..].fill(b'7'); + assert_eq!(RetirementLeaf::decode(&l.encode()).unwrap(), l); + l.source_name[5] = b'c'; + assert_eq!( + RetirementLeaf::decode(&l.encode()), + Err(CodecIssue::Corrupt) + ); + } + #[test] + fn runtime_leaf_tombstone_and_corruption_never_alias_new_operation() { + let l = leaf(); + let mut other = l; + other.operation[0] ^= 1; + assert_ne!(l.tombstone(), other.tombstone()); + assert!(l.tombstone().starts_with(b".retired-")); + let mut bytes = l.encode(); + bytes[72] ^= 1; + assert_eq!(RetirementLeaf::decode(&bytes), Err(CodecIssue::Corrupt)); + } +} + +/// Fixed restore intent facts. Construction/decoding grants no source or +/// destination authority; the backup adapter must rejoin the actual association. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct BackupRestoreRecord { + pub(crate) stage: u8, + pub(crate) source_epoch: u64, + pub(crate) root: super::locks::FileIdentity, + pub(crate) shard: super::locks::FileIdentity, + pub(crate) source: super::locks::FileIdentity, + pub(crate) payload: super::locks::FixedFileStamp, + pub(crate) digest: [u8; 32], + pub(crate) destination: super::locks::FileIdentity, + pub(crate) operation: [u8; 32], + pub(crate) name: [u8; 128], + pub(crate) name_len: u8, +} +impl BackupRestoreRecord { + pub(crate) fn encode(&self) -> [u8; 512] { + use sha2::{Digest, Sha256}; + let mut b = [0; 512]; + b[..8].copy_from_slice(b"CGBKR001"); + b[8] = self.stage; + b[16..24].copy_from_slice(&self.source_epoch.to_le_bytes()); + for (at, id) in [ + (24, self.root), + (40, self.shard), + (56, self.source), + (184, self.destination), + ] { + let (a, c) = id.fixed_words(); + b[at..at + 8].copy_from_slice(&a.to_le_bytes()); + b[at + 8..at + 16].copy_from_slice(&c.to_le_bytes()); + } + self.payload + .encode_checkpoint((&mut b[72..152]).try_into().unwrap()); + b[152..184].copy_from_slice(&self.digest); + b[200..232].copy_from_slice(&self.operation); + b[232] = self.name_len; + b[233..361].copy_from_slice(&self.name); + let hash = Sha256::digest(&b[..480]); + b[480..].copy_from_slice(&hash); + b + } + pub(crate) fn decode(b: &[u8; 512]) -> Result { + use sha2::{Digest, Sha256}; + let n = usize::from(b[232]); + if &b[..8] != b"CGBKR001" + || !(1..=4).contains(&b[8]) + || b[9..16].iter().chain(b[361..480].iter()).any(|x| *x != 0) + || n == 0 + || n > 128 + || b[233 + n..361].iter().any(|x| *x != 0) + || super::maintenance_backup::BackupAddress::parse(&b[233..233 + n]).is_none() + || b[200..232].iter().all(|x| *x == 0) + || b[480..] != Sha256::digest(&b[..480])[..] + { + return Err(CodecIssue::Corrupt); + } + let id = |at| { + super::locks::FileIdentity::from_fixed_words( + u64::from_le_bytes(b[at..at + 8].try_into().unwrap()), + u64::from_le_bytes(b[at + 8..at + 16].try_into().unwrap()), + ) + }; + Ok(Self { + stage: b[8], + source_epoch: u64::from_le_bytes(b[16..24].try_into().unwrap()), + root: id(24), + shard: id(40), + source: id(56), + payload: super::locks::FixedFileStamp::decode_checkpoint( + b[72..152].try_into().unwrap(), + ) + .map_err(|_| CodecIssue::Corrupt)?, + digest: b[152..184].try_into().unwrap(), + destination: id(184), + operation: b[200..232].try_into().unwrap(), + name: b[233..361].try_into().unwrap(), + name_len: b[232], + }) + } +} + +#[cfg(test)] +mod backup_restore_tests { + use super::*; + #[test] + fn backup_restore_record_preserves_exact_identities_and_rejects_foreign_addresses() { + let path = std::env::temp_dir().join(format!( + "dc-backup-codec-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + std::fs::create_dir(&path).unwrap(); + let file = std::fs::File::create(path.join("partition.json")).unwrap(); + let stamp = super::super::locks::FixedFileStamp::capture( + &file, + super::super::locks::FixedControlKind::File, + ) + .unwrap(); + let id = stamp.identity(); + let mut name = [0; 128]; + let address = format!(".replaced-{}-1", "a".repeat(64)); + name[..address.len()].copy_from_slice(address.as_bytes()); + let record = BackupRestoreRecord { + stage: 1, + source_epoch: 7, + root: id, + shard: id, + source: id, + payload: stamp, + digest: [3; 32], + destination: id, + operation: [4; 32], + name, + name_len: address.len() as u8, + }; + assert_eq!( + BackupRestoreRecord::decode(&record.encode()).unwrap(), + record + ); + for stage in [0, 5, 255] { + let mut invalid = record; + invalid.stage = stage; + assert!(BackupRestoreRecord::decode(&invalid.encode()).is_err()); + } + let mut foreign = record; + foreign.name[0] = b'x'; + assert!(BackupRestoreRecord::decode(&foreign.encode()).is_err()); + let mut zero_operation = record; + zero_operation.operation = [0; 32]; + assert!(BackupRestoreRecord::decode(&zero_operation.encode()).is_err()); + drop(file); + std::fs::remove_dir_all(path).unwrap(); + } +} diff --git a/src/storage/maintenance_json.rs b/src/storage/maintenance_json.rs new file mode 100644 index 00000000..38c28dcc --- /dev/null +++ b/src/storage/maintenance_json.rs @@ -0,0 +1,555 @@ +//! Allocation-bounded JSON facts, not schema, provenance or usability proofs. +use super::maintenance_budget::{CleanupMemory, MemoryReservation, ReservedBytes, ResourceLimit}; +use sha2::{Digest, Sha256}; +use std::io::{self, Read}; + +const IO_BYTES: usize = 4096; +// serde_json 1.0.150 defaults to 128 and rejects when decrement reaches zero. +// The ordinary artifact loader does not disable that recursion limit. +const MAX_CONTAINERS: usize = 127; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum JsonFailure { + Resource(ResourceLimit), + Io(io::ErrorKind), + Malformed, + LoaderDepthExceeded, + OffsetOverflow, +} +impl From for JsonFailure { + fn from(value: ResourceLimit) -> Self { + Self::Resource(value) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum TokenKind { + ObjectStart, + ObjectEnd, + ArrayStart, + ArrayEnd, + String { + key: bool, + decoded_len: u64, + decoded_sha256: [u8; 32], + producer_escapes: bool, + }, + Number, + Bool(bool), + Null, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct JsonToken { + pub(crate) start: u64, + pub(crate) end: u64, + pub(crate) kind: TokenKind, +} + +#[derive(Clone, Copy)] +enum Frame { + ObjectFirst, + ObjectKey, + Colon, + ObjectValue, + ObjectNext, + ArrayFirst, + ArrayValue, + ArrayNext, +} + +pub(crate) struct MaintenanceJsonReader { + input: R, + buffer: ReservedBytes, + cursor: usize, + filled: usize, + offset: u64, + frames: [Frame; MAX_CONTAINERS], + depth: usize, + started: bool, + failed: bool, + raw_hash: Sha256, + compact_hash: Sha256, + // Covers this reader's fixed state and bounded stack scratch, including R's + // inline layout. R must not conceal an uncharged heap buffer/path allocation. + _floor: MemoryReservation, +} +impl MaintenanceJsonReader { + pub(crate) fn new(input: R, memory: &CleanupMemory) -> Result { + let floor = std::mem::size_of::() + .checked_add(512) + .ok_or(JsonFailure::Resource(ResourceLimit::ArithmeticOverflow))?; + let reservation = memory.reserve(floor)?; + let buffer = memory.reserve(IO_BYTES)?.bytes(IO_BYTES)?; + Ok(Self { + input, + buffer, + cursor: 0, + filled: 0, + offset: 0, + frames: [Frame::ArrayFirst; MAX_CONTAINERS], + depth: 0, + started: false, + failed: false, + raw_hash: Sha256::new(), + compact_hash: Sha256::new(), + _floor: reservation, + }) + } + fn peek(&mut self) -> Result, JsonFailure> { + if self.cursor == self.filled { + loop { + match self.input.read(self.buffer.as_mut_slice()) { + Ok(n) => { + self.cursor = 0; + self.filled = n; + break; + } + Err(e) if e.kind() == io::ErrorKind::Interrupted => continue, + Err(e) => return Err(JsonFailure::Io(e.kind())), + } + } + } + Ok((self.cursor < self.filled).then(|| self.buffer.as_slice()[self.cursor])) + } + fn byte(&mut self, compact: bool) -> Result { + let byte = self.peek()?.ok_or(JsonFailure::Malformed)?; + self.offset = self + .offset + .checked_add(1) + .ok_or(JsonFailure::OffsetOverflow)?; + self.cursor += 1; + self.raw_hash.update([byte]); + if compact { + self.compact_hash.update([byte]); + } + Ok(byte) + } + fn whitespace(&mut self) -> Result<(), JsonFailure> { + while matches!(self.peek()?, Some(b' ' | b'\n' | b'\r' | b'\t')) { + self.byte(false)?; + } + Ok(()) + } + fn expected(&mut self, byte: u8) -> Result<(), JsonFailure> { + if self.byte(true)? == byte { + Ok(()) + } else { + Err(JsonFailure::Malformed) + } + } + fn hex4(&mut self) -> Result { + let mut value = 0; + for _ in 0..4 { + let digit = match self.byte(true)? { + b @ b'0'..=b'9' => b - b'0', + b @ b'a'..=b'f' => b - b'a' + 10, + b @ b'A'..=b'F' => b - b'A' + 10, + _ => return Err(JsonFailure::Malformed), + }; + value = value * 16 + u32::from(digit); + } + Ok(value) + } + fn string(&mut self, key: bool) -> Result { + self.expected(b'"')?; + let mut hash = Sha256::new(); + let mut len = 0u64; + let mut producer_escapes = true; + loop { + let byte = self.byte(true)?; + let mut scalar = [0u8; 4]; + let count; + match byte { + b'"' => break, + 0..=31 => return Err(JsonFailure::Malformed), + b'\\' => { + let escape = self.byte(true)?; + match escape { + b'"' | b'\\' => { + scalar[0] = escape; + count = 1; + } + b'/' => { + scalar[0] = b'/'; + count = 1; + producer_escapes = false; + } + b'b' => { + scalar[0] = 8; + count = 1; + } + b'f' => { + scalar[0] = 12; + count = 1; + } + b'n' => { + scalar[0] = 10; + count = 1; + } + b'r' => { + scalar[0] = 13; + count = 1; + } + b't' => { + scalar[0] = 9; + count = 1; + } + b'u' => { + let first = self.hex4()?; + let code = if (0xd800..=0xdbff).contains(&first) { + self.expected(b'\\')?; + self.expected(b'u')?; + let low = self.hex4()?; + if !(0xdc00..=0xdfff).contains(&low) { + return Err(JsonFailure::Malformed); + } + 0x10000 + ((first - 0xd800) << 10) + low - 0xdc00 + } else { + first + }; + let character = char::from_u32(code).ok_or(JsonFailure::Malformed)?; + count = character.encode_utf8(&mut scalar).len(); + // Conservative fact: escaped Unicode is never used + // as an exact producer-form proof by this tokenizer. + producer_escapes = false; + } + _ => return Err(JsonFailure::Malformed), + } + } + 0x20..=0x7f => { + scalar[0] = byte; + count = 1; + } + _ => { + count = match byte { + 0xc2..=0xdf => 2, + 0xe0..=0xef => 3, + 0xf0..=0xf4 => 4, + _ => return Err(JsonFailure::Malformed), + }; + scalar[0] = byte; + for value in &mut scalar[1..count] { + *value = self.byte(true)?; + } + std::str::from_utf8(&scalar[..count]).map_err(|_| JsonFailure::Malformed)?; + } + } + len = len + .checked_add(count as u64) + .ok_or(JsonFailure::OffsetOverflow)?; + hash.update(&scalar[..count]); + } + Ok(TokenKind::String { + key, + decoded_len: len, + decoded_sha256: hash.finalize().into(), + producer_escapes, + }) + } + fn number(&mut self) -> Result<(), JsonFailure> { + if self.peek()? == Some(b'-') { + self.byte(true)?; + } + match self.byte(true)? { + b'0' => {} + b'1'..=b'9' => { + while matches!(self.peek()?, Some(b'0'..=b'9')) { + self.byte(true)?; + } + } + _ => return Err(JsonFailure::Malformed), + } + if self.peek()? == Some(b'.') { + self.byte(true)?; + if !matches!(self.peek()?, Some(b'0'..=b'9')) { + return Err(JsonFailure::Malformed); + } + while matches!(self.peek()?, Some(b'0'..=b'9')) { + self.byte(true)?; + } + } + if matches!(self.peek()?, Some(b'e' | b'E')) { + self.byte(true)?; + if matches!(self.peek()?, Some(b'+' | b'-')) { + self.byte(true)?; + } + if !matches!(self.peek()?, Some(b'0'..=b'9')) { + return Err(JsonFailure::Malformed); + } + while matches!(self.peek()?, Some(b'0'..=b'9')) { + self.byte(true)?; + } + } + Ok(()) + } + fn value(&mut self) -> Result { + Ok(match self.peek()?.ok_or(JsonFailure::Malformed)? { + b'{' | b'[' => { + if self.depth == MAX_CONTAINERS { + return Err(JsonFailure::LoaderDepthExceeded); + } + let byte = self.byte(true)?; + self.frames[self.depth] = if byte == b'{' { + Frame::ObjectFirst + } else { + Frame::ArrayFirst + }; + self.depth += 1; + if byte == b'{' { + TokenKind::ObjectStart + } else { + TokenKind::ArrayStart + } + } + b'"' => self.string(false)?, + b'-' | b'0'..=b'9' => { + self.number()?; + TokenKind::Number + } + b't' => { + for byte in b"true" { + self.expected(*byte)?; + } + TokenKind::Bool(true) + } + b'f' => { + for byte in b"false" { + self.expected(*byte)?; + } + TokenKind::Bool(false) + } + b'n' => { + for byte in b"null" { + self.expected(*byte)?; + } + TokenKind::Null + } + _ => return Err(JsonFailure::Malformed), + }) + } + fn next_inner(&mut self) -> Result, JsonFailure> { + loop { + self.whitespace()?; + let start = self.offset; + let kind = if self.depth == 0 { + if self.started { + return if self.peek()?.is_none() { + Ok(None) + } else { + Err(JsonFailure::Malformed) + }; + } + self.started = true; + self.value()? + } else { + let index = self.depth - 1; + match self.frames[index] { + Frame::Colon => { + self.expected(b':')?; + self.frames[index] = Frame::ObjectValue; + continue; + } + Frame::ObjectFirst | Frame::ObjectKey => { + if matches!(self.frames[index], Frame::ObjectFirst) + && self.peek()? == Some(b'}') + { + self.byte(true)?; + self.depth -= 1; + TokenKind::ObjectEnd + } else { + let token = self.string(true)?; + self.frames[index] = Frame::Colon; + token + } + } + Frame::ObjectValue => { + self.frames[index] = Frame::ObjectNext; + self.value()? + } + Frame::ArrayFirst | Frame::ArrayValue => { + if matches!(self.frames[index], Frame::ArrayFirst) + && self.peek()? == Some(b']') + { + self.byte(true)?; + self.depth -= 1; + TokenKind::ArrayEnd + } else { + self.frames[index] = Frame::ArrayNext; + self.value()? + } + } + Frame::ObjectNext | Frame::ArrayNext => { + let object = matches!(self.frames[index], Frame::ObjectNext); + if self.peek()? == Some(b',') { + self.byte(true)?; + self.frames[index] = if object { + Frame::ObjectKey + } else { + Frame::ArrayValue + }; + continue; + } + self.expected(if object { b'}' } else { b']' })?; + self.depth -= 1; + if object { + TokenKind::ObjectEnd + } else { + TokenKind::ArrayEnd + } + } + } + }; + return Ok(Some(JsonToken { + start, + end: self.offset, + kind, + })); + } + } + pub(crate) fn next_token(&mut self) -> Result, JsonFailure> { + if self.failed { + return Err(JsonFailure::Malformed); + } + let result = self.next_inner(); + if result.is_err() { + self.failed = true; + } + result + } + /// Logical consumed offset, excluding buffered read-ahead. Facts only. + pub(crate) fn position(&self) -> u64 { + self.offset + } + /// Available only after complete EOF validation. Compact means whitespace + /// removal, NOT schema-specific or Serde canonicalization. + pub(crate) fn finish(mut self) -> Result<([u8; 32], [u8; 32]), JsonFailure> { + while self.next_token()?.is_some() {} + Ok(( + self.raw_hash.finalize().into(), + self.compact_hash.finalize().into(), + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + fn scan(bytes: &[u8]) -> Result<([u8; 32], [u8; 32]), JsonFailure> { + let memory = CleanupMemory::new(16384).unwrap(); + MaintenanceJsonReader::new(bytes, &memory)?.finish() + } + #[test] + fn dc_json_differential_producer_whitespace_hash() { + let values = [ + serde_json::json!({"x": [null, true, -1, 0.5, "é\n\\\""], "metadata": {"z": 1, "a": 2}}), + serde_json::json!([]), + ]; + for value in values { + let pretty = serde_json::to_vec_pretty(&value).unwrap(); + let compact = serde_json::to_vec(&value).unwrap(); + let (raw, stripped) = scan(&pretty).unwrap(); + assert_eq!(raw, <[u8; 32]>::from(Sha256::digest(&pretty))); + assert_eq!(stripped, <[u8; 32]>::from(Sha256::digest(&compact))); + } + } + #[test] + fn dc_json_producer_defaults_and_nonfinite_are_only_facts() { + #[derive(serde::Serialize, serde::Deserialize)] + struct Typed { + confidence: f64, + #[serde(default, skip_serializing_if = "Option::is_none")] + optional: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + rows: Vec, + } + for confidence in [0.5, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + let value = Typed { + confidence, + optional: None, + rows: Vec::new(), + }; + let pretty = serde_json::to_vec_pretty(&value).unwrap(); + let compact = serde_json::to_vec(&value).unwrap(); + assert_eq!( + scan(&pretty).unwrap().1, + <[u8; 32]>::from(Sha256::digest(&compact)) + ); + assert_eq!( + serde_json::from_slice::(&pretty).is_ok(), + confidence.is_finite() + ); + // Token acceptance and a matching compact hash do NOT certify that + // typed deserialization (including GraphEdgeRow) can read it. + } + } + #[test] + fn dc_json_long_string_dense_values_fixed_account() { + let input = format!("[\"{}\",{}0]", "é".repeat(100_000), "0,".repeat(100_000)); + let memory = CleanupMemory::new(16384).unwrap(); + let floor = memory.used(); + MaintenanceJsonReader::new(input.as_bytes(), &memory) + .unwrap() + .finish() + .unwrap(); + assert_eq!(memory.used(), floor); + assert!(memory.peak() < 16384); + } + #[test] + fn dc_json_unicode_duplicate_keys_and_lexical_facts() { + let input = r#"{"x":"\uD83D\uDE00","x":"😀"}"#.as_bytes(); + let memory = CleanupMemory::new(16384).unwrap(); + let mut reader = MaintenanceJsonReader::new(input, &memory).unwrap(); + let mut strings = Vec::new(); + while let Some(token) = reader.next_token().unwrap() { + if let TokenKind::String { + key: false, + decoded_len, + decoded_sha256, + producer_escapes, + } = token.kind + { + strings.push((decoded_len, decoded_sha256, producer_escapes)); + } + } + assert_eq!(strings[0].0, 4); + assert_eq!(strings[0].1, strings[1].1); + assert!(!strings[0].2); + assert!(strings[1].2); + } + #[test] + fn dc_json_rejects_malformed_trailing_utf8_and_matches_loader_depth() { + for input in [ + b"".as_slice(), + b"[1,]", + b"{\"x\":1,}", + b"01", + b"1.", + b"1e+", + b"null true", + br#""\uD800""#, + b"\"\xff\"", + b"[", + b"{1:2}", + ] { + assert!(scan(input).is_err(), "{input:?}"); + } + for depth in [126, 127, 128, 129] { + let input = format!("{}0{}", "[".repeat(depth), "]".repeat(depth)); + assert_eq!( + scan(input.as_bytes()).is_ok(), + serde_json::from_str::(&input).is_ok(), + "depth={depth}" + ); + } + // Lexically valid JSON may exceed a loader's numeric range: facts only. + assert!(scan(b"1e999999").is_ok()); + assert!(serde_json::from_slice::(b"1e999999").is_err()); + } + #[test] + fn dc_json_low_ceiling_releases_failed_construction() { + let memory = CleanupMemory::new(4096).unwrap(); + let floor = memory.used(); + assert!(MaintenanceJsonReader::new(&b"null"[..], &memory).is_err()); + assert_eq!(memory.used(), floor); + } +} diff --git a/src/storage/maintenance_legacy_association.rs b/src/storage/maintenance_legacy_association.rs new file mode 100644 index 00000000..527766dd --- /dev/null +++ b/src/storage/maintenance_legacy_association.rs @@ -0,0 +1,3994 @@ +//! Controlled historical association facts. These never enroll a root or grant +//! Ready, publication, migration, native-open, restoration or deletion authority. +use super::locks::{FileIdentity, FixedControlKind, FixedFileStamp, RootedDirectory}; +use super::maintenance_budget::{CleanupMemory, MemoryReservation, ResourceLimit}; +use super::maintenance_json::{JsonFailure, JsonToken, MaintenanceJsonReader, TokenKind}; +use super::maintenance_native_projection::{ + self as native, NativeCatalogEvent, NativeDefaultFacts, NativeSelectedTable, + NativeStringLocator, NativeStringSource, NativeUnknown, +}; +use super::maintenance_ownership::{ReferenceDomain, ReferenceEpoch}; +use super::maintenance_validation::{ + self as validation, ByteSource, KeyEncoding, ReplayWitnesses, Span, ValidationIssue, + WitnessEvent, +}; +use sha2::{Digest, Sha256}; +use std::cmp::Ordering; +use std::fs::File; +use std::io::{self, Read}; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum LegacyIssue { + Resource(ResourceLimit), + Io(io::ErrorKind), + Unknown, + Changed, + Cancelled, +} +impl From for LegacyIssue { + fn from(e: ResourceLimit) -> Self { + Self::Resource(e) + } +} +impl From for LegacyIssue { + fn from(e: io::Error) -> Self { + Self::Io(e.kind()) + } +} +impl From for LegacyIssue { + fn from(e: ValidationIssue) -> Self { + match e { + ValidationIssue::Resource(r) => Self::Resource(r), + ValidationIssue::Io(k) => Self::Io(k), + _ => Self::Unknown, + } + } +} +impl From for LegacyIssue { + fn from(e: JsonFailure) -> Self { + match e { + JsonFailure::Resource(r) => Self::Resource(r), + JsonFailure::Io(k) => Self::Io(k), + _ => Self::Unknown, + } + } +} +fn fixed(e: super::locks::FixedControlError) -> LegacyIssue { + match e { + super::locks::FixedControlError::Resource(r) => LegacyIssue::Resource(r), + super::locks::FixedControlError::Io(e) => e.into(), + _ => LegacyIssue::Changed, + } +} +fn native_error(e: NativeUnknown) -> LegacyIssue { + match e { + NativeUnknown::Source(e) => e.into(), + NativeUnknown::Cancelled => LegacyIssue::Cancelled, + #[cfg(test)] + NativeUnknown::SourceChanged => LegacyIssue::Changed, + _ => LegacyIssue::Unknown, + } +} +fn validation_error(e: LegacyIssue) -> ValidationIssue { + match e { + LegacyIssue::Resource(r) => ValidationIssue::Resource(r), + LegacyIssue::Io(k) => ValidationIssue::Io(k), + _ => ValidationIssue::Schema, + } +} +struct FileSource<'a> { + file: &'a File, + len: u64, +} +impl ByteSource for FileSource<'_> { + fn len(&self) -> u64 { + self.len + } + fn read_at(&self, o: u64, b: &mut [u8]) -> Result { + #[cfg(unix)] + { + use std::os::unix::fs::FileExt; + self.file + .read_at(b, o) + .map_err(|e| ValidationIssue::Io(e.kind())) + } + #[cfg(windows)] + { + use std::os::windows::fs::FileExt; + self.file + .seek_read(b, o) + .map_err(|e| ValidationIssue::Io(e.kind())) + } + #[cfg(not(any(unix, windows)))] + { + let _ = (o, b); + Err(ValidationIssue::Schema) + } + } +} +struct Slice<'a>(&'a [u8]); +impl ByteSource for Slice<'_> { + fn len(&self) -> u64 { + self.0.len() as u64 + } + fn read_at(&self, o: u64, b: &mut [u8]) -> Result { + self.0.read_at(o, b) + } +} +struct Window<'a> { + file: &'a File, + at: u64, + end: u64, + prefix: bool, +} +impl Read for Window<'_> { + fn read(&mut self, b: &mut [u8]) -> io::Result { + if self.prefix && !b.is_empty() { + self.prefix = false; + b[0] = b'{'; + return Ok(1); + } + let n = (self.end - self.at).min(b.len() as u64) as usize; + let source = FileSource { + file: self.file, + len: self.end, + }; + let n = source + .read_at(self.at, &mut b[..n]) + .map_err(|_| io::ErrorKind::InvalidData)?; + self.at += n as u64; + Ok(n) + } +} +fn span(t: JsonToken) -> Span { + Span { + start: t.start, + end: t.end, + } +} +fn raw_equal(source: &dyn ByteSource, s: Span, bytes: &[u8]) -> Result { + if s.end.checked_sub(s.start) != Some(bytes.len() as u64) { + return Ok(false); + } + let mut buf = [0; 256]; + let mut offset = 0; + while offset < bytes.len() { + let n = (bytes.len() - offset).min(buf.len()); + let got = source.read_at(s.start + offset as u64, &mut buf[..n])?; + if got != n || buf[..n] != bytes[offset..offset + n] { + return Ok(false); + } + offset += n; + } + Ok(true) +} +fn same_json( + source: &dyn ByteSource, + a: Span, + b: Span, + memory: &CleanupMemory, +) -> Result { + Ok(validation::compare_decoded_spans(source, a, source, b, memory)? == Ordering::Equal) +} +fn json_literal( + source: &dyn ByteSource, + a: Span, + literal: &[u8], + memory: &CleanupMemory, +) -> Result { + let right = Slice(literal); + Ok(validation::compare_native_json( + &right, + Span { + start: 0, + end: literal.len() as u64, + }, + source, + a, + memory, + )? == Ordering::Equal) +} + +struct Scanner<'a> { + source: &'a dyn ByteSource, + reader: MaintenanceJsonReader>, + memory: &'a CleanupMemory, + cancel: &'a dyn Fn() -> bool, + base: u64, +} +impl<'a> Scanner<'a> { + fn new( + file: &'a File, + source: &'a dyn ByteSource, + range: Span, + memory: &'a CleanupMemory, + cancel: &'a dyn Fn() -> bool, + ) -> Result { + Ok(Self { + source, + reader: MaintenanceJsonReader::new( + Window { + file, + at: range.start, + end: range.end, + prefix: false, + }, + memory, + )?, + memory, + cancel, + base: range.start, + }) + } + fn token(&mut self) -> Result { + if (self.cancel)() { + return Err(LegacyIssue::Cancelled); + } + let mut t = self.reader.next_token()?.ok_or(LegacyIssue::Unknown)?; + t.start += self.offset(); + t.end += self.offset(); + Ok(t) + } + // All current Scanner instances start at zero. Nested-range cursors below + // retain their explicit base, rather than rebadging parser-relative spans. + fn offset(&self) -> u64 { + self.base + } + fn field(&self, t: JsonToken, fields: &[&str]) -> Result, LegacyIssue> { + if !matches!(t.kind, TokenKind::String { key: true, .. }) { + return Err(LegacyIssue::Unknown); + } + for (i, f) in fields.iter().enumerate() { + if json_literal(self.source, span(t), f.as_bytes(), self.memory)? { + return Ok(Some(i)); + } + } + Ok(None) + } + fn string(&self, t: JsonToken) -> Result { + if matches!(t.kind, TokenKind::String { key: false, .. }) { + Ok(span(t)) + } else { + Err(LegacyIssue::Unknown) + } + } + fn skip(&mut self, t: JsonToken) -> Result { + let mut depth = usize::from(matches!( + t.kind, + TokenKind::ObjectStart | TokenKind::ArrayStart + )); + let mut end = t.end; + while depth > 0 { + let next = self.token()?; + end = next.end; + match next.kind { + TokenKind::ObjectStart | TokenKind::ArrayStart => depth += 1, + TokenKind::ObjectEnd | TokenKind::ArrayEnd => depth -= 1, + _ => {} + } + } + Ok(Span { + start: t.start, + end, + }) + } + fn key(&self, t: JsonToken) -> Result<[u8; 32], LegacyIssue> { + self.string(t)?; + let mut bytes = [0; 64]; + if validation::copy_decoded_key( + self.source, + span(t), + KeyEncoding::JsonDecoded, + &mut bytes, + self.memory, + )? != 64 + { + return Err(LegacyIssue::Unknown); + } + let mut key = [0; 32]; + for (i, pair) in bytes.chunks_exact(2).enumerate() { + let digit = |b| match b { + b'0'..=b'9' => Some(b - b'0'), + b'a'..=b'f' => Some(b - b'a' + 10), + _ => None, + }; + key[i] = digit(pair[0]).ok_or(LegacyIssue::Unknown)? * 16 + + digit(pair[1]).ok_or(LegacyIssue::Unknown)?; + } + Ok(key) + } + fn uint(&self, t: JsonToken) -> Result { + if !matches!(t.kind, TokenKind::Number) { + return Err(LegacyIssue::Unknown); + } + let mut raw = [0; 32]; + let len = t.end.checked_sub(t.start).ok_or(LegacyIssue::Unknown)? as usize; + if len > raw.len() { + return Err(LegacyIssue::Unknown); + } + let n = self.source.read_at(t.start, &mut raw[..len])?; + if n != len { + return Err(LegacyIssue::Unknown); + } + serde_json::from_slice::(&raw[..len]).map_err(|_| LegacyIssue::Unknown) + } + fn strings(&mut self, t: JsonToken, map: bool) -> Result { + if t.kind + != if map { + TokenKind::ObjectStart + } else { + TokenKind::ArrayStart + } + { + return Err(LegacyIssue::Unknown); + } + let mut previous = None; + let mut count = 0u64; + loop { + let next = self.token()?; + if next.kind + == if map { + TokenKind::ObjectEnd + } else { + TokenKind::ArrayEnd + } + { + break; + } + if map { + if !matches!(next.kind, TokenKind::String { key: true, .. }) { + return Err(LegacyIssue::Unknown); + } + if let Some(p) = previous { + if validation::compare_decoded_spans( + self.source, + p, + self.source, + span(next), + self.memory, + )? != Ordering::Less + { + return Err(LegacyIssue::Unknown); + } + } + previous = Some(span(next)); + let value = self.token()?; + self.string(value)?; + } else { + self.string(next)?; + } + count = count.checked_add(1).ok_or(LegacyIssue::Unknown)?; + } + Ok(count) + } + fn search_backend(&mut self, t: JsonToken) -> Result<(), LegacyIssue> { + if t.kind == TokenKind::Null { + return Ok(()); + } + if t.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + let mut seen = 0u8; + loop { + let key = self.token()?; + if key.kind == TokenKind::ObjectEnd { + break; + } + let field = self.field( + key, + &[ + "backend", + "format_version", + "files", + "document_count", + "term_count", + "total_tokens", + ], + )?; + let value = self.token()?; + if let Some(i) = field { + if seen & (1 << i) != 0 { + return Err(LegacyIssue::Unknown); + } + seen |= 1 << i; + match i { + 0 => { + self.string(value)?; + } + 2 => { + self.strings(value, true)?; + } + _ => { + self.uint(value)?; + } + } + } else { + self.skip(value)?; + } + } + if seen & 3 != 3 { + return Err(LegacyIssue::Unknown); + } + Ok(()) + } + fn manifest_entry(&mut self) -> Result { + let mut r = ManifestRecord::default(); + let mut seen = 0u16; + if self.token()?.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + loop { + let k = self.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let f = self.field( + k, + &[ + "path", + "content_hash", + "language", + "partition_id", + "artifact_key", + "node_count", + "edge_count", + "node_ids", + "edge_ids", + "node_types", + "edge_types", + "materialized_at", + ], + )?; + let v = self.token()?; + if let Some(i) = f { + if seen & (1 << i) != 0 { + return Err(LegacyIssue::Unknown); + } + seen |= 1 << i; + match i { + 0 => r.path = self.string(v)?, + 1 => r.hash = self.string(v)?, + 2 => r.language = self.string(v)?, + 3 => r.partition = self.string(v)?, + 4 => r.key = self.key(v)?, + 5 => r.nodes = self.uint(v)?, + 6 => r.edges = self.uint(v)?, + 7..=10 => { + self.strings(v, i >= 9)?; + } + 11 => { + self.string(v)?; + } + _ => unreachable!(), + } + } else { + self.skip(v)?; + } + } + if seen & 31 != 31 { + return Err(LegacyIssue::Unknown); + } + Ok(r) + } +} +#[derive(Debug, Clone, Copy, Default)] +struct ManifestRecord { + path: Span, + hash: Span, + language: Span, + partition: Span, + key: [u8; 32], + nodes: u64, + edges: u64, +} + +// Exact producer schema from assets/graph_schema.json and catalog_support.rs. +const TABLE_NAMES: [&str; 112] = [ + "APIEndpoint", + "Assignment", + "CallExpression", + "Class", + "ClassAttribute", + "Component", + "Constant", + "ControlFlowBlock", + "Decorator", + "Dependency", + "DocumentationChunk", + "DocumentationSource", + "ExceptionFlow", + "ExportDeclaration", + "Expression", + "File", + "Function", + "ImportDeclaration", + "InstanceAttribute", + "Literal", + "Method", + "Module", + "Parameter", + "Property", + "Query", + "Reference", + "Repository", + "ReturnType", + "Route", + "Scope", + "SecretRef", + "SourceRoot", + "Symbol", + "SyntaxCapture", + "TypeAlias", + "TypeAnnotation", + "Variable", + "Assigns", + "Calls", + "Contains", + "Declares", + "DecoratedBy", + "Defines", + "DependsOn", + "DerivedFrom", + "Documents", + "EvidencedBy", + "ExecutesQuery", + "Exports", + "Exposes", + "Handles", + "HasParameter", + "HasReturnType", + "HasScope", + "HasTypeAnnotation", + "Imports", + "Raises", + "References", + "ResolvesTo", + "RoutesTo", + "SyntaxChild", + "UsesSecret", + "FROM_Assigns", + "FROM_Calls", + "FROM_Contains", + "FROM_Declares", + "FROM_DecoratedBy", + "FROM_Defines", + "FROM_DependsOn", + "FROM_DerivedFrom", + "FROM_Documents", + "FROM_EvidencedBy", + "FROM_ExecutesQuery", + "FROM_Exports", + "FROM_Exposes", + "FROM_Handles", + "FROM_HasParameter", + "FROM_HasReturnType", + "FROM_HasScope", + "FROM_HasTypeAnnotation", + "FROM_Imports", + "FROM_Raises", + "FROM_References", + "FROM_ResolvesTo", + "FROM_RoutesTo", + "FROM_SyntaxChild", + "FROM_UsesSecret", + "TO_Assigns", + "TO_Calls", + "TO_Contains", + "TO_Declares", + "TO_DecoratedBy", + "TO_Defines", + "TO_DependsOn", + "TO_DerivedFrom", + "TO_Documents", + "TO_EvidencedBy", + "TO_ExecutesQuery", + "TO_Exports", + "TO_Exposes", + "TO_Handles", + "TO_HasParameter", + "TO_HasReturnType", + "TO_HasScope", + "TO_HasTypeAnnotation", + "TO_Imports", + "TO_Raises", + "TO_References", + "TO_ResolvesTo", + "TO_RoutesTo", + "TO_SyntaxChild", + "TO_UsesSecret", +]; +const ENDPOINTS: &[(usize, usize, usize)] = &[ + (64, 3, 39), + (64, 5, 39), + (64, 15, 39), + (64, 16, 39), + (64, 20, 39), + (64, 21, 39), + (64, 26, 39), + (64, 29, 39), + (64, 31, 39), + (89, 39, 0), + (89, 39, 1), + (89, 39, 2), + (89, 39, 3), + (89, 39, 4), + (89, 39, 5), + (89, 39, 6), + (89, 39, 7), + (89, 39, 8), + (89, 39, 10), + (89, 39, 11), + (89, 39, 12), + (89, 39, 13), + (89, 39, 14), + (89, 39, 15), + (89, 39, 16), + (89, 39, 17), + (89, 39, 18), + (89, 39, 19), + (89, 39, 20), + (89, 39, 21), + (89, 39, 22), + (89, 39, 23), + (89, 39, 24), + (89, 39, 25), + (89, 39, 27), + (89, 39, 28), + (89, 39, 29), + (89, 39, 30), + (89, 39, 31), + (89, 39, 32), + (89, 39, 34), + (89, 39, 35), + (89, 39, 36), + (67, 3, 42), + (67, 5, 42), + (67, 15, 42), + (67, 16, 42), + (67, 20, 42), + (67, 21, 42), + (67, 29, 42), + (92, 42, 0), + (92, 42, 1), + (92, 42, 3), + (92, 42, 4), + (92, 42, 5), + (92, 42, 6), + (92, 42, 8), + (92, 42, 16), + (92, 42, 18), + (92, 42, 20), + (92, 42, 22), + (92, 42, 23), + (92, 42, 24), + (92, 42, 27), + (92, 42, 28), + (92, 42, 30), + (92, 42, 32), + (92, 42, 34), + (92, 42, 35), + (92, 42, 36), + (80, 15, 55), + (80, 21, 55), + (80, 29, 55), + (105, 55, 9), + (105, 55, 17), + (105, 55, 21), + (105, 55, 32), + (73, 5, 48), + (73, 15, 48), + (73, 21, 48), + (73, 29, 48), + (98, 48, 0), + (98, 48, 1), + (98, 48, 3), + (98, 48, 4), + (98, 48, 5), + (98, 48, 6), + (98, 48, 8), + (98, 48, 13), + (98, 48, 16), + (98, 48, 18), + (98, 48, 20), + (98, 48, 22), + (98, 48, 23), + (98, 48, 24), + (98, 48, 27), + (98, 48, 28), + (98, 48, 30), + (98, 48, 32), + (98, 48, 34), + (98, 48, 35), + (98, 48, 36), + (65, 1, 40), + (65, 3, 40), + (65, 16, 40), + (65, 20, 40), + (65, 21, 40), + (65, 29, 40), + (90, 40, 0), + (90, 40, 1), + (90, 40, 3), + (90, 40, 4), + (90, 40, 5), + (90, 40, 6), + (90, 40, 8), + (90, 40, 16), + (90, 40, 18), + (90, 40, 20), + (90, 40, 22), + (90, 40, 23), + (90, 40, 24), + (90, 40, 27), + (90, 40, 28), + (90, 40, 30), + (90, 40, 32), + (90, 40, 34), + (90, 40, 35), + (90, 40, 36), + (78, 0, 53), + (78, 1, 53), + (78, 2, 53), + (78, 3, 53), + (78, 4, 53), + (78, 5, 53), + (78, 6, 53), + (78, 7, 53), + (78, 8, 53), + (78, 12, 53), + (78, 14, 53), + (78, 15, 53), + (78, 16, 53), + (78, 18, 53), + (78, 19, 53), + (78, 20, 53), + (78, 21, 53), + (78, 22, 53), + (78, 23, 53), + (78, 24, 53), + (78, 25, 53), + (78, 27, 53), + (78, 28, 53), + (78, 30, 53), + (78, 32, 53), + (78, 34, 53), + (78, 35, 53), + (78, 36, 53), + (103, 53, 29), + (76, 0, 51), + (76, 2, 51), + (76, 16, 51), + (76, 20, 51), + (76, 28, 51), + (101, 51, 22), + (77, 0, 52), + (77, 16, 52), + (77, 20, 52), + (102, 52, 27), + (79, 4, 54), + (79, 6, 54), + (79, 18, 54), + (79, 22, 54), + (79, 27, 54), + (79, 32, 54), + (79, 34, 54), + (79, 36, 54), + (104, 54, 19), + (104, 54, 25), + (104, 54, 35), + (62, 1, 37), + (62, 4, 37), + (62, 6, 37), + (62, 18, 37), + (62, 23, 37), + (62, 36, 37), + (87, 37, 2), + (87, 37, 4), + (87, 37, 6), + (87, 37, 14), + (87, 37, 18), + (87, 37, 19), + (87, 37, 23), + (87, 37, 36), + (82, 1, 57), + (82, 2, 57), + (82, 7, 57), + (82, 8, 57), + (82, 14, 57), + (82, 24, 57), + (82, 25, 57), + (82, 30, 57), + (82, 35, 57), + (107, 57, 3), + (107, 57, 4), + (107, 57, 6), + (107, 57, 9), + (107, 57, 16), + (107, 57, 18), + (107, 57, 20), + (107, 57, 21), + (107, 57, 22), + (107, 57, 23), + (107, 57, 32), + (107, 57, 36), + (63, 0, 38), + (63, 2, 38), + (63, 5, 38), + (63, 8, 38), + (63, 16, 38), + (63, 20, 38), + (63, 28, 38), + (88, 38, 0), + (88, 38, 2), + (88, 38, 3), + (88, 38, 16), + (88, 38, 20), + (66, 0, 41), + (66, 3, 41), + (66, 5, 41), + (66, 16, 41), + (66, 20, 41), + (66, 23, 41), + (66, 28, 41), + (91, 41, 2), + (91, 41, 8), + (91, 41, 25), + (83, 2, 58), + (83, 8, 58), + (83, 17, 58), + (83, 25, 58), + (83, 35, 58), + (108, 58, 3), + (108, 58, 6), + (108, 58, 9), + (108, 58, 16), + (108, 58, 20), + (108, 58, 21), + (108, 58, 22), + (108, 58, 32), + (108, 58, 36), + (68, 5, 43), + (68, 9, 43), + (68, 15, 43), + (68, 17, 43), + (68, 21, 43), + (68, 26, 43), + (68, 31, 43), + (93, 43, 5), + (93, 43, 9), + (93, 43, 21), + (93, 43, 30), + (70, 10, 45), + (70, 11, 45), + (70, 19, 45), + (95, 45, 0), + (95, 45, 1), + (95, 45, 3), + (95, 45, 4), + (95, 45, 5), + (95, 45, 6), + (95, 45, 8), + (95, 45, 15), + (95, 45, 16), + (95, 45, 18), + (95, 45, 20), + (95, 45, 21), + (95, 45, 22), + (95, 45, 23), + (95, 45, 24), + (95, 45, 26), + (95, 45, 27), + (95, 45, 28), + (95, 45, 30), + (95, 45, 32), + (95, 45, 34), + (95, 45, 35), + (95, 45, 36), + (84, 0, 59), + (84, 5, 59), + (84, 28, 59), + (109, 59, 0), + (109, 59, 5), + (109, 59, 16), + (109, 59, 20), + (74, 0, 49), + (74, 5, 49), + (74, 21, 49), + (74, 26, 49), + (74, 28, 49), + (99, 49, 0), + (99, 49, 5), + (99, 49, 13), + (99, 49, 16), + (99, 49, 20), + (99, 49, 28), + (72, 0, 47), + (72, 2, 47), + (72, 5, 47), + (72, 16, 47), + (72, 20, 47), + (97, 47, 24), + (86, 0, 61), + (86, 2, 61), + (86, 5, 61), + (86, 9, 61), + (86, 16, 61), + (86, 20, 61), + (111, 61, 30), + (81, 2, 56), + (81, 7, 56), + (81, 16, 56), + (81, 20, 56), + (106, 56, 12), + (75, 7, 50), + (75, 12, 50), + (75, 16, 50), + (75, 20, 50), + (100, 50, 12), + (85, 33, 60), + (110, 60, 33), + (69, 0, 44), + (69, 1, 44), + (69, 2, 44), + (69, 3, 44), + (69, 4, 44), + (69, 5, 44), + (69, 6, 44), + (69, 7, 44), + (69, 8, 44), + (69, 10, 44), + (69, 11, 44), + (69, 12, 44), + (69, 13, 44), + (69, 14, 44), + (69, 16, 44), + (69, 17, 44), + (69, 18, 44), + (69, 19, 44), + (69, 20, 44), + (69, 21, 44), + (69, 22, 44), + (69, 23, 44), + (69, 24, 44), + (69, 25, 44), + (69, 27, 44), + (69, 28, 44), + (69, 30, 44), + (69, 32, 44), + (69, 34, 44), + (69, 35, 44), + (69, 36, 44), + (94, 44, 33), + (71, 0, 46), + (71, 1, 46), + (71, 2, 46), + (71, 3, 46), + (71, 4, 46), + (71, 5, 46), + (71, 6, 46), + (71, 7, 46), + (71, 8, 46), + (71, 9, 46), + (71, 10, 46), + (71, 11, 46), + (71, 12, 46), + (71, 14, 46), + (71, 15, 46), + (71, 16, 46), + (71, 18, 46), + (71, 19, 46), + (71, 20, 46), + (71, 21, 46), + (71, 22, 46), + (71, 23, 46), + (71, 24, 46), + (71, 25, 46), + (71, 26, 46), + (71, 27, 46), + (71, 28, 46), + (71, 30, 46), + (71, 32, 46), + (71, 34, 46), + (71, 35, 46), + (71, 36, 46), + (96, 46, 10), + (96, 46, 15), + (96, 46, 33), +]; +/// Current program schema facts for the separately guarded legacy COPY lane. +pub(crate) fn staging_endpoint_allowed(table: u8, from: u8, to: u8) -> bool { + ENDPOINTS.contains(&(table as usize, from as usize, to as usize)) +} + +const NODE_FIELDS: [(&[u8], u8, u8); 17] = [ + (b"id", 50, 20), + (b"label", 50, 20), + (b"kind", 50, 20), + (b"language", 50, 20), + (b"grammar_version", 50, 20), + (b"path", 50, 20), + (b"qualified_name", 50, 20), + (b"scope_id", 50, 20), + (b"line_start", 23, 2), + (b"line_end", 23, 2), + (b"byte_start", 23, 2), + (b"byte_end", 23, 2), + (b"tree_sitter_node_type", 50, 20), + (b"capture_name", 50, 20), + (b"summary", 50, 20), + (b"text", 50, 20), + (b"metadata", 60, 21), +]; +const EDGE_FIELDS: [(&[u8], u8, u8); 10] = [ + (b"id", 50, 20), + (b"kind", 50, 20), + (b"source_id", 50, 20), + (b"target_id", 50, 20), + (b"confidence", 32, 11), + (b"field_name", 50, 20), + (b"child_index", 23, 2), + (b"line_start", 23, 2), + (b"line_end", 23, 2), + (b"metadata", 60, 21), +]; +#[derive(Clone, Copy)] +struct CatalogCell { + id: u64, + seen: bool, + properties: u32, + columns: u32, + group: bool, +} +#[derive(Clone, Copy)] +struct Endpoint { + table: u64, + source: u64, + target: u64, +} +fn recognize_catalog( + source: &dyn ByteSource, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result<(), LegacyIssue> { + let _floor = memory.reserve( + std::mem::size_of::<[CatalogCell; 112]>() + + std::mem::size_of::<[Endpoint; ENDPOINTS.len()]>() + + 2048, + )?; + let mut cells = [CatalogCell { + id: u64::MAX, + seen: false, + properties: 0, + columns: 0, + group: false, + }; 112]; + let mut endpoints = [Endpoint { + table: u64::MAX, + source: 0, + target: 0, + }; ENDPOINTS.len()]; + let mut endpoint_count = 0usize; + let result = native::trace_native_catalog43(source, memory, &mut |event| { + if cancel() { + return Err(NativeUnknown::Cancelled); + } + let run = (|| -> Result<(), LegacyIssue> { + match event { + NativeCatalogEvent::Table { id, name, node } => { + let mut index = None; + for (i, n) in TABLE_NAMES.iter().enumerate() { + if raw_equal(source, name, n.as_bytes())? { + index = Some(i); + break; + } + } + let i = index.ok_or(LegacyIssue::Unknown)?; + if cells[i].seen || node != (i < 62) { + return Err(LegacyIssue::Unknown); + } + cells[i].seen = true; + cells[i].id = id; + } + NativeCatalogEvent::Property { + table, + id, + name, + logical, + physical, + default, + } => { + let i = cells + .iter() + .position(|c| c.seen && c.id == table) + .ok_or(LegacyIssue::Unknown)?; + let p = id as usize; + let expected = if i < 37 { + if p < NODE_FIELDS.len() { + NODE_FIELDS[p] + } else if TABLE_NAMES[i] == "File" && p == 17 { + (b"content_hash".as_slice(), 50, 20) + } else { + return Err(LegacyIssue::Unknown); + } + } else if i < 62 { + *EDGE_FIELDS.get(p).ok_or(LegacyIssue::Unknown)? + } else { + match p { + 0 => (b"_ID".as_slice(), 42, 14), + 1 => (b"role".as_slice(), 50, 20), + _ => return Err(LegacyIssue::Unknown), + } + }; + if cells[i].properties & (1 << p) != 0 + || !raw_equal(source, name, expected.0)? + || (logical, physical) != (expected.1, expected.2) + { + return Err(LegacyIssue::Unknown); + } + match default { + NativeDefaultFacts::Null => { + if i >= 62 && p == 1 { + return Err(LegacyIssue::Unknown); + } + } + NativeDefaultFacts::Text(s) => { + if i < 62 + || p != 1 + || !raw_equal( + source, + s, + if i < 87 { b"source" } else { b"target" }, + )? + { + return Err(LegacyIssue::Unknown); + } + } + } + cells[i].properties |= 1 << p; + } + NativeCatalogEvent::Column { + table, + property, + column, + } => { + let i = cells + .iter() + .position(|c| c.seen && c.id == table) + .ok_or(LegacyIssue::Unknown)?; + if property.checked_add(u32::from(i >= 62)) != Some(column) + || property >= 18 + || cells[i].columns & (1 << property) != 0 + { + return Err(LegacyIssue::Unknown); + } + cells[i].columns |= 1 << property; + } + NativeCatalogEvent::RelationshipGroup { + table, + source_multiplicity, + target_multiplicity, + direction, + } => { + let i = cells + .iter() + .position(|c| c.seen && c.id == table) + .ok_or(LegacyIssue::Unknown)?; + if i < 62 + || cells[i].group + || (source_multiplicity, target_multiplicity, direction) != (0, 0, 2) + { + return Err(LegacyIssue::Unknown); + } + cells[i].group = true; + } + NativeCatalogEvent::RelationshipEndpoint { + table, + source, + target, + source_flag, + target_flag, + .. + } => { + if source_flag || target_flag || endpoint_count >= endpoints.len() { + return Err(LegacyIssue::Unknown); + } + endpoints[endpoint_count] = Endpoint { + table, + source, + target, + }; + endpoint_count += 1; + } + _ => {} + } + Ok(()) + })(); + run.map_err(|e| match e { + LegacyIssue::Resource(r) => NativeUnknown::Source(ValidationIssue::Resource(r)), + LegacyIssue::Cancelled => NativeUnknown::Cancelled, + _ => NativeUnknown::UnsupportedLayout, + }) + }) + .map_err(native_error)?; + if result.tables != 112 || result.nodes != 62 || endpoint_count != ENDPOINTS.len() { + return Err(LegacyIssue::Unknown); + } + for (i, c) in cells.iter().enumerate() { + let count = if i < 37 { + if TABLE_NAMES[i] == "File" { + 18 + } else { + 17 + } + } else if i < 62 { + 10 + } else { + 2 + }; + let mask = (1 << count) - 1; + if !c.seen || c.properties != mask || c.columns != mask || c.group != (i >= 62) { + return Err(LegacyIssue::Unknown); + } + } + let mut matched = [false; ENDPOINTS.len()]; + for e in &endpoints[..endpoint_count] { + let t = cells + .iter() + .position(|c| c.id == e.table) + .ok_or(LegacyIssue::Unknown)?; + let s = cells + .iter() + .position(|c| c.id == e.source) + .ok_or(LegacyIssue::Unknown)?; + let d = cells + .iter() + .position(|c| c.id == e.target) + .ok_or(LegacyIssue::Unknown)?; + let i = ENDPOINTS + .iter() + .position(|v| *v == (t, s, d)) + .ok_or(LegacyIssue::Unknown)?; + if matched[i] { + return Err(LegacyIssue::Unknown); + } + matched[i] = true; + } + if matched.iter().any(|v| !*v) { + return Err(LegacyIssue::Unknown); + } + Ok(()) +} + +fn project_manifest( + file: &File, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + let stamp = FixedFileStamp::capture(file, FixedControlKind::File).map_err(fixed)?; + let source = FileSource { + file, + len: stamp.len(), + }; + let mut scanner = Scanner::new( + file, + &source, + Span { + start: 0, + end: source.len(), + }, + memory, + cancel, + )?; + let mut fields = 0u8; + let mut files = None; + if scanner.token()?.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + loop { + let k = scanner.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let f = scanner.field( + k, + &[ + "schema_version", + "ontology", + "parser_version", + "graph_build_digest", + "search_backend", + "files", + ], + )?; + let v = scanner.token()?; + if let Some(i) = f { + if fields & (1 << i) != 0 { + return Err(LegacyIssue::Unknown); + } + fields |= 1 << i; + match i { + 0 => { + if scanner.uint(v)? != 5 { + return Err(LegacyIssue::Unknown); + } + } + 1 | 2 => { + scanner.string(v)?; + } + 3 => { + if v.kind != TokenKind::Null { + scanner.string(v)?; + } + } + 4 => { + scanner.search_backend(v)?; + } + 5 => { + if v.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + files = Some(scanner.skip(v)?); + } + _ => unreachable!(), + } + } else { + scanner.skip(v)?; + } + } + if fields & 39 != 39 || scanner.reader.next_token()?.is_some() { + return Err(LegacyIssue::Unknown); + } + stamp.recheck(file).map_err(fixed)?; + files.ok_or(LegacyIssue::Unknown) +} +// Retain logical progress, not duplicate native descriptors. Each next() owns +// a charged bounded reader over the same borrowed manifest with an independent +// positional offset. Replaying its one-token lookahead preserves exact spans. +struct ManifestCursor { + end: u64, + previous: Option, + next_offset: u64, + done: bool, +} +impl ManifestCursor { + fn new(file: &File, range: Span, memory: &CleanupMemory) -> Result { + let mut reader = MaintenanceJsonReader::new( + Window { + file, + at: range.start, + end: range.end, + prefix: false, + }, + memory, + )?; + if reader.next_token()?.map(|token| token.kind) != Some(TokenKind::ObjectStart) { + return Err(LegacyIssue::Unknown); + } + Ok(Self { + end: range.end, + previous: None, + next_offset: range.start + 1, + done: false, + }) + } + fn resume( + file: &File, + range: Span, + next_offset: u64, + previous: Option, + memory: &CleanupMemory, + ) -> Result { + if next_offset <= range.start || next_offset >= range.end { + return Err(LegacyIssue::Unknown); + } + let mut reader = MaintenanceJsonReader::new( + Window { + file, + at: next_offset, + end: range.end, + prefix: true, + }, + memory, + )?; + if reader.next_token()?.map(|token| token.kind) != Some(TokenKind::ObjectStart) { + return Err(LegacyIssue::Unknown); + } + Ok(Self { + end: range.end, + previous, + next_offset, + done: false, + }) + } + fn next( + &mut self, + file: &File, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + ) -> Result, LegacyIssue> { + if self.done { + return Ok(None); + } + let source = FileSource { + file, + len: file.metadata()?.len(), + }; + let mut reader = MaintenanceJsonReader::new( + Window { + file, + at: self.next_offset, + end: self.end, + prefix: true, + }, + memory, + )?; + if reader.next_token()?.map(|token| token.kind) != Some(TokenKind::ObjectStart) { + return Err(LegacyIssue::Unknown); + } + let mut scanner = Scanner { + source: &source, + reader, + memory, + cancel, + base: self.next_offset - 1, + }; + let key = scanner.token()?; + if key.kind == TokenKind::ObjectEnd { + self.next_offset = key.start; + self.done = true; + return Ok(None); + } + if !matches!(key.kind, TokenKind::String { key: true, .. }) { + return Err(LegacyIssue::Unknown); + } + let keyspan = span(key); + if let Some(previous) = self.previous { + if validation::compare_decoded_spans(&source, previous, &source, keyspan, memory)? + != Ordering::Less + { + return Err(LegacyIssue::Unknown); + } + } + let record = scanner.manifest_entry()?; + if !same_json(&source, keyspan, record.path, memory)? { + return Err(LegacyIssue::Unknown); + } + self.previous = Some(keyspan); + self.next_offset = scanner.token()?.start; + Ok(Some(record)) + } +} + +fn hex(key: [u8; 32]) -> [u8; 64] { + let mut out = [0; 64]; + let digits = b"0123456789abcdef"; + for (i, b) in key.iter().enumerate() { + out[2 * i] = digits[(b >> 4) as usize]; + out[2 * i + 1] = digits[(b & 15) as usize]; + } + out +} +#[derive(Clone, Copy)] +struct BackupCache { + key: [u8; 32], + name: [u8; 128], + len: u8, + directory: FileIdentity, + stamp: FixedFileStamp, +} +fn backup_name(key: [u8; 32], name: &[u8]) -> bool { + if name.len() < 76 + || name.len() > 114 + || &name[..10] != b".replaced-" + || name[10..74] != hex(key) + || name[74] != b'-' + { + return false; + } + let digits = &name[75..]; + if digits.len() > 1 && digits[0] == b'0' { + return false; + } + digits + .iter() + .try_fold(0u128, |n, b| { + if b.is_ascii_digit() { + n.checked_mul(10)?.checked_add((b - b'0') as u128) + } else { + None + } + }) + .is_some() +} +fn open_cache( + root: &RootedDirectory, + key: [u8; 32], + backup: Option, +) -> Result { + let name = hex(key); + let prefix = root.open_directory_readonly(&name[..2])?; + let directory = if let Some(b) = backup.filter(|b| b.key == key) { + match prefix.open_directory_readonly(&name) { + Ok(_) => return Err(LegacyIssue::Changed), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(e.into()), + } + let directory = prefix.open_directory_readonly(&b.name[..usize::from(b.len)])?; + if directory.identity() != b.directory { + return Err(LegacyIssue::Changed); + } + directory + } else { + prefix.open_directory_readonly(&name)? + }; + let file = directory.open_readonly_file(b"partition.json")?; + if let Some(b) = backup.filter(|b| b.key == key) { + b.stamp.recheck(&file).map_err(fixed)?; + } + Ok(file) +} + +/// Key comparators may nest two logical sources while native DB and manifest +/// remain pinned. Each positional read borrows one payload descriptor and drops +/// it before the other source reads, preserving the aggregate three-FD bound. +struct CacheKeySource<'a> { + root: &'a RootedDirectory, + key: [u8; 32], + backup: Option, + stamp: FixedFileStamp, +} +impl ByteSource for CacheKeySource<'_> { + fn len(&self) -> u64 { + self.stamp.len() + } + fn read_at(&self, offset: u64, bytes: &mut [u8]) -> Result { + let file = open_cache(self.root, self.key, self.backup).map_err(validation_error)?; + self.stamp + .recheck(&file) + .map_err(|_| ValidationIssue::Schema)?; + let source = FileSource { + file: &file, + len: self.stamp.len(), + }; + let read = source.read_at(offset, bytes)?; + self.stamp + .recheck(&file) + .map_err(|_| ValidationIssue::Schema)?; + Ok(read) + } +} + +fn file_digest( + file: &File, + stamp: FixedFileStamp, + cancel: &dyn Fn() -> bool, +) -> Result<[u8; 32], LegacyIssue> { + let source = FileSource { + file, + len: stamp.len(), + }; + let mut buffer = [0; 4096]; + let mut at = 0; + let mut hash = Sha256::new(); + while at < source.len() { + if cancel() { + return Err(LegacyIssue::Cancelled); + } + let n = ((source.len() - at) as usize).min(buffer.len()); + let got = source.read_at(at, &mut buffer[..n])?; + if got == 0 { + return Err(LegacyIssue::Changed); + } + hash.update(&buffer[..got]); + at += got as u64; + } + stamp.recheck(file).map_err(fixed)?; + Ok(hash.finalize().into()) +} + +#[derive(Clone, Copy)] +enum WitnessKey { + Cache { + key: [u8; 32], + stamp: FixedFileStamp, + span: Span, + }, + Native { + locator: NativeStringLocator, + span: Span, + json: bool, + }, +} +struct Witnesses<'a> { + database: &'a File, + db_stamp: FixedFileStamp, + manifest: &'a File, + manifest_stamp: FixedFileStamp, + root: &'a RootedDirectory, + backup: Option, + certificate: [u8; 32], + cache_count: u64, + certificates: &'a dyn ReplayLegacyCertificates, + cancel: &'a dyn Fn() -> bool, +} +impl Witnesses<'_> { + fn with_key( + &self, + key: WitnessKey, + memory: &CleanupMemory, + f: impl FnOnce(&dyn ByteSource, Span, KeyEncoding) -> Result, + ) -> Result { + if (self.cancel)() { + return Err(ValidationIssue::Schema); + } + match key { + WitnessKey::Cache { key, stamp, span } => { + let _floor = memory.reserve(2048)?; + let source = CacheKeySource { + root: self.root, + key, + backup: self.backup, + stamp, + }; + f(&source, span, KeyEncoding::JsonDecoded) + } + WitnessKey::Native { + locator, + span, + json, + } => { + self.db_stamp + .recheck(self.database) + .map_err(|_| ValidationIssue::Schema)?; + let source = FileSource { + file: self.database, + len: self.db_stamp.len(), + }; + let logical = NativeStringSource::new(&source, locator, memory) + .map_err(|_| ValidationIssue::Schema)?; + let value = f( + &logical, + span, + if json { + KeyEncoding::JsonDecoded + } else { + KeyEncoding::NativeUtf8 + }, + )?; + self.db_stamp + .recheck(self.database) + .map_err(|_| ValidationIssue::Schema)?; + Ok(value) + } + } + } + fn compare_keys( + &self, + a: WitnessKey, + b: WitnessKey, + memory: &CleanupMemory, + ) -> Result { + self.with_key(a, memory, |left, ls, le| { + self.with_key(b, memory, |right, rs, re| match (le, re) { + (KeyEncoding::JsonDecoded, KeyEncoding::JsonDecoded) => { + validation::compare_decoded_spans(left, ls, right, rs, memory) + } + (KeyEncoding::NativeUtf8, KeyEncoding::JsonDecoded) => { + validation::compare_native_json(left, ls, right, rs, memory) + } + (KeyEncoding::JsonDecoded, KeyEncoding::NativeUtf8) => { + validation::compare_native_json(right, rs, left, ls, memory) + .map(Ordering::reverse) + } + _ => { + validation::key_stream_hash(left, ls, le, 0, memory)?; + validation::key_stream_hash(right, rs, re, 0, memory)?; + let mut ordering = Ordering::Equal; + let mut l = [0; 256]; + let mut r = [0; 256]; + let mut at = 0; + let llen = ls.end - ls.start; + let rlen = rs.end - rs.start; + while at < llen.max(rlen) { + let ln = ((llen.saturating_sub(at)).min(l.len() as u64)) as usize; + let rn = ((rlen.saturating_sub(at)).min(r.len() as u64)) as usize; + if left.read_at(ls.start + at, &mut l[..ln])? != ln + || right.read_at(rs.start + at, &mut r[..rn])? != rn + { + return Err(ValidationIssue::InvalidSpan); + } + if ordering == Ordering::Equal { + ordering = l[..ln].cmp(&r[..rn]); + } + at += l.len() as u64; + } + Ok(ordering) + } + }) + }) + } +} +fn id_bytes( + source: &dyn ByteSource, + key: Span, + encoding: KeyEncoding, + prefix: &[u8], + memory: &CleanupMemory, +) -> Result<([u8; 64], usize), LegacyIssue> { + let hash = validation::key_sha1_prefix20(source, key, encoding, memory)?; + let mut out = [0; 64]; + if prefix.len() + 21 > out.len() { + return Err(LegacyIssue::Unknown); + } + out[..prefix.len()].copy_from_slice(prefix); + out[prefix.len()] = b':'; + let digits = b"0123456789abcdef"; + for (i, b) in hash[..10].iter().enumerate() { + out[prefix.len() + 1 + 2 * i] = digits[(b >> 4) as usize]; + out[prefix.len() + 2 + 2 * i] = digits[(b & 15) as usize]; + } + Ok((out, prefix.len() + 21)) +} +impl Scanner<'_> { + fn metadata_key(&mut self, t: JsonToken) -> Result { + if t.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + let mut canonical = None; + loop { + let k = self.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let field = self.field(k, &["canonical_key"])?; + let v = self.token()?; + if field.is_some() { + if canonical.is_some() { + return Err(LegacyIssue::Unknown); + } + canonical = Some(self.string(v)?); + } else { + self.skip(v)?; + } + } + canonical.ok_or(LegacyIssue::Unknown) + } + // Projection keeps exact source spans/stamps and accounted validation callbacks as independent inputs. + #[allow(clippy::too_many_arguments)] + fn selected_row( + &mut self, + t: JsonToken, + edge: bool, + record: ManifestRecord, + stamp: FixedFileStamp, + repository: &[u8], + label: &[u8], + ordinal: u64, + emit: &mut dyn FnMut(WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), LegacyIssue> { + if t.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + let mut fields = [None; 8]; + loop { + let k = self.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let f = self.field( + k, + &[ + "id", + if edge { "edge_type" } else { "table" }, + "label", + "kind", + "path", + "source_id", + "target_id", + "metadata", + ], + )?; + let v = self.token()?; + if let Some(i) = f { + if fields[i].is_some() { + return Err(LegacyIssue::Unknown); + } + fields[i] = Some(if i == 7 { + self.metadata_key(v)? + } else { + self.string(v)? + }); + } else { + self.skip(v)?; + } + } + let get = |i: usize| fields[i].ok_or(LegacyIssue::Unknown); + let table = get(1)?; + let domain = if json_literal(self.source, table, b"Repository", self.memory)? { + 1 + } else if json_literal(self.source, table, b"SourceRoot", self.memory)? { + 2 + } else if json_literal(self.source, table, b"File", self.memory)? { + 3 + } else if edge && json_literal(self.source, table, b"Contains", self.memory)? { + 4 + } else { + return Ok(()); + }; + let id = get(0)?; + let kind = get(3)?; + let canonical = get(7)?; + let prefix = match domain { + 1 => b"Repository".as_slice(), + 2 => b"SourceRoot", + 3 => b"File", + 4 => b"edge", + _ => unreachable!(), + }; + let (expected_id, id_len) = id_bytes( + self.source, + canonical, + KeyEncoding::JsonDecoded, + prefix, + self.memory, + )?; + if !json_literal(self.source, id, &expected_id[..id_len], self.memory)? { + return Err(LegacyIssue::Unknown); + } + let projected = match domain { + 1 => { + if edge + || !json_literal(self.source, kind, b"repository", self.memory)? + || !json_literal(self.source, get(4)?, b"", self.memory)? + || !json_literal(self.source, canonical, label, self.memory)? + || !same_json(self.source, get(2)?, canonical, self.memory)? + { + return Err(LegacyIssue::Unknown); + } + [id, get(2)?, kind, get(4)?, canonical] + } + 2 => { + if edge + || !json_literal(self.source, kind, b"sourceroot", self.memory)? + || !json_literal(self.source, canonical, repository, self.memory)? + || !same_json(self.source, get(2)?, canonical, self.memory)? + || !same_json(self.source, get(4)?, canonical, self.memory)? + { + return Err(LegacyIssue::Unknown); + } + [id, get(2)?, kind, get(4)?, canonical] + } + 3 => { + if edge + || !json_literal(self.source, kind, b"file", self.memory)? + || !same_json(self.source, get(4)?, record.path, self.memory)? + || !same_json(self.source, canonical, record.path, self.memory)? + { + return Err(LegacyIssue::Unknown); + } + [id, kind, get(4)?, record.hash, canonical] + } + 4 => { + if !edge { + return Err(LegacyIssue::Unknown); + } + let role = + if json_literal(self.source, kind, b"repository_source_root", self.memory)? { + 0 + } else if json_literal(self.source, kind, b"source_root_file", self.memory)? { + 1 + } else { + return Ok(()); + }; + let root_source = Slice(repository); + let label_source = Slice(label); + let (root_id, root_len) = id_bytes( + &root_source, + Span { + start: 0, + end: repository.len() as u64, + }, + KeyEncoding::NativeUtf8, + b"SourceRoot", + self.memory, + )?; + let (repo_id, repo_len) = id_bytes( + &label_source, + Span { + start: 0, + end: label.len() as u64, + }, + KeyEncoding::NativeUtf8, + b"Repository", + self.memory, + )?; + let (file_id, file_len) = id_bytes( + self.source, + record.path, + KeyEncoding::JsonDecoded, + b"File", + self.memory, + )?; + let (source_id, target_id) = if role == 0 { + (&repo_id[..repo_len], &root_id[..root_len]) + } else { + (&root_id[..root_len], &file_id[..file_len]) + }; + if !json_literal(self.source, get(5)?, source_id, self.memory)? + || !json_literal(self.source, get(6)?, target_id, self.memory)? + { + return Err(LegacyIssue::Unknown); + } + let role = if role == 0 { + b"repository_source_root".as_slice() + } else { + b"source_root_file" + }; + let mut expected = [0; 256]; + let mut len = 0; + for part in [ + b"Contains|".as_slice(), + source_id, + b"|", + target_id, + b"|", + role, + ] { + let end = len + part.len(); + if end > expected.len() { + return Err(LegacyIssue::Unknown); + } + expected[len..end].copy_from_slice(part); + len = end; + } + if !json_literal(self.source, canonical, &expected[..len], self.memory)? { + return Err(LegacyIssue::Unknown); + } + [id, kind, get(5)?, get(6)?, canonical] + } + _ => unreachable!(), + }; + emit(WitnessEvent { + domain, + native: false, + ordinal, + fields: projected.map(|span| { + Some(WitnessKey::Cache { + key: record.key, + stamp, + span, + }) + }), + })?; + Ok(()) + } + // Projection keeps exact source spans/stamps and accounted validation callbacks as independent inputs. + #[allow(clippy::too_many_arguments)] + fn selected_array( + &mut self, + t: JsonToken, + edge: bool, + record: ManifestRecord, + stamp: FixedFileStamp, + repository: &[u8], + label: &[u8], + ordinal: &mut u64, + emit: &mut dyn FnMut(WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), LegacyIssue> { + if t.kind != TokenKind::ArrayStart { + return Err(LegacyIssue::Unknown); + } + loop { + let t = self.token()?; + if t.kind == TokenKind::ArrayEnd { + break; + } + self.selected_row(t, edge, record, stamp, repository, label, *ordinal, emit)?; + *ordinal = ordinal.checked_add(1).ok_or(LegacyIssue::Unknown)?; + } + Ok(()) + } + fn cache_entry(&mut self, t: JsonToken) -> Result { + if t.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + let mut fields = [None; 5]; + let mut key = None; + let mut nodes = None; + let mut edges = None; + loop { + let k = self.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let f = self.field( + k, + &[ + "path", + "content_hash", + "language", + "partition_id", + "artifact_key", + "node_count", + "edge_count", + ], + )?; + let v = self.token()?; + match f { + Some(i @ 0..=3) => { + if fields[i].is_some() { + return Err(LegacyIssue::Unknown); + } + fields[i] = Some(self.string(v)?); + } + Some(4) => { + if key.is_some() { + return Err(LegacyIssue::Unknown); + } + key = Some(self.key(v)?); + } + Some(5) => nodes = Some(self.uint(v)?), + Some(6) => edges = Some(self.uint(v)?), + _ => { + self.skip(v)?; + } + } + } + Ok(ManifestRecord { + path: fields[0].ok_or(LegacyIssue::Unknown)?, + hash: fields[1].ok_or(LegacyIssue::Unknown)?, + language: fields[2].ok_or(LegacyIssue::Unknown)?, + partition: fields[3].ok_or(LegacyIssue::Unknown)?, + key: key.ok_or(LegacyIssue::Unknown)?, + nodes: nodes.ok_or(LegacyIssue::Unknown)?, + edges: edges.ok_or(LegacyIssue::Unknown)?, + }) + } +} + +// Projection keeps exact source spans/stamps and accounted validation callbacks as independent inputs. +#[allow(clippy::too_many_arguments)] +fn project_cache( + file: &File, + stamp: FixedFileStamp, + key: [u8; 32], + manifest: &FileSource<'_>, + expected: ManifestRecord, + repository: &[u8], + label: &[u8], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + ordinal: &mut u64, + emit: &mut dyn FnMut(WitnessEvent) -> Result<(), ValidationIssue>, +) -> Result<[u8; 32], LegacyIssue> { + let source = FileSource { + file, + len: stamp.len(), + }; + let mut scanner = Scanner::new( + file, + &source, + Span { + start: 0, + end: source.len(), + }, + memory, + cancel, + )?; + let mut partition = false; + if scanner.token()?.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + loop { + let k = scanner.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let f = scanner.field(k, &["partition"])?; + let v = scanner.token()?; + if f.is_some() { + if partition || v.kind != TokenKind::ObjectStart { + return Err(LegacyIssue::Unknown); + } + partition = true; + let mut entry = None; + let mut seen = 0u8; + loop { + let k = scanner.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let f = scanner.field(k, &["entry", "nodes", "edges"])?; + let v = scanner.token()?; + if let Some(i) = f { + if seen & (1 << i) != 0 { + return Err(LegacyIssue::Unknown); + } + seen |= 1 << i; + match i { + 0 => { + let record = scanner.cache_entry(v)?; + if record.key != key + || record.nodes != expected.nodes + || record.edges != expected.edges + { + return Err(LegacyIssue::Unknown); + } + for (a, b) in [ + (record.path, expected.path), + (record.hash, expected.hash), + (record.language, expected.language), + (record.partition, expected.partition), + ] { + if validation::compare_decoded_spans( + &source, a, manifest, b, memory, + )? != Ordering::Equal + { + return Err(LegacyIssue::Unknown); + } + } + entry = Some(record); + } + 1 | 2 => scanner.selected_array( + v, + i == 2, + entry.ok_or(LegacyIssue::Unknown)?, + stamp, + repository, + label, + ordinal, + emit, + )?, + _ => unreachable!(), + } + } else { + return Err(LegacyIssue::Unknown); + } + } + if seen != 7 { + return Err(LegacyIssue::Unknown); + } + } else { + scanner.skip(v)?; + } + } + if !partition { + return Err(LegacyIssue::Unknown); + } + let digest = scanner.reader.finish()?.0; + stamp.recheck(file).map_err(fixed)?; + Ok(digest) +} +impl ReplayWitnesses for Witnesses<'_> { + type Key = WitnessKey; + fn replay( + &self, + memory: &CleanupMemory, + emit: &mut dyn FnMut(WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue> { + let _floor = memory.reserve(std::mem::size_of::() + 16384)?; + self.db_stamp + .recheck(self.database) + .map_err(|_| ValidationIssue::Schema)?; + self.manifest_stamp + .recheck(self.manifest) + .map_err(|_| ValidationIssue::Schema)?; + let source = FileSource { + file: self.database, + len: self.db_stamp.len(), + }; + let mut failure = None; + native::scan_native_selected43( + &source, + memory, + &mut || { + if (self.cancel)() { + Err(NativeUnknown::Cancelled) + } else { + Ok(()) + } + }, + &mut |row| { + let domain = match row.table { + NativeSelectedTable::Repository => 1, + NativeSelectedTable::SourceRoot => 2, + NativeSelectedTable::File => 3, + NativeSelectedTable::Contains => 4, + }; + let mut fields = [None; 5]; + for (i, f) in row.fields.iter().enumerate() { + let Some(locator) = f else { + return Err(NativeUnknown::UnsupportedLayout); + }; + fields[i] = Some(WitnessKey::Native { + locator: *locator, + span: if i == 4 { + row.canonical_key.ok_or(NativeUnknown::UnsupportedLayout)? + } else { + Span { + start: 0, + end: locator.length, + } + }, + json: i == 4, + }); + } + if let Err(e) = emit(WitnessEvent { + domain, + native: true, + ordinal: row.ordinal, + fields, + }) { + failure = Some(e); + return Err(NativeUnknown::UnsupportedLayout); + } + Ok(()) + }, + ) + .map_err(|_| failure.unwrap_or(ValidationIssue::Schema))?; + let mut chain = [0u8; 32]; + let mut count = 0u64; + let mut ordinal_offset = 0u64; + self.certificates + .replay(memory, &mut |certificate| { + if (self.cancel)() { + return Err(LegacyIssue::Cancelled); + } + let file = open_cache(self.root, certificate.key, self.backup)?; + certificate.stamp.recheck(&file).map_err(fixed)?; + drop(file); + for fact in certificate.facts { + emit(WitnessEvent { + domain: fact.domain, + native: false, + ordinal: ordinal_offset + .checked_add(fact.ordinal) + .ok_or(ValidationIssue::Overflow)?, + fields: fact.fields.map(|span| { + Some(WitnessKey::Cache { + key: certificate.key, + stamp: certificate.stamp, + span, + }) + }), + })?; + } + ordinal_offset = ordinal_offset + .checked_add(certificate.rows) + .ok_or(LegacyIssue::Unknown)?; + chain_certificate(&mut chain, &certificate)?; + count = count.checked_add(1).ok_or(LegacyIssue::Unknown)?; + let file = open_cache(self.root, certificate.key, self.backup)?; + certificate.stamp.recheck(&file).map_err(fixed)?; + Ok(()) + }) + .map_err(validation_error)?; + if chain != self.certificate || count != self.cache_count { + return Err(ValidationIssue::Schema); + } + self.db_stamp + .recheck(self.database) + .map_err(|_| ValidationIssue::Schema)?; + self.manifest_stamp + .recheck(self.manifest) + .map_err(|_| ValidationIssue::Schema)?; + Ok(()) + } + fn hash( + &self, + key: Self::Key, + domain: u64, + memory: &CleanupMemory, + ) -> Result<[u8; 32], ValidationIssue> { + self.with_key(key, memory, |s, p, e| { + validation::key_stream_hash(s, p, e, domain, memory) + }) + } + fn compare( + &self, + left: Self::Key, + right: Self::Key, + memory: &CleanupMemory, + ) -> Result { + self.compare_keys(left, right, memory) + } + fn empty(&self, key: Self::Key, memory: &CleanupMemory) -> Result { + self.with_key(key, memory, |source, span, encoding| { + validation::key_stream_hash(source, span, encoding, 0, memory)?; + Ok(span.end - span.start + == match encoding { + KeyEncoding::NativeUtf8 => 0, + KeyEncoding::JsonDecoded => 2, + }) + }) + } +} + +/// Exact prepared facts for the selected historical profile. Cache certificate +/// covers only the manifest's required payloads; it does NOT adopt unreferenced +/// or unknown children of the cache root. The caller retains those separately. +pub(crate) struct LegacyAssociation { + database: FixedFileStamp, + manifest: FixedFileStamp, + artifact_root: FileIdentity, + backup: Option, + backup_loader_usable: Option, + expected: ReferenceEpoch, + cache_certificate: [u8; 32], + #[cfg(test)] + cache_count: u64, + #[cfg(test)] + cache_loader_usable: bool, + _floor: MemoryReservation, +} +impl LegacyAssociation { + pub(crate) fn source_epoch(&self) -> &ReferenceEpoch { + &self.expected + } + pub(crate) fn artifact_root_identity(&self) -> FileIdentity { + self.artifact_root + } + pub(crate) fn database_stamp(&self) -> FixedFileStamp { + self.database + } + pub(crate) fn manifest_stamp(&self) -> FixedFileStamp { + self.manifest + } + #[cfg(test)] + pub(crate) fn cache_count(&self) -> u64 { + self.cache_count + } + #[cfg(test)] + pub(crate) fn cache_loader_usable(&self) -> bool { + self.cache_loader_usable + } + pub(crate) fn cache_certificate(&self) -> [u8; 32] { + self.cache_certificate + } + pub(crate) fn matches( + &self, + database: &File, + manifest: &File, + artifact_root: &RootedDirectory, + ) -> bool { + self.database.recheck(database).is_ok() + && self.manifest.recheck(manifest).is_ok() + && artifact_root.identity() == self.artifact_root + } +} +pub(crate) enum LegacyProgress { + Pending, + CacheValidated(LegacyCacheCertificate), + Complete(LegacyAssociation), +} +#[derive(Clone, Copy, PartialEq, Eq)] +enum Stage { + Catalog, + Caches, + Join, + Done, +} +/// One job stays on its joined preparation thread: RootedDirectory is an +/// actual readonly anchor and intentionally !Send. No authority is retained. +pub(crate) struct LegacyAssociationPreparation { + database: File, + manifest: File, + artifact_root: RootedDirectory, + backup: Option, + artifact_root_path: PathBuf, + repository: PathBuf, + repository_identity: FileIdentity, + expected: ReferenceEpoch, + database_stamp: FixedFileStamp, + manifest_stamp: FixedFileStamp, + files: Span, + cursor: Option, + stage: Stage, + database_sha256: [u8; 32], + manifest_sha256: [u8; 32], + certificate: [u8; 32], + pending: Option, + resume_validation: bool, + closed_source_authorized: bool, + cache_count: u64, + cache_loader_usable: bool, + _floor: MemoryReservation, +} +impl LegacyAssociationPreparation { + pub(crate) fn new( + database: File, + manifest: File, + artifact_root: RootedDirectory, + artifact_root_path: &Path, + repository_root: &Path, + source_epoch: ReferenceEpoch, + memory: &CleanupMemory, + ) -> Result { + let charge = std::mem::size_of::() + .checked_add(repository_root.as_os_str().as_encoded_bytes().len()) + .and_then(|n| n.checked_add(artifact_root_path.as_os_str().as_encoded_bytes().len())) + .and_then(|n| n.checked_add(32768)) + .ok_or(ResourceLimit::ArithmeticOverflow)?; + let floor = memory.reserve(charge)?; + if !repository_root.is_absolute() + || repository_root.to_str().is_none() + || repository_root + .file_name() + .and_then(|n| n.to_str()) + .is_none() + { + return Err(LegacyIssue::Unknown); + } + let mut scratch = super::locks::FixedControlScratch::new( + memory.reserve(super::locks::FixedControlScratch::required_bytes())?, + ) + .map_err(fixed)?; + let repository_file = super::locks::open_fixed_control( + repository_root, + FixedControlKind::Directory, + false, + &mut scratch, + ) + .map_err(fixed)?; + let repository_identity = + FixedFileStamp::capture(&repository_file, FixedControlKind::Directory) + .map_err(fixed)? + .identity(); + let root_file = super::locks::open_fixed_control( + artifact_root_path, + FixedControlKind::Directory, + false, + &mut scratch, + ) + .map_err(fixed)?; + if FixedFileStamp::capture(&root_file, FixedControlKind::Directory) + .map_err(fixed)? + .identity() + != artifact_root.identity() + { + return Err(LegacyIssue::Changed); + } + let database_stamp = + FixedFileStamp::capture(&database, FixedControlKind::File).map_err(fixed)?; + let manifest_stamp = + FixedFileStamp::capture(&manifest, FixedControlKind::File).map_err(fixed)?; + Ok(Self { + database, + manifest, + artifact_root, + backup: None, + artifact_root_path: artifact_root_path.to_owned(), + repository: repository_root.to_owned(), + repository_identity, + expected: source_epoch, + database_stamp, + manifest_stamp, + files: Span::default(), + cursor: None, + stage: Stage::Catalog, + database_sha256: [0; 32], + manifest_sha256: [0; 32], + certificate: [0; 32], + pending: None, + resume_validation: false, + closed_source_authorized: false, + cache_count: 0, + cache_loader_usable: true, + _floor: floor, + }) + } + fn current( + &self, + domain: &ReferenceDomain, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + ) -> Result<(), LegacyIssue> { + if cancel() { + return Err(LegacyIssue::Cancelled); + } + self.database_stamp.recheck(&self.database).map_err(fixed)?; + self.manifest_stamp.recheck(&self.manifest).map_err(fixed)?; + let mut scratch = super::locks::FixedControlScratch::new( + memory.reserve(super::locks::FixedControlScratch::required_bytes())?, + ) + .map_err(fixed)?; + for (path, expected) in [ + (&self.artifact_root_path, self.artifact_root.identity()), + (&self.repository, self.repository_identity), + ] { + let file = super::locks::open_fixed_control( + path, + FixedControlKind::Directory, + false, + &mut scratch, + ) + .map_err(fixed)?; + if FixedFileStamp::capture(&file, FixedControlKind::Directory) + .map_err(fixed)? + .identity() + != expected + { + return Err(LegacyIssue::Changed); + } + } + if !self.closed_source_authorized + && !super::maintenance_ownership::expected_reference_current( + domain, + &self.expected, + memory, + ) + .map_err(fixed)? + { + return Err(LegacyIssue::Changed); + } + Ok(()) + } + #[cfg(test)] + pub(crate) fn advance( + &mut self, + domain: &ReferenceDomain, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + ) -> Result { + let result = self.advance_inner(domain, memory, cancel, &NoCertificates); + if result.is_err() { + self.stage = Stage::Done; + } + result + } + pub(crate) fn advance_with_certificates( + &mut self, + domain: &ReferenceDomain, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + certificates: &dyn ReplayLegacyCertificates, + ) -> Result { + let result = self.advance_inner(domain, memory, cancel, certificates); + if result.is_err() { + self.stage = Stage::Done; + } + result + } + pub(crate) fn acknowledge_cache( + &mut self, + certificate_digest: [u8; 32], + ) -> Result<(), LegacyIssue> { + let certificate = self.pending.ok_or(LegacyIssue::Unknown)?; + if certificate.digest()? != certificate_digest { + return Err(LegacyIssue::Changed); + } + chain_certificate(&mut self.certificate, &certificate)?; + self.cache_count = self + .cache_count + .checked_add(1) + .ok_or(LegacyIssue::Unknown)?; + self.cache_loader_usable &= certificate.loader_usable; + self.pending = None; + Ok(()) + } + fn advance_inner( + &mut self, + domain: &ReferenceDomain, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + certificates: &dyn ReplayLegacyCertificates, + ) -> Result { + let _floor = memory.reserve(32768)?; + self.current(domain, memory, cancel)?; + if self.resume_validation { + let source = FileSource { + file: &self.database, + len: self.database_stamp.len(), + }; + recognize_catalog(&source, memory, cancel)?; + if file_digest(&self.database, self.database_stamp, cancel)? != self.database_sha256 + || file_digest(&self.manifest, self.manifest_stamp, cancel)? != self.manifest_sha256 + || project_manifest(&self.manifest, memory, cancel)? != self.files + { + return Err(LegacyIssue::Changed); + } + self.current(domain, memory, cancel)?; + self.resume_validation = false; + return Ok(LegacyProgress::Pending); + } + if let Some(certificate) = self.pending { + return Ok(LegacyProgress::CacheValidated(certificate)); + } + match self.stage { + Stage::Catalog => { + self.files = project_manifest(&self.manifest, memory, cancel)?; + self.manifest_sha256 = file_digest(&self.manifest, self.manifest_stamp, cancel)?; + self.cursor = Some(ManifestCursor::new(&self.manifest, self.files, memory)?); + let source = FileSource { + file: &self.database, + len: self.database_stamp.len(), + }; + recognize_catalog(&source, memory, cancel)?; + self.database_sha256 = file_digest(&self.database, self.database_stamp, cancel)?; + self.current(domain, memory, cancel)?; + self.stage = Stage::Caches; + Ok(LegacyProgress::Pending) + } + Stage::Caches => { + let Some(record) = self.cursor.as_mut().ok_or(LegacyIssue::Unknown)?.next( + &self.manifest, + memory, + cancel, + )? + else { + if self.cache_count == 0 { + return Err(LegacyIssue::Unknown); + } + self.stage = Stage::Join; + return Ok(LegacyProgress::Pending); + }; + let file = open_cache(&self.artifact_root, record.key, self.backup)?; + let stamp = + FixedFileStamp::capture(&file, FixedControlKind::File).map_err(fixed)?; + let source = FileSource { + file: &file, + len: stamp.len(), + }; + let facts = validation::scan_artifact(&source, memory, &mut |_| { + if cancel() { + Err(ValidationIssue::Schema) + } else { + Ok(()) + } + })?; + if facts.key != record.key + || !facts.producer_tokens + || !validation::wire_checksum_matches(&source, &facts, memory)? + { + return Err(LegacyIssue::Unknown); + } + validation::join_raw_rows( + &validation::ArtifactEvents { source: &source }, + memory, + 256, + )?; + let loader_usable = + facts.typed_numbers && facts.stable_numbers && facts.metadata_keys_sorted; + let manifest = FileSource { + file: &self.manifest, + len: self.manifest_stamp.len(), + }; + let mut selected = [LegacyProjectedFact { + domain: 0, + ordinal: 0, + fields: [Span::default(); 5], + }; 5]; + let mut count = 0usize; + let mut ordinal = 0; + let digest = project_cache( + &file, + stamp, + record.key, + &manifest, + record, + self.repository.as_os_str().as_encoded_bytes(), + self.repository.file_name().unwrap().as_encoded_bytes(), + memory, + cancel, + &mut ordinal, + &mut |event| { + if count >= selected.len() { + return Err(ValidationIssue::Schema); + } + let mut fields = [Span::default(); 5]; + for (i, key) in event.fields.iter().enumerate() { + match key { + Some(WitnessKey::Cache { + key, + stamp: s, + span, + }) if *key == record.key && *s == stamp => fields[i] = *span, + _ => return Err(ValidationIssue::Schema), + } + } + selected[count] = LegacyProjectedFact { + domain: event.domain, + ordinal: event.ordinal, + fields, + }; + count += 1; + Ok(()) + }, + )?; + if count != 5 { + return Err(LegacyIssue::Unknown); + } + let mut domains = [0u8; 4]; + for fact in selected { + let i = + usize::try_from(fact.domain.checked_sub(1).ok_or(LegacyIssue::Unknown)?) + .map_err(|_| LegacyIssue::Unknown)?; + if i >= domains.len() { + return Err(LegacyIssue::Unknown); + } + domains[i] += 1; + } + if domains != [1, 1, 1, 2] { + return Err(LegacyIssue::Unknown); + } + // Recheck the exact current name without retaining a fourth + // payload descriptor beside DB, manifest and this old handle. + drop(file); + let current = open_cache(&self.artifact_root, record.key, self.backup)?; + stamp.recheck(¤t).map_err(fixed)?; + self.current(domain, memory, cancel)?; + let certificate = LegacyCacheCertificate { + key: record.key, + stamp, + digest, + facts: selected, + rows: facts + .actual_nodes + .checked_add(facts.actual_edges) + .ok_or(LegacyIssue::Unknown)?, + loader_usable, + }; + self.pending = Some(certificate); + Ok(LegacyProgress::CacheValidated(certificate)) + } + Stage::Join => { + let mut backup_loader_usable = None; + if let Some(backup) = self.backup { + certificates.replay(memory, &mut |certificate| { + if certificate.key == backup.key { + if certificate.stamp != backup.stamp || backup_loader_usable.is_some() { + return Err(LegacyIssue::Unknown); + } + backup_loader_usable = Some(certificate.loader_usable); + } + Ok(()) + })?; + if backup_loader_usable.is_none() { + return Err(LegacyIssue::Unknown); + } + } + let cert = self.certificate; + let replay = Witnesses { + database: &self.database, + db_stamp: self.database_stamp, + manifest: &self.manifest, + manifest_stamp: self.manifest_stamp, + root: &self.artifact_root, + backup: self.backup, + certificate: cert, + cache_count: self.cache_count, + certificates, + cancel, + }; + validation::join_selected_witnesses(&replay, memory, 256)?; + self.current(domain, memory, cancel)?; + self.stage = Stage::Done; + Ok(LegacyProgress::Complete(LegacyAssociation { + database: self.database_stamp, + manifest: self.manifest_stamp, + artifact_root: self.artifact_root.identity(), + backup: self.backup, + backup_loader_usable, + expected: self.expected.clone(), + + cache_certificate: cert, + #[cfg(test)] + cache_count: self.cache_count, + #[cfg(test)] + cache_loader_usable: self.cache_loader_usable, + + _floor: memory.reserve(std::mem::size_of::())?, + })) + } + Stage::Done => Err(LegacyIssue::Unknown), + } + } +} + +#[cfg(test)] +mod tests { + use super::super::layout::{create_fresh_reference_root, managed_generation_id, ManagedLayout}; + use super::super::locks::{open_locked, path_identity, LockMode}; + use super::super::maintenance_ownership::{ + initialize_fresh_reference_domain, observe_reference_epoch, EpochObservation, + ReferenceMutation, + }; + use super::*; + use crate::protocol::{NativeManifest, NativeSyntaxMaterializationRequest}; + #[derive(Default)] + struct Certificates(std::cell::RefCell>); + impl ReplayLegacyCertificates for Certificates { + fn replay( + &self, + _: &CleanupMemory, + emit: &mut dyn FnMut(LegacyCacheCertificate) -> Result<(), LegacyIssue>, + ) -> Result<(), LegacyIssue> { + for bytes in self.0.borrow().iter() { + emit(LegacyCacheCertificate::decode(bytes)?)?; + } + Ok(()) + } + } + fn persist_certificate( + job: &mut LegacyAssociationPreparation, + certificates: &Certificates, + certificate: LegacyCacheCertificate, + ) -> Result<(), LegacyIssue> { + let mut bytes = [0; LEGACY_CERTIFICATE_BYTES]; + certificate.encode(&mut bytes); + let certificate = LegacyCacheCertificate::decode(&bytes)?; + let checkpoint = job.checkpoint_after_cache()?; + let mut checkpoint_bytes = [0; LEGACY_CHECKPOINT_BYTES]; + checkpoint.encode(&mut checkpoint_bytes); + let _ = LegacyCheckpoint::decode(&checkpoint_bytes)?; + certificates.0.borrow_mut().push(bytes); + job.acknowledge_cache(certificate.digest()?) + } + struct Fixture { + root: PathBuf, + layout: ManagedLayout, + domain: ReferenceDomain, + database: PathBuf, + manifest: PathBuf, + } + impl Fixture { + fn new() -> Self { + let root = std::env::temp_dir() + .join(format!("legacy-association-{}", managed_generation_id())); + std::fs::create_dir(&root).unwrap(); + let root = std::fs::canonicalize(root).unwrap(); + std::fs::create_dir(root.join("src")).unwrap(); + std::fs::write(root.join("src/a.rs"), b"pub fn alpha(v:i64)->i64{v+1}\n").unwrap(); + std::fs::write(root.join("src/b.rs"), b"pub struct Beta{pub value:i64}\n").unwrap(); + let layout = ManagedLayout::new(root.join("storage")); + let fresh = create_fresh_reference_root(layout.storage_root()) + .unwrap() + .unwrap(); + layout.ensure_roots().unwrap(); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + drop(writer); + let database = root.join("graph.ldb"); + let manifest = root.join("manifest.json"); + let mutation = ReferenceMutation::begin_in_domain(&domain).unwrap(); + let request:NativeSyntaxMaterializationRequest=serde_json::from_value(serde_json::json!({"source_root":root,"repository_label":root.file_name().unwrap().to_str().unwrap(),"mode":"full","parser_version":"legacy-association-test","manifest_schema_version":5,"ontology":"code_ontology_v1","profiles":[],"excluded_parts":[],"artifact_root":layout.artifacts_root(),"db_path":database,"include_fts":false,"staging_dir":root.join("native-staging"),"parallel":false,"spill_chunk_mib":1,"max_parallelism":1,"atomic_rebuild":false})).unwrap(); + let response = crate::execute_materialization_pipeline(&request).unwrap(); + let files = response + .materialized_entries + .into_iter() + .map(|(path, entry)| (path, entry.compact())) + .collect(); + let value = NativeManifest { + schema_version: 5, + ontology: request.ontology, + parser_version: request.parser_version, + graph_build_digest: response.graph_build_digest, + search_backend: response.search_backend, + files, + }; + std::fs::write(&manifest, serde_json::to_vec(&value).unwrap()).unwrap(); + drop(mutation); + Self { + root, + layout, + domain, + database, + manifest, + } + } + fn epoch(&self) -> ReferenceEpoch { + match observe_reference_epoch(&self.domain).unwrap() { + EpochObservation::Verified(e) => e, + e => panic!("{e:?}"), + } + } + fn job(&self, memory: &CleanupMemory) -> Result { + let root = RootedDirectory::open_readonly_root( + &self.layout.artifacts_root(), + path_identity(&self.layout.artifacts_root()).unwrap(), + ) + .unwrap(); + LegacyAssociationPreparation::new( + File::open(&self.database).unwrap(), + File::open(&self.manifest).unwrap(), + root, + &self.layout.artifacts_root(), + &self.root, + self.epoch(), + memory, + ) + } + fn complete(&self, memory: &CleanupMemory) -> Result { + let mut job = self.job(memory)?; + let certificates = Certificates::default(); + for _ in 0..16 { + match job.advance_with_certificates( + &self.domain, + memory, + &|| false, + &certificates, + )? { + LegacyProgress::Complete(f) => return Ok(f), + LegacyProgress::Pending => {} + LegacyProgress::CacheValidated(certificate) => { + persist_certificate(&mut job, &certificates, certificate)? + } + } + } + panic!("bounded fixture did not complete") + } + fn first_cache(&self) -> PathBuf { + let manifest: NativeManifest = + serde_json::from_slice(&std::fs::read(&self.manifest).unwrap()).unwrap(); + let key = manifest + .files + .values() + .next() + .unwrap() + .artifact_key + .as_ref() + .unwrap(); + self.layout + .artifacts_root() + .join(&key[..2]) + .join(key) + .join("partition.json") + } + } + impl Drop for Fixture { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.root); + } + } + #[cfg(any(target_os = "linux", target_os = "macos"))] + #[test] + fn dc_legacy_actual_native_association_restart_and_backup_keep_three_payload_fds() { + use std::os::unix::fs::MetadataExt; + let f = Fixture::new(); + let manifest: NativeManifest = + serde_json::from_slice(&std::fs::read(&f.manifest).unwrap()).unwrap(); + let selected_key = manifest + .files + .values() + .next() + .unwrap() + .artifact_key + .as_ref() + .unwrap(); + let canonical = f + .layout + .artifacts_root() + .join(&selected_key[..2]) + .join(selected_key); + let backup_name = format!(".replaced-{selected_key}-1"); + let backup = canonical.parent().unwrap().join(&backup_name); + let mutation = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + std::fs::rename(&canonical, &backup).unwrap(); + drop(mutation); + let address = + super::super::maintenance_backup::BackupAddress::parse(backup_name.as_bytes()).unwrap(); + let mut paths = vec![f.database.clone(), f.manifest.clone()]; + for entry in manifest.files.values() { + let key = entry.artifact_key.as_ref().unwrap(); + paths.push(if key == selected_key { + backup.join("partition.json") + } else { + f.layout + .artifacts_root() + .join(&key[..2]) + .join(key) + .join("partition.json") + }); + } + // fdescfs reports its own device number on macOS while preserving the + // actual inode/size/times. All uniquely created fixture payloads are on + // one filesystem; matching these facts counts the real open handles, + // including duplicates, without opening/duplicating a payload descriptor. + let payloads: Vec<_> = paths + .iter() + .map(|path| { + let m = std::fs::metadata(path).unwrap(); + (m.ino(), m.len(), m.mtime(), m.mtime_nsec()) + }) + .collect(); + let count = || { + let table = if cfg!(target_os = "linux") { + "/proc/self/fd" + } else { + "/dev/fd" + }; + std::fs::read_dir(table) + .unwrap() + .filter_map(Result::ok) + .filter_map(|entry| std::fs::metadata(entry.path()).ok()) + .filter(|m| { + m.is_file() && payloads.contains(&(m.ino(), m.len(), m.mtime(), m.mtime_nsec())) + }) + .count() + }; + let peak = std::cell::Cell::new(0); + let cancel = || { + let live = count(); + peak.set(peak.get().max(live)); + assert!( + live <= 3, + "actual aggregate payload FD count {live} exceeds 3" + ); + false + }; + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let initial_floor = memory.used(); + let mut job = f.job(&memory).unwrap(); + job.select_backup_cache(address.key(), address.name()) + .unwrap(); + assert_eq!( + count(), + 2, + "OS probe must observe the actual retained source pair" + ); + let certificates = Certificates::default(); + let mut association = None; + let mut restarted = 0; + for _ in 0..32 { + match job + .advance_with_certificates(&f.domain, &memory, &cancel, &certificates) + .unwrap() + { + LegacyProgress::Pending => {} + LegacyProgress::CacheValidated(certificate) => { + persist_certificate(&mut job, &certificates, certificate).unwrap(); + let checkpoint = job.checkpoint().unwrap(); + let (database, manifest) = job.into_source_handles(); + job = LegacyAssociationPreparation::restore( + database, + manifest, + RootedDirectory::open_readonly_root( + &f.layout.artifacts_root(), + path_identity(&f.layout.artifacts_root()).unwrap(), + ) + .unwrap(), + &f.layout.artifacts_root(), + &f.root, + checkpoint, + &memory, + ) + .unwrap(); + restarted += 1; + } + LegacyProgress::Complete(value) => { + association = Some(value); + break; + } + } + cancel(); + } + assert_eq!(restarted, 2); + let association = association.expect("actual native/manifest/backup join must complete"); + let (database, manifest) = job.into_source_handles(); + let prepared = super::super::maintenance_backup::prepare_backup( + &f.domain, + &f.epoch(), + address, + association, + database, + manifest, + &memory, + &cancel, + ) + .unwrap(); + assert_eq!(count(), 3); + assert_eq!(peak.get(), 3); + drop(prepared); + assert_eq!(count(), 0); + assert_eq!(memory.used(), initial_floor); + assert!(memory.peak() <= memory.limit()); + } + + #[test] + fn dc_legacy_real_public_producer_without_receipts_has_complete_native_association() { + let f = Fixture::new(); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let facts = f.complete(&memory).unwrap(); + assert_eq!(facts.cache_count(), 2); + assert!(facts.cache_loader_usable()); + let root = RootedDirectory::open_readonly_root( + &f.layout.artifacts_root(), + path_identity(&f.layout.artifacts_root()).unwrap(), + ) + .unwrap(); + assert!(facts.matches( + &File::open(&f.database).unwrap(), + &File::open(&f.manifest).unwrap(), + &root + )); + assert!(memory.peak() <= 8 * 1024 * 1024); + } + #[test] + fn dc_legacy_manifest_and_cache_substitution_never_mint_association() { + let f = Fixture::new(); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let original = std::fs::read(&f.manifest).unwrap(); + let mut value: serde_json::Value = serde_json::from_slice(&original).unwrap(); + value["files"]["src/a.rs"]["content_hash"] = "foreign".into(); + let mutation = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + std::fs::write(&f.manifest, serde_json::to_vec(&value).unwrap()).unwrap(); + drop(mutation); + assert!(matches!(f.complete(&memory), Err(LegacyIssue::Unknown))); + let mutation = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + std::fs::write(&f.manifest, original).unwrap(); + let cache = f.first_cache(); + std::fs::write(cache, b"{}").unwrap(); + drop(mutation); + assert!(f.complete(&memory).is_err()); + } + #[test] + fn dc_legacy_database_catalog_and_source_root_substitution_are_unknown() { + let f = Fixture::new(); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let file = File::open(&f.database).unwrap(); + let source = FileSource { + file: &file, + len: file.metadata().unwrap().len(), + }; + let mut property = None; + native::trace_native_catalog43(&source, &memory, &mut |event| { + if let NativeCatalogEvent::Property { name, .. } = event { + if raw_equal(&source, name, b"content_hash").unwrap() { + property = Some(name.start); + } + } + Ok(()) + }) + .unwrap(); + let mut bytes = std::fs::read(&f.database).unwrap(); + bytes[property.unwrap() as usize] = b'X'; + let mutation = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + std::fs::write(&f.database, bytes).unwrap(); + drop(mutation); + assert!(f.complete(&memory).is_err()); + let foreign = Fixture::new(); + let bytes = std::fs::read(&foreign.database).unwrap(); + let mutation = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + std::fs::write(&f.database, bytes).unwrap(); + drop(mutation); + assert!(f.complete(&memory).is_err()); + } + #[test] + fn dc_legacy_original_epoch_cancellation_and_cap_fail_closed() { + let f = Fixture::new(); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let mut job = f.job(&memory).unwrap(); + let mutation = ReferenceMutation::begin_in_domain(&f.domain).unwrap(); + drop(mutation); + assert!(matches!( + job.advance(&f.domain, &memory, &|| false), + Err(LegacyIssue::Changed) + )); + let mut job = f.job(&memory).unwrap(); + assert!(matches!( + job.advance(&f.domain, &memory, &|| true), + Err(LegacyIssue::Cancelled) + )); + let tiny = CleanupMemory::new(1024).unwrap(); + assert!(matches!(f.job(&tiny), Err(LegacyIssue::Resource(_)))); + } + #[test] + fn dc_legacy_cache_change_after_certification_invalidates_final_join() { + let f = Fixture::new(); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let mut job = f.job(&memory).unwrap(); + let certificates = Certificates::default(); + assert!(matches!( + job.advance_with_certificates(&f.domain, &memory, &|| false, &certificates) + .unwrap(), + LegacyProgress::Pending + )); + let certificate = match job + .advance_with_certificates(&f.domain, &memory, &|| false, &certificates) + .unwrap() + { + LegacyProgress::CacheValidated(c) => c, + _ => panic!("expected one validated cache"), + }; + persist_certificate(&mut job, &certificates, certificate).unwrap(); + let mut bytes = std::fs::read(f.first_cache()).unwrap(); + bytes.push(b' '); + std::fs::write(f.first_cache(), bytes).unwrap(); + let mut rejected = false; + for _ in 0..8 { + match job.advance_with_certificates(&f.domain, &memory, &|| false, &certificates) { + Ok(LegacyProgress::Pending) => {} + Ok(LegacyProgress::CacheValidated(c)) => { + persist_certificate(&mut job, &certificates, c).unwrap() + } + Ok(LegacyProgress::Complete(_)) => panic!("changed certified cache accepted"), + Err(_) => { + rejected = true; + break; + } + } + } + assert!(rejected); + } + #[test] + fn dc_legacy_durable_checkpoint_resumes_after_one_payload_without_relabeling_epoch() { + let f = Fixture::new(); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let certificates = Certificates::default(); + let mut job = f.job(&memory).unwrap(); + assert!(matches!( + job.advance_with_certificates(&f.domain, &memory, &|| false, &certificates) + .unwrap(), + LegacyProgress::Pending + )); + let certificate = match job + .advance_with_certificates(&f.domain, &memory, &|| false, &certificates) + .unwrap() + { + LegacyProgress::CacheValidated(c) => c, + _ => panic!("expected one cache"), + }; + let checkpoint = job.checkpoint_after_cache().unwrap(); + let context = checkpoint.context_digest(); + let original_epoch = checkpoint.source_epoch().clone(); + let mut checkpoint_bytes = [0; LEGACY_CHECKPOINT_BYTES]; + checkpoint.encode(&mut checkpoint_bytes); + persist_certificate(&mut job, &certificates, certificate).unwrap(); + std::fs::write(f.root.join("checkpoint.bin"), checkpoint_bytes).unwrap(); + drop(job); + let bytes: [u8; LEGACY_CHECKPOINT_BYTES] = std::fs::read(f.root.join("checkpoint.bin")) + .unwrap() + .try_into() + .unwrap(); + let checkpoint = LegacyCheckpoint::decode(&bytes).unwrap(); + assert_eq!(checkpoint.certified_cache_count(), 1); + assert_eq!(checkpoint.source_epoch(), &original_epoch); + assert_eq!(checkpoint.context_digest(), context); + let root = RootedDirectory::open_readonly_root( + &f.layout.artifacts_root(), + path_identity(&f.layout.artifacts_root()).unwrap(), + ) + .unwrap(); + let mut job = LegacyAssociationPreparation::restore( + File::open(&f.database).unwrap(), + File::open(&f.manifest).unwrap(), + root, + &f.layout.artifacts_root(), + &f.root, + checkpoint, + &memory, + ) + .unwrap(); + let mut complete = None; + for _ in 0..10 { + match job + .advance_with_certificates(&f.domain, &memory, &|| false, &certificates) + .unwrap() + { + LegacyProgress::Pending => {} + LegacyProgress::CacheValidated(c) => { + persist_certificate(&mut job, &certificates, c).unwrap() + } + LegacyProgress::Complete(facts) => { + complete = Some(facts); + break; + } + } + } + let facts = complete.unwrap(); + assert_eq!(facts.cache_count(), 2); + assert_eq!(facts.source_epoch(), &original_epoch); + assert_eq!(certificates.0.borrow().len(), 2); + for cut in 0..bytes.len() { + let mut torn = bytes; + torn[cut..].fill(0); + if torn != bytes { + assert!( + LegacyCheckpoint::decode(&torn).is_err(), + "checkpoint cut {cut}" + ) + } + } + } +} + +pub(crate) const LEGACY_CERTIFICATE_BYTES: usize = 768; +pub(crate) const LEGACY_CHECKPOINT_BYTES: usize = 2048; +#[derive(Clone, Copy)] +struct LegacyProjectedFact { + domain: u64, + ordinal: u64, + fields: [Span; 5], +} +/// An opaque fixed batch produced only after one complete actual cache check. +/// Its bytes must be published by the caller's genuine scoped private journal. +#[derive(Clone, Copy)] +pub(crate) struct LegacyCacheCertificate { + key: [u8; 32], + stamp: FixedFileStamp, + digest: [u8; 32], + facts: [LegacyProjectedFact; 5], + rows: u64, + loader_usable: bool, +} +fn put_u64(bytes: &mut [u8], value: u64) { + bytes[..8].copy_from_slice(&value.to_le_bytes()); +} +fn get_u64(bytes: &[u8]) -> u64 { + u64::from_le_bytes(bytes[..8].try_into().unwrap()) +} +fn put_id(bytes: &mut [u8], identity: FileIdentity) { + let (a, b) = identity.fixed_words(); + put_u64(bytes, a); + put_u64(&mut bytes[8..], b); +} +fn get_id(bytes: &[u8]) -> FileIdentity { + FileIdentity::from_fixed_words(get_u64(bytes), get_u64(&bytes[8..])) +} +fn checksum(bytes: &mut [u8]) { + let end = bytes.len() - 32; + let hash = Sha256::digest(&bytes[..end]); + bytes[end..].copy_from_slice(&hash); +} +fn valid_checksum(bytes: &[u8]) -> bool { + let end = bytes.len() - 32; + Sha256::digest(&bytes[..end]).as_slice() == &bytes[end..] +} +impl LegacyCacheCertificate { + pub(crate) fn encode(&self, out: &mut [u8; LEGACY_CERTIFICATE_BYTES]) { + out.fill(0); + out[..8].copy_from_slice(b"CGLGCF01"); + out[8] = u8::from(self.loader_usable); + out[16..48].copy_from_slice(&self.key); + self.stamp + .encode_checkpoint((&mut out[48..128]).try_into().unwrap()); + out[128..160].copy_from_slice(&self.digest); + for (i, fact) in self.facts.iter().enumerate() { + let row = &mut out[160 + i * 96..256 + i * 96]; + put_u64(row, fact.domain); + put_u64(&mut row[8..], fact.ordinal); + for (j, span) in fact.fields.iter().enumerate() { + put_u64(&mut row[16 + j * 16..], span.start); + put_u64(&mut row[24 + j * 16..], span.end); + } + } + put_u64(&mut out[640..], self.rows); + checksum(out); + } + pub(crate) fn decode(bytes: &[u8; LEGACY_CERTIFICATE_BYTES]) -> Result { + if &bytes[..8] != b"CGLGCF01" + || bytes[8] > 1 + || bytes[9..16].iter().any(|b| *b != 0) + || bytes[648..736].iter().any(|b| *b != 0) + || !valid_checksum(bytes) + { + return Err(LegacyIssue::Unknown); + } + let stamp = FixedFileStamp::decode_checkpoint((&bytes[48..128]).try_into().unwrap()) + .map_err(fixed)?; + let rows = get_u64(&bytes[640..]); + let mut facts = [LegacyProjectedFact { + domain: 0, + ordinal: 0, + fields: [Span::default(); 5], + }; 5]; + let mut domains = [0u8; 4]; + for (i, fact) in facts.iter_mut().enumerate() { + let row = &bytes[160 + i * 96..256 + i * 96]; + fact.domain = get_u64(row); + fact.ordinal = get_u64(&row[8..]); + if !(1..=4).contains(&fact.domain) || fact.ordinal >= rows { + return Err(LegacyIssue::Unknown); + } + domains[(fact.domain - 1) as usize] += 1; + for (j, span) in fact.fields.iter_mut().enumerate() { + span.start = get_u64(&row[16 + j * 16..]); + span.end = get_u64(&row[24 + j * 16..]); + if span.start >= span.end || span.end > stamp.len() { + return Err(LegacyIssue::Unknown); + } + } + } + if domains != [1, 1, 1, 2] { + return Err(LegacyIssue::Unknown); + } + Ok(Self { + key: bytes[16..48].try_into().unwrap(), + stamp, + digest: bytes[128..160].try_into().unwrap(), + facts, + rows, + loader_usable: bytes[8] == 1, + }) + } + pub(crate) fn digest(&self) -> Result<[u8; 32], LegacyIssue> { + let mut bytes = [0; LEGACY_CERTIFICATE_BYTES]; + self.encode(&mut bytes); + Ok(Sha256::digest(bytes).into()) + } +} +fn chain_certificate( + chain: &mut [u8; 32], + certificate: &LegacyCacheCertificate, +) -> Result<(), LegacyIssue> { + let mut hash = Sha256::new(); + hash.update(b"CGLegacyCertificateChain01"); + hash.update(*chain); + hash.update(certificate.digest()?); + *chain = hash.finalize().into(); + Ok(()) +} +/// Implemented by the scoped private publisher. Replay only committed complete +/// certificate frames bound to this exact checkpoint/source incarnation. +pub(crate) trait ReplayLegacyCertificates { + fn replay( + &self, + memory: &CleanupMemory, + emit: &mut dyn FnMut(LegacyCacheCertificate) -> Result<(), LegacyIssue>, + ) -> Result<(), LegacyIssue>; +} +#[cfg(test)] +struct NoCertificates; +#[cfg(test)] +impl ReplayLegacyCertificates for NoCertificates { + fn replay( + &self, + _: &CleanupMemory, + _: &mut dyn FnMut(LegacyCacheCertificate) -> Result<(), LegacyIssue>, + ) -> Result<(), LegacyIssue> { + Err(LegacyIssue::Unknown) + } +} + +/// Checksum-protected progress facts only. Publication and recoverable selection +/// of these bytes belong to the existing scoped journal, never this codec. +pub(crate) struct LegacyCheckpoint { + expected: ReferenceEpoch, + database: FixedFileStamp, + manifest: FixedFileStamp, + cache_root: FileIdentity, + backup: Option, + repository_identity: FileIdentity, + context: [u8; 32], + files: Span, + next: u64, + previous: Option, + count: u64, + chain: [u8; 32], + database_sha256: [u8; 32], + manifest_sha256: [u8; 32], + loader_usable: bool, + stage: Stage, +} +fn context_digest(root: &Path, repository: &Path) -> [u8; 32] { + let mut hash = Sha256::new(); + hash.update(b"CGLegacyContext01"); + hash.update((root.as_os_str().as_encoded_bytes().len() as u64).to_le_bytes()); + hash.update(root.as_os_str().as_encoded_bytes()); + hash.update(repository.as_os_str().as_encoded_bytes()); + hash.finalize().into() +} +impl LegacyCheckpoint { + pub(crate) fn encode(&self, out: &mut [u8; LEGACY_CHECKPOINT_BYTES]) { + out.fill(0); + out[..8].copy_from_slice(b"CGLGCP01"); + out[8] = match self.stage { + Stage::Catalog => 0, + Stage::Caches => 1, + Stage::Join => 2, + Stage::Done => 3, + }; + out[9] = u8::from(self.loader_usable); + out[10] = u8::from(self.previous.is_some()); + self.expected + .encode_checkpoint((&mut out[16..528]).try_into().unwrap()); + self.database + .encode_checkpoint((&mut out[528..608]).try_into().unwrap()); + self.manifest + .encode_checkpoint((&mut out[608..688]).try_into().unwrap()); + put_id(&mut out[688..704], self.cache_root); + out[704..736].copy_from_slice(&self.context); + put_u64(&mut out[736..], self.files.start); + put_u64(&mut out[744..], self.files.end); + put_u64(&mut out[752..], self.next); + if let Some(previous) = self.previous { + put_u64(&mut out[760..], previous.start); + put_u64(&mut out[768..], previous.end); + } + put_u64(&mut out[776..], self.count); + out[784..816].copy_from_slice(&self.chain); + out[816..848].copy_from_slice(&self.database_sha256); + out[848..880].copy_from_slice(&self.manifest_sha256); + put_id(&mut out[880..896], self.repository_identity); + if let Some(backup) = self.backup { + out[11] = 1; + out[896..928].copy_from_slice(&backup.key); + out[928..1056].copy_from_slice(&backup.name); + out[1056] = backup.len; + put_id(&mut out[1057..1073], backup.directory); + backup + .stamp + .encode_checkpoint((&mut out[1073..1153]).try_into().unwrap()); + } + checksum(out); + } + pub(crate) fn decode(bytes: &[u8; LEGACY_CHECKPOINT_BYTES]) -> Result { + if &bytes[..8] != b"CGLGCP01" + || bytes[8] > 3 + || bytes[9] > 1 + || bytes[10] > 1 + || bytes[11] > 1 + || bytes[12..16].iter().any(|b| *b != 0) + || bytes[1153..2016].iter().any(|b| *b != 0) + || !valid_checksum(bytes) + { + return Err(LegacyIssue::Unknown); + } + let expected = ReferenceEpoch::decode_checkpoint((&bytes[16..528]).try_into().unwrap()) + .map_err(fixed)?; + let database = FixedFileStamp::decode_checkpoint((&bytes[528..608]).try_into().unwrap()) + .map_err(fixed)?; + let manifest = FixedFileStamp::decode_checkpoint((&bytes[608..688]).try_into().unwrap()) + .map_err(fixed)?; + let files = Span { + start: get_u64(&bytes[736..]), + end: get_u64(&bytes[744..]), + }; + let next = get_u64(&bytes[752..]); + let previous = if bytes[10] == 1 { + Some(Span { + start: get_u64(&bytes[760..]), + end: get_u64(&bytes[768..]), + }) + } else { + if bytes[760..776].iter().any(|b| *b != 0) { + return Err(LegacyIssue::Unknown); + } + None + }; + let count = get_u64(&bytes[776..]); + let stage = match bytes[8] { + 0 => Stage::Catalog, + 1 => Stage::Caches, + 2 => Stage::Join, + _ => Stage::Done, + }; + if stage != Stage::Catalog + && (files.start >= files.end + || files.end > manifest.len() + || next <= files.start + || next >= files.end + || count > 0 && previous.is_none()) + { + return Err(LegacyIssue::Unknown); + } + if previous.is_some_and(|p| p.start >= p.end || p.end > manifest.len()) { + return Err(LegacyIssue::Unknown); + } + let backup = if bytes[11] == 1 { + let b = BackupCache { + key: bytes[896..928].try_into().unwrap(), + name: bytes[928..1056].try_into().unwrap(), + len: bytes[1056], + directory: get_id(&bytes[1057..1073]), + stamp: FixedFileStamp::decode_checkpoint((&bytes[1073..1153]).try_into().unwrap()) + .map_err(fixed)?, + }; + if b.len > 128 + || !backup_name(b.key, &b.name[..usize::from(b.len)]) + || b.name[usize::from(b.len)..].iter().any(|v| *v != 0) + { + return Err(LegacyIssue::Unknown); + } + Some(b) + } else { + if bytes[896..1153].iter().any(|v| *v != 0) { + return Err(LegacyIssue::Unknown); + } + None + }; + Ok(Self { + expected, + database, + manifest, + backup, + cache_root: get_id(&bytes[688..704]), + repository_identity: get_id(&bytes[880..896]), + context: bytes[704..736].try_into().unwrap(), + files, + next, + previous, + count, + chain: bytes[784..816].try_into().unwrap(), + database_sha256: bytes[816..848].try_into().unwrap(), + manifest_sha256: bytes[848..880].try_into().unwrap(), + loader_usable: bytes[9] == 1, + stage, + }) + } + pub(crate) fn source_epoch(&self) -> &ReferenceEpoch { + &self.expected + } + pub(crate) fn context_digest(&self) -> [u8; 32] { + let mut epoch = [0; 512]; + self.expected.encode_checkpoint(&mut epoch); + let mut db = [0; 80]; + self.database.encode_checkpoint(&mut db); + let mut manifest = [0; 80]; + self.manifest.encode_checkpoint(&mut manifest); + let (a, b) = self.cache_root.fixed_words(); + let mut hash = Sha256::new(); + hash.update(b"CGLegacyJobContext01"); + hash.update(epoch); + hash.update(db); + hash.update(manifest); + hash.update(a.to_le_bytes()); + hash.update(b.to_le_bytes()); + hash.update(self.context); + let (a, b) = self.repository_identity.fixed_words(); + hash.update(a.to_le_bytes()); + hash.update(b.to_le_bytes()); + if let Some(b) = self.backup { + hash.update(b.key); + hash.update(b.name); + let mut stamp = [0; 80]; + b.stamp.encode_checkpoint(&mut stamp); + hash.update(stamp); + let (a, c) = b.directory.fixed_words(); + hash.update(a.to_le_bytes()); + hash.update(c.to_le_bytes()); + } + hash.finalize().into() + } + #[cfg(test)] + pub(crate) fn certified_cache_count(&self) -> u64 { + self.count + } +} +impl LegacyAssociationPreparation { + pub(crate) fn checkpoint(&self) -> Result { + if self.pending.is_some() { + return Err(LegacyIssue::Unknown); + } + self.checkpoint_inner(false) + } + pub(crate) fn checkpoint_after_cache(&self) -> Result { + if self.pending.is_none() { + return Err(LegacyIssue::Unknown); + } + self.checkpoint_inner(true) + } + fn checkpoint_inner(&self, include_pending: bool) -> Result { + let mut count = self.cache_count; + let mut chain = self.certificate; + let mut loader_usable = self.cache_loader_usable; + if include_pending { + let pending = self.pending.as_ref().ok_or(LegacyIssue::Unknown)?; + chain_certificate(&mut chain, pending)?; + count = count.checked_add(1).ok_or(LegacyIssue::Unknown)?; + loader_usable &= pending.loader_usable; + } + Ok(LegacyCheckpoint { + expected: self.expected.clone(), + database: self.database_stamp, + manifest: self.manifest_stamp, + cache_root: self.artifact_root.identity(), + backup: self.backup, + repository_identity: self.repository_identity, + context: context_digest(&self.artifact_root_path, &self.repository), + files: self.files, + next: self.cursor.as_ref().map(|c| c.next_offset).unwrap_or(0), + previous: self.cursor.as_ref().and_then(|c| c.previous), + count, + chain, + database_sha256: self.database_sha256, + manifest_sha256: self.manifest_sha256, + loader_usable, + stage: self.stage, + }) + } + pub(crate) fn restore( + database: File, + manifest: File, + artifact_root: RootedDirectory, + artifact_root_path: &Path, + repository_root: &Path, + checkpoint: LegacyCheckpoint, + memory: &CleanupMemory, + ) -> Result { + if checkpoint.stage == Stage::Done + || context_digest(artifact_root_path, repository_root) != checkpoint.context + || checkpoint.cache_root != artifact_root.identity() + { + return Err(LegacyIssue::Changed); + } + let mut job = Self::new( + database, + manifest, + artifact_root, + artifact_root_path, + repository_root, + checkpoint.expected, + memory, + )?; + job.backup = checkpoint.backup; + if let Some(b) = job.backup { + let _ = open_cache(&job.artifact_root, b.key, job.backup)?; + } + if job.database_stamp != checkpoint.database || job.manifest_stamp != checkpoint.manifest { + return Err(LegacyIssue::Changed); + } + if checkpoint.stage != Stage::Catalog { + job.files = checkpoint.files; + job.cursor = Some(ManifestCursor::resume( + &job.manifest, + checkpoint.files, + checkpoint.next, + checkpoint.previous, + memory, + )?); + job.stage = checkpoint.stage; + job.database_sha256 = checkpoint.database_sha256; + job.manifest_sha256 = checkpoint.manifest_sha256; + job.certificate = checkpoint.chain; + job.cache_count = checkpoint.count; + job.cache_loader_usable = checkpoint.loader_usable; + job.resume_validation = true; + } + Ok(job) + } +} + +#[cfg(test)] +mod checkpoint_tests { + use super::*; + #[test] + fn dc_legacy_fixed_certificate_rejects_reserved_and_torn_frames() { + let root = std::env::temp_dir().join(format!( + "legacy-codec-{}", + super::super::layout::managed_generation_id() + )); + std::fs::create_dir(&root).unwrap(); + let path = root.join("payload"); + std::fs::write(&path, b"\"key\"").unwrap(); + let stamp = + FixedFileStamp::capture(&File::open(&path).unwrap(), FixedControlKind::File).unwrap(); + let fields = [Span { start: 0, end: 5 }; 5]; + let facts = [ + LegacyProjectedFact { + domain: 1, + ordinal: 0, + fields, + }, + LegacyProjectedFact { + domain: 2, + ordinal: 1, + fields, + }, + LegacyProjectedFact { + domain: 3, + ordinal: 2, + fields, + }, + LegacyProjectedFact { + domain: 4, + ordinal: 3, + fields, + }, + LegacyProjectedFact { + domain: 4, + ordinal: 4, + fields, + }, + ]; + let certificate = LegacyCacheCertificate { + key: [1; 32], + stamp, + digest: [2; 32], + facts, + rows: 5, + loader_usable: true, + }; + let mut bytes = [0; LEGACY_CERTIFICATE_BYTES]; + certificate.encode(&mut bytes); + assert!(LegacyCacheCertificate::decode(&bytes).is_ok()); + for cut in 0..bytes.len() { + let mut torn = bytes; + torn[cut..].fill(0); + if torn != bytes { + assert!(LegacyCacheCertificate::decode(&torn).is_err(), "cut {cut}") + } + } + let mut reserved = bytes; + reserved[700] = 1; + checksum(&mut reserved); + assert!(LegacyCacheCertificate::decode(&reserved).is_err()); + let mut domain = bytes; + put_u64(&mut domain[160..], 9); + checksum(&mut domain); + assert!(LegacyCacheCertificate::decode(&domain).is_err()); + std::fs::remove_dir_all(root).unwrap(); + } + #[test] + fn dc_legacy_streaming_native_schema_matches_current_assets() { + let value: serde_json::Value = + serde_json::from_str(include_str!("../../assets/graph_schema.json")).unwrap(); + let mut nodes = std::collections::BTreeSet::new(); + for node in value["node_types"].as_array().unwrap() { + nodes.insert(node["name"].as_str().unwrap()); + } + for relation in value["relation_types"].as_array().unwrap() { + for field in ["source_types", "target_types"] { + for n in relation[field].as_array().unwrap() { + nodes.insert(n.as_str().unwrap()); + } + } + } + assert_eq!(nodes.into_iter().collect::>(), TABLE_NAMES[..37]); + assert_eq!(ENDPOINTS.len(), 396); + } +} +impl LegacyAssociationPreparation { + /// Select one exact historical replacement directory before any scan. The + /// name is an address only; complete producer/schema/native joins still run. + pub(crate) fn select_backup_cache( + &mut self, + key: [u8; 32], + name: &[u8], + ) -> Result<(), LegacyIssue> { + if self.stage != Stage::Catalog || self.backup.is_some() || !backup_name(key, name) { + return Err(LegacyIssue::Unknown); + } + let hex = hex(key); + let prefix = self.artifact_root.open_directory_readonly(&hex[..2])?; + match prefix.open_directory_readonly(&hex) { + Ok(_) => return Err(LegacyIssue::Unknown), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(e.into()), + } + let directory = prefix.open_directory_readonly(name)?; + let file = directory.open_readonly_file(b"partition.json")?; + let mut owned = [0; 128]; + owned[..name.len()].copy_from_slice(name); + self.backup = Some(BackupCache { + key, + name: owned, + len: name.len() as u8, + directory: directory.identity(), + stamp: FixedFileStamp::capture(&file, FixedControlKind::File).map_err(fixed)?, + }); + Ok(()) + } +} +impl LegacyAssociation { + pub(crate) fn selected_backup_loader_usable(&self) -> Option { + self.backup_loader_usable + } + pub(crate) fn selected_backup_matches( + &self, + key: [u8; 32], + directory: FileIdentity, + stamp: FixedFileStamp, + ) -> bool { + self.backup + .is_some_and(|b| b.key == key && b.directory == directory && b.stamp == stamp) + } +} +impl LegacyCheckpoint { + pub(crate) fn matches_closed_source( + &self, + database: Option, + manifest: Option, + cache: FileIdentity, + ) -> bool { + database == Some(self.database) + && manifest == Some(self.manifest) + && cache == self.cache_root + } +} + +#[derive(Default)] +pub(crate) struct SelectedBackupAssociationState { + pub(super) job: Option>, + pub(super) transport: Option, +} +impl SelectedBackupAssociationState { + pub(crate) fn clear(&mut self) { + drop(self.job.take()); + drop(self.transport.take()); + } +} +/// Pure bounded preparation. The caller supplies actual resolved source handles +/// and a certified replay reader; this function creates no reference authority. +#[allow(clippy::too_many_arguments)] +impl LegacyAssociationPreparation { + pub(super) fn source_matches( + &self, + database: &File, + manifest: &File, + root: &RootedDirectory, + path: &Path, + repository: &Path, + expected: &ReferenceEpoch, + ) -> bool { + self.database_stamp.recheck(database).is_ok() + && self.manifest_stamp.recheck(manifest).is_ok() + && self.artifact_root.identity() == root.identity() + && self.artifact_root_path == path + && self.repository == repository + && (self.closed_source_authorized || self.expected == *expected) + } + pub(super) fn into_source_handles(self) -> (File, File) { + (self.database, self.manifest) + } +} + +impl SelectedBackupAssociationState { + pub(crate) fn needs_source_handles(&self) -> bool { + self.job.is_none() && self.transport.as_ref().is_none_or(|t| !t.has_completed()) + } +} +impl LegacyAssociationPreparation { + pub(super) fn check_parked_source( + &self, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + root: &Path, + repository: &Path, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + ) -> Result<(), LegacyIssue> { + if (!self.closed_source_authorized && self.expected != *expected) + || self.artifact_root_path != root + || self.repository != repository + { + return Err(LegacyIssue::Changed); + } + self.current(domain, memory, cancel) + } +} + +impl LegacyCacheCertificate { + pub(crate) fn key(&self) -> [u8; 32] { + self.key + } + pub(crate) fn stamp(&self) -> FixedFileStamp { + self.stamp + } + pub(crate) fn payload_digest(&self) -> [u8; 32] { + self.digest + } + pub(crate) fn loader_usable(&self) -> bool { + self.loader_usable + } +} + +impl LegacyAssociationPreparation { + pub(super) fn authorize_closed_source( + &mut self, + owner: &super::maintenance_legacy_bootstrap::LegacySourceOwner, + memory: &CleanupMemory, + ) -> Result<(), LegacyIssue> { + let source = owner.closed_version(); + if owner.is_administrative() + || !owner.is_current(memory).map_err(fixed)? + || !matches!( + source.phase(), + super::maintenance_legacy_guard::LegacyPhase::Preparing + | super::maintenance_legacy_guard::LegacyPhase::Retiring + ) + || source.database_stamp() != Some(self.database_stamp) + || source.manifest_stamp() != Some(self.manifest_stamp) + || source.cache_identity() != self.artifact_root.identity() + { + return Err(LegacyIssue::Changed); + } + // The original destination epoch remains immutable checkpoint data. A + // genuinely closed source has its own retained version; unrelated live + // destination producers do not mutate that closed bundle. + self.closed_source_authorized = true; + Ok(()) + } +} diff --git a/src/storage/maintenance_legacy_bootstrap.rs b/src/storage/maintenance_legacy_bootstrap.rs new file mode 100644 index 00000000..0c58ea59 --- /dev/null +++ b/src/storage/maintenance_legacy_bootstrap.rs @@ -0,0 +1,4315 @@ +//! Actual legacy enrollment and scoped mutation. The lower guard module alone +//! supplies noncreating ordinary native/producer use checks. +use super::inventory::{default_direct, DirectPair, MaintenanceTarget}; +use super::layout::DirectLayout; +use super::locks::{ + self, FileIdentity, FixedControlError, FixedControlKind as Kind, + FixedControlProblem as Problem, FixedControlScratch, FixedFileStamp, FixedLockedFile, LockMode, + RootedDirectory, +}; +use super::maintenance_admission::PreparedTargetBinding; +use super::maintenance_budget::{CleanupMemory, MemoryReservation, MutationGrant}; +use super::maintenance_controls as codec; +use super::maintenance_legacy_guard::*; +use super::maintenance_legacy_guard::{ + component, invalid, Binding, Record, BINDING_BYTES, GATE, OWNER, SELECTOR, SLOT0, SLOT1, STATE, + STATE_BYTES, +}; +use super::maintenance_scheduler::LegacyQuiescenceConfirmation; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::path::{Path, PathBuf}; +#[derive(Clone, Copy)] +struct Entry { + stamp: Option, + kind: Kind, +} +impl Entry { + fn capture( + path: &Path, + kind: Kind, + optional: bool, + scratch: &mut FixedControlScratch, + ) -> Result { + let stamp = match locks::open_fixed_control(path, kind, false, scratch) { + Ok(file) => Some(FixedFileStamp::capture(&file, kind)?), + Err(FixedControlError::Io(e)) + if optional && e.kind() == std::io::ErrorKind::NotFound => + { + None + } + Err(e) => return Err(e), + }; + Ok(Self { stamp, kind }) + } +} +pub(crate) enum LegacyScope<'a> { + DefaultDirect { + pair: &'a DirectPair, + artifact_root: &'a Path, + }, + ManagedGeneration { + storage_root: &'a Path, + generation_root: &'a Path, + }, + ManagedAdministration { + storage_root: &'a Path, + generation_root: &'a Path, + }, +} +pub(crate) struct PreparedLegacyScope { + kind: LegacyScopeKind, + cohort_scope: [u8; 32], + root: PathBuf, + repository: PathBuf, + artifact: PathBuf, + member: PathBuf, + database: PathBuf, + manifest: PathBuf, + writer: PathBuf, + family: PathBuf, + native: PathBuf, + root_use: PathBuf, + paths: [PathBuf; 3], + entries: [Entry; 10], + binding: PreparedTargetBinding, + nonce: [u8; 32], + owner_nonce: [u8; 32], + coordination: [[u8; locks::COORDINATION_HEADER_BYTES]; 3], + enrollment_waiting: bool, + birth: Option>, + observation: Option>, + publication: Option< + super::maintenance_budget::MemoryBox< + super::maintenance_legacy_guard::PreparedSourcePublication, + >, + >, + job: [u8; 32], + key: [u8; 64], + member_name: [u8; 128], + member_len: usize, + freeze_cache: bool, + scratch: FixedControlScratch, + confirmation_source: Option, + confirmation_caches: [(PathBuf, Entry); 2], + administration_selector: Option<(PathBuf, Entry)>, + enrollment_runs: Option<(PathBuf, Entry)>, + _floor: MemoryReservation, +} + +pub(crate) fn prepare_legacy_scope( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + scope: LegacyScope<'_>, + freeze_cache: bool, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let mut prepared = prepare_legacy_scope_layout(target, binding, scope, freeze_cache, memory)?; + prepare_legacy_scope_source(&mut prepared, target, memory)?; + prepared.observation = + PreparedSourceObservation::read(&prepared.root, &prepared.paths, None, memory)?; + Ok(prepared) +} + +// Layout construction and source observation use distinct stack frames. The +// returned charged allocation owns every field before any source is observed. +#[inline(never)] +fn prepare_legacy_scope_layout( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + scope: LegacyScope<'_>, + freeze_cache: bool, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let mut charge = std::mem::size_of::() + 65536; + for p in [ + target.repository_root.as_path(), + target.state_root.as_path(), + ] { + charge = charge + .checked_add( + p.as_os_str() + .as_encoded_bytes() + .len() + .checked_mul(32) + .ok_or(FixedControlError::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?, + ) + .ok_or(FixedControlError::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?; + } + let floor = memory + .reserve(charge) + .map_err(FixedControlError::Resource)?; + let administrative = matches!(&scope, LegacyScope::ManagedAdministration { .. }); + // Fixed branch layout keeps every intended path and physical binding explicit without a dynamic container. + #[allow(clippy::type_complexity)] + let (kind, root, artifact, member, database, manifest, writer, key, member_name, member_len): ( + LegacyScopeKind, + PathBuf, + PathBuf, + PathBuf, + PathBuf, + PathBuf, + PathBuf, + [u8; 64], + [u8; 128], + usize, + ) = match scope { + LegacyScope::DefaultDirect { + pair, + artifact_root, + } => { + if *pair != default_direct(target) + || pair.database.parent() != Some(target.state_root.as_path()) + || pair.manifest != target.state_root.join("manifest.json") + || artifact_root.parent() != Some(target.state_root.as_path()) + { + return Err(invalid(Problem::InvalidRecord)); + } + let layout = DirectLayout::new(&pair.database, &pair.manifest); + let conventional = target.state_root.join("artifacts"); + if artifact_root != conventional && artifact_root != layout.artifact_root_path() { + return Err(invalid(Problem::InvalidRecord)); + } + if pair.database.to_str().is_none() || pair.manifest.to_str().is_none() { + return Err(invalid(Problem::InvalidRecord)); + } + let mut hash = Sha256::new(); + hash.update(pair.database.as_os_str().as_encoded_bytes()); + hash.update([0]); + hash.update(pair.manifest.as_os_str().as_encoded_bytes()); + let digest = hash.finalize(); + let mut key = [0; 64]; + let digits = b"0123456789abcdef"; + for (i, b) in digest.iter().enumerate() { + key[2 * i] = digits[(b >> 4) as usize]; + key[2 * i + 1] = digits[(b & 15) as usize]; + } + ( + LegacyScopeKind::DefaultDirect, + target.state_root.clone(), + artifact_root.to_owned(), + target.state_root.clone(), + pair.database.clone(), + pair.manifest.clone(), + layout.writer_lock_path(), + key, + [0; 128], + 0, + ) + } + LegacyScope::ManagedGeneration { + storage_root, + generation_root, + } + | LegacyScope::ManagedAdministration { + storage_root, + generation_root, + } => { + if super::maintenance_projection::project_binding(target, memory) + .map_err(|_| invalid(Problem::InvalidRecord))? + .storage + != storage_root + || generation_root.parent() != Some(storage_root.join("generations").as_path()) + { + return Err(invalid(Problem::InvalidRecord)); + } + let name = generation_root + .file_name() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .as_encoded_bytes(); + if !name.starts_with(b"gen-") || name.len() > 128 || !component(name) { + return Err(invalid(Problem::InvalidRecord)); + } + let mut member_name = [0; 128]; + member_name[..name.len()].copy_from_slice(name); + ( + if administrative { + LegacyScopeKind::ManagedAdministration + } else { + LegacyScopeKind::ManagedGeneration + }, + storage_root.to_owned(), + storage_root.join("artifacts"), + generation_root.to_owned(), + generation_root.join("graph.ldb"), + generation_root.join("manifest.json"), + storage_root.join("writer.lock"), + [0; 64], + member_name, + name.len(), + ) + } + }; + let (_, native) = super::maintenance_ownership::database_lock_path(&database, false) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let family = database + .parent() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .join(".database-use-family.lock"); + let root_use = artifact.join(".use.lock"); + let paths = [ + root.join(std::str::from_utf8(if administrative { ADMIN_GATE } else { GATE }).unwrap()), + root.join(std::str::from_utf8(if administrative { ADMIN_STATE } else { STATE }).unwrap()), + root.join(std::str::from_utf8(if administrative { ADMIN_OWNER } else { OWNER }).unwrap()), + ]; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(FixedControlError::Resource)?, + )?; + let entries = [ + Entry::capture(&root, Kind::Directory, false, &mut scratch)?, + Entry::capture( + &target.repository_root, + Kind::Directory, + false, + &mut scratch, + )?, + Entry::capture(&artifact, Kind::Directory, false, &mut scratch)?, + Entry::capture( + &member, + Kind::Directory, + matches!( + kind, + LegacyScopeKind::ManagedGeneration | LegacyScopeKind::ManagedAdministration + ), + &mut scratch, + )?, + Entry::capture(&database, Kind::File, true, &mut scratch)?, + Entry::capture(&manifest, Kind::File, true, &mut scratch)?, + Entry::capture(&writer, Kind::File, true, &mut scratch)?, + Entry::capture(&family, Kind::File, true, &mut scratch)?, + Entry::capture(&native, Kind::File, true, &mut scratch)?, + Entry::capture(&root_use, Kind::File, true, &mut scratch)?, + ]; + let mut nonce = [0; 32]; + let mut owner_nonce = [0; 32]; + let mut job = [0; 32]; + for out in [&mut nonce, &mut owner_nonce, &mut job] { + getrandom::fill(out).map_err(|_| invalid(Problem::InvalidRecord))?; + } + let default_pair = default_direct(target); + let conventional = target.state_root.join("artifacts"); + let keyed = + DirectLayout::new(&default_pair.database, &default_pair.manifest).artifact_root_path(); + let confirmation_caches = [ + ( + conventional.clone(), + Entry::capture(&conventional, Kind::Directory, true, &mut scratch)?, + ), + ( + keyed.clone(), + Entry::capture(&keyed, Kind::Directory, true, &mut scratch)?, + ), + ]; + let cohort_scope = legacy_cohort_scope(target, &root, &artifact, memory)?; + let administration_selector = if matches!( + kind, + LegacyScopeKind::ManagedAdministration | LegacyScopeKind::ManagedGeneration + ) { + let path = root.join("active.json"); + let entry = Entry::capture(&path, Kind::File, false, &mut scratch)?; + if kind == LegacyScopeKind::ManagedGeneration { + use std::io::Read; + let mut pointer = locks::open_fixed_control(&path, Kind::File, false, &mut scratch)?; + let stamp = entry.stamp.ok_or_else(|| invalid(Problem::Changed))?; + if stamp.len() > 1024 { + return Err(invalid(Problem::InvalidRecord)); + } + let mut bytes = [0; 1024]; + pointer.read_exact(&mut bytes[..stamp.len() as usize])?; + let active: super::managed::ActiveGeneration = + serde_json::from_slice(&bytes[..stamp.len() as usize]) + .map_err(|_| invalid(Problem::InvalidRecord))?; + super::layout::validate_generation_id(&active.generation_id) + .map_err(|_| invalid(Problem::InvalidRecord))?; + if active.schema_version != 2 + || super::layout::ManagedLayout::new(&root) + .generation(&active.generation_id) + .map_err(|_| invalid(Problem::InvalidRecord))? + .root() + == member + { + return Err(invalid(Problem::Busy)); + } + stamp.recheck(&pointer)?; + } + Some((path, entry)) + } else { + None + }; + let enrollment_runs = if kind == LegacyScopeKind::ManagedGeneration { + Some(prepare_empty_runs(&root, memory)?) + } else { + None + }; + super::maintenance_budget::MemoryBox::new( + PreparedLegacyScope { + enrollment_waiting: false, + birth: None, + observation: None, + coordination: [[0; locks::COORDINATION_HEADER_BYTES]; 3], + publication: None, + kind, + cohort_scope, + root, + repository: target.repository_root.clone(), + artifact, + member, + database, + manifest, + writer, + family, + native, + root_use, + paths, + entries, + binding: binding.duplicate(memory)?, + nonce, + owner_nonce, + job, + key, + member_name, + member_len, + freeze_cache: freeze_cache && kind == LegacyScopeKind::DefaultDirect, + scratch, + confirmation_source: None, + confirmation_caches, + administration_selector, + enrollment_runs, + _floor: floor, + }, + memory, + ) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +fn pending_scope_capsule_boxed( + prepared: &PreparedLegacyScope, + memory: &CleanupMemory, +) -> Result< + Option< + super::maintenance_budget::MemoryBox< + super::maintenance_legacy_guard::SourceCreationCapsule, + >, + >, + FixedControlError, +> { + let capsule = { + let directory = RootedDirectory::open_readonly_root( + &prepared.root, + prepared.entries[0].stamp.unwrap().identity(), + )?; + match directory + .open_readonly_file(prepared.paths[1].file_name().unwrap().as_encoded_bytes()) + { + Ok(file) => super::maintenance_legacy_guard::read_creation_capsule(&directory, &file)? + .filter(|c| { + locks::created_file_nonce(&file).ok().flatten() == Some(c.permission.job) + }), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e.into()), + } + }; + capsule + .map(|capsule| { + super::maintenance_budget::MemoryBox::new(capsule, memory) + .map_err(FixedControlError::Resource) + }) + .transpose() +} +#[inline(never)] +fn prepare_legacy_scope_source( + prepared: &mut PreparedLegacyScope, + target: &MaintenanceTarget, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + let pending_capsule = pending_scope_capsule_boxed(prepared, memory)?; + prepared.confirmation_source = if prepared.kind == LegacyScopeKind::DefaultDirect { + if let Some(capsule) = pending_capsule.as_ref() { + capsule.permission.root_default_source + } else if let Some(accepted) = + accepted_source_observation(&prepared.root, &prepared.paths, false, memory)?.filter( + |accepted| accepted.1.phase != LegacyPhase::Active || accepted.1.operator_pending, + ) + { + accepted.1.root_default_source + } else { + Some(super::maintenance_admission::capture_legacy_confirmation_source(target, memory)?) + } + } else { + None + }; + if let Some(capsule) = pending_capsule.as_ref() { + prepared.job = capsule.permission.job; + } + let observed_job = accepted_source_observation(&prepared.root, &prepared.paths, false, memory)? + .map(|accepted| accepted.1.job) + .unwrap_or(prepared.job); + prepared.nonce = existing_coordination_nonce(&prepared.paths[0])?.unwrap_or(prepared.nonce); + prepared.owner_nonce = + existing_coordination_nonce(&prepared.paths[2])?.unwrap_or(prepared.owner_nonce); + let use_nonce = existing_coordination_nonce(&prepared.root_use)?.unwrap_or(observed_job); + prepared.coordination = [ + locks::encode_coordination_incarnation(prepared.nonce), + locks::encode_coordination_incarnation(prepared.owner_nonce), + locks::encode_coordination_incarnation(use_nonce), + ]; + Ok(()) +} +/// This value can only borrow actual held exclusive guards inside enrollment. +/// It is neither serialized nor constructible from names, IDs or a bool. +pub(crate) struct LegacyEnrollmentAuthority<'a> { + kind: LegacyScopeKind, + directory: &'a RootedDirectory, + artifact: &'a RootedDirectory, + member: &'a RootedDirectory, + writer: &'a FixedLockedFile, + root_use: &'a FixedLockedFile, + _family: &'a FixedLockedFile, + _native: &'a FixedLockedFile, + _gate: &'a FixedLockedFile, + _confirmation: Option<&'a LegacyQuiescenceConfirmation<'a>>, + incarnation: [u8; 32], +} +impl LegacyEnrollmentAuthority<'_> { + pub(crate) fn directory(&self) -> &RootedDirectory { + self.directory + } + pub(crate) fn artifact_directory(&self) -> &RootedDirectory { + self.artifact + } + pub(crate) fn member_directory(&self) -> &RootedDirectory { + self.member + } + pub(crate) fn writer_identity(&self) -> Result { + Ok(FixedFileStamp::capture(self.writer.file(), Kind::File)?.identity()) + } + pub(crate) fn artifact_use_identity(&self) -> Result { + Ok(FixedFileStamp::capture(self.root_use.file(), Kind::File)?.identity()) + } + pub(crate) fn artifact_use_guard(&self) -> &FixedLockedFile { + self.root_use + } + pub(crate) fn proposed_artifact_incarnation(&self) -> [u8; 32] { + self.incarnation + } +} +fn checked_entry( + path: &Path, + entry: Entry, + scratch: &mut FixedControlScratch, + grant: &mut MutationGrant, +) -> Result { + grant.take_entry()?; + let file = locks::open_fixed_control(path, entry.kind, false, scratch)?; + let current = FixedFileStamp::capture(&file, entry.kind)?; + let expected = entry.stamp.ok_or_else(|| invalid(Problem::Changed))?; + if entry.kind == Kind::Directory { + if current.identity() != expected.identity() { + return Err(invalid(Problem::Changed)); + } + } else { + expected.recheck(&file)?; + } + Ok(file) +} +fn lock_entry( + path: &Path, + entry: Entry, + parent: &RootedDirectory, + scratch: &mut FixedControlScratch, + grant: &mut MutationGrant, +) -> Result<(FixedLockedFile, bool), FixedControlError> { + grant.take_entry()?; + let (file, created) = if let Some(stamp) = entry.stamp { + let f = locks::open_fixed_control(path, Kind::File, true, scratch)?; + stamp.recheck(&f)?; + (f, false) + } else { + let name = path + .file_name() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .as_encoded_bytes(); + (parent.create_new(name)?, true) + }; + let guard = FixedLockedFile::try_lock(file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + Ok((guard, created)) +} + +fn init_coordination( + file: &File, + image: &[u8; locks::COORDINATION_HEADER_BYTES], + grant: &mut MutationGrant, +) -> Result<(), FixedControlError> { + if file.metadata()?.len() != 0 { + return Err(invalid(Problem::InvalidRecord)); + } + write_range(file, 0, image, grant) +} +fn init_owner_coordination( + file: &File, + image: &[u8; locks::COORDINATION_HEADER_BYTES], + grant: &mut MutationGrant, +) -> Result<(), FixedControlError> { + init_coordination(file, image, grant)?; + #[cfg(windows)] + { + grant.take_entries(2)?; + locks::set_created_file_nonce(file, image[8..40].try_into().unwrap())?; + grant.check()?; + } + Ok(()) +} + +pub(crate) struct LegacySourceOwner { + state_stamp: FixedFileStamp, + state_pin: File, + gate_pin: File, + gate_header: [u8; locks::COORDINATION_HEADER_BYTES], + artifact_header: [u8; locks::COORDINATION_HEADER_BYTES], + owner_header: [u8; locks::COORDINATION_HEADER_BYTES], + commit: codec::CommitFact, + owner: FixedLockedFile, + staging: bool, + pending_publication: bool, + _root_pin: File, + binding: Binding, + record: Record, + state_path: PathBuf, + gate_path: PathBuf, + _floor: MemoryReservation, +} +/// A closed operational source version, not program origin or deletion proof. +#[derive(Clone, Copy)] +pub(crate) struct LegacyClosedVersion { + root: FileIdentity, + state: FileIdentity, + gate: FileIdentity, + nonce: [u8; 32], + version: u64, + job: [u8; 32], + phase: LegacyPhase, + member: FileIdentity, + database: Option, + manifest: Option, + cache: FileIdentity, +} +impl LegacyClosedVersion { + pub(crate) fn root_identity(&self) -> FileIdentity { + self.root + } + pub(crate) fn member_identity(&self) -> FileIdentity { + self.member + } + pub(crate) fn phase(&self) -> LegacyPhase { + self.phase + } + pub(crate) fn cache_identity(&self) -> FileIdentity { + self.cache + } + pub(crate) fn database_stamp(&self) -> Option { + self.database + } + pub(crate) fn manifest_stamp(&self) -> Option { + self.manifest + } + pub(crate) fn context_digest(&self) -> [u8; 32] { + let mut bytes = [0; 320]; + id(&mut bytes[0..16], self.root); + id(&mut bytes[16..32], self.state); + id(&mut bytes[32..48], self.gate); + bytes[48..80].copy_from_slice(&self.nonce); + bytes[80..88].copy_from_slice(&self.version.to_le_bytes()); + bytes[88..120].copy_from_slice(&self.job); + bytes[120] = match self.phase { + LegacyPhase::Active => 0, + LegacyPhase::Preparing => 1, + LegacyPhase::Retiring => 2, + LegacyPhase::Removed => 3, + }; + id(&mut bytes[128..144], self.member); + if let Some(stamp) = self.database { + stamp.encode_checkpoint((&mut bytes[144..224]).try_into().unwrap()); + } + if let Some(stamp) = self.manifest { + stamp.encode_checkpoint((&mut bytes[224..304]).try_into().unwrap()); + } + id(&mut bytes[304..320], self.cache); + Sha256::digest(bytes).into() + } +} +impl LegacySourceOwner { + pub(crate) fn closed_version(&self) -> LegacyClosedVersion { + LegacyClosedVersion { + root: self.binding.root, + state: self.binding.state, + gate: self.binding.gate, + nonce: self.binding.nonce, + version: self.record.version, + job: self.record.job, + phase: self.record.phase, + member: self.record.member, + database: self.record.database, + manifest: self.record.manifest, + cache: self.record.cache, + } + } + pub(crate) fn is_current(&self, memory: &CleanupMemory) -> Result { + let _floor = memory.reserve(24576).map_err(FixedControlError::Resource)?; + let parent = self + .state_path + .parent() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + let root = RootedDirectory::open_readonly_root(parent, self.binding.root)?; + let gate = locks::try_open_existing_locked(&self.gate_path, LockMode::Shared) + .map_err(|_| invalid(Problem::Changed))? + .ok_or_else(|| invalid(Problem::Busy))?; + let administrative = self.binding.kind == LegacyScopeKind::ManagedAdministration; + let gate_name = if self.staging { + STAGING_GATE + } else if administrative { + ADMIN_GATE + } else { + GATE + }; + let state_name = if self.staging { + STAGING_STATE + } else if administrative { + ADMIN_STATE + } else { + STATE + }; + let owner_name = if self.staging { + STAGING_OWNER + } else if administrative { + ADMIN_OWNER + } else { + OWNER + }; + let gate_file = root.open_readonly_file(gate_name)?; + let state = root.open_readonly_file(state_name)?; + let _ = owner_name; + let current = self.matches_current_files(&root, &gate_file, &state); + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + if let Err(error) = ¤t { + eprintln!( + "legacy source owner validation failed at {}: {error:?}", + self.state_path.display() + ); + } + } + let matches = current.is_ok() + && gate.identity().map_err(|_| invalid(Problem::Changed))? == self.binding.gate; + Ok(matches) + } +} + +impl PreparedLegacyScope { + pub(crate) fn artifact_root(&self) -> &Path { + &self.artifact + } + pub(crate) fn database_path(&self) -> &Path { + &self.database + } + pub(crate) fn manifest_path(&self) -> &Path { + &self.manifest + } +} +/// Administrative enrollment and source closure. The returned owner retains +/// only the dedicated janitor lifecycle lease; all reader-facing exclusives +/// are dropped before the caller performs association or any payload scan. +fn with_legacy_enrollment_inner( + prepared: &mut PreparedLegacyScope, + confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + cohort: Option<&AcceptedLegacyRootCohort>, + mut automatic: Option<&mut super::maintenance_ownership::PreparedAutomaticDirect>, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result<(R, LegacySourceOwner), FixedControlError> { + let administrative = prepared.kind == LegacyScopeKind::ManagedAdministration; + if automatic.is_some() && prepared.kind != LegacyScopeKind::DefaultDirect { + return Err(invalid(Problem::Changed)); + } + if prepared.publication_recovery_pending() { + prepared.recover_publication(None, grant, memory)?; + prepared.enrollment_waiting = true; + return Err(invalid(Problem::Busy)); + } + if let Some(cohort) = cohort { + let allowed = match prepared.kind { + LegacyScopeKind::ManagedGeneration => cohort.matches(prepared, memory)?, + LegacyScopeKind::DefaultDirect => { + cohort.current(memory)? + && cohort.default_source.is_some() + && (cohort.default_source == prepared.confirmation_source + || prepared.observation.as_ref().is_some_and(|o| { + o.observed.record.phase == LegacyPhase::Active + && o.observed.record.operator_pending + && o.observed.record.root_default_source == cohort.default_source + && o.observed.record.operator_generation + == cohort.operator_generation + })) + } + _ => false, + }; + if !allowed { + return Err(invalid(Problem::Changed)); + } + } + grant.check()?; + if grant.remaining_entries() < 48 { + return Err(FixedControlError::Grant( + super::maintenance_budget::GrantExhausted::Entries, + )); + } + if !prepared.binding.is_current()? { + return Err(invalid(Problem::Changed)); + } + if confirmation.is_some_and(|c| c.binding_digest() != prepared.binding.digest()) { + return Err(invalid(Problem::Changed)); + } + if prepared.kind == LegacyScopeKind::DefaultDirect + && confirmation.is_some_and(|c| c.source() != prepared.confirmation_source.as_ref()) + { + return Err(invalid(Problem::Changed)); + } + + let floor = memory + .reserve( + std::mem::size_of::() + + 24576 + + prepared.root.as_os_str().as_encoded_bytes().len() * 4, + ) + .map_err(FixedControlError::Resource)?; + let scratch = &mut prepared.scratch; + let root_file = checked_entry(&prepared.root, prepared.entries[0], scratch, grant)?; + let repository_file = checked_entry(&prepared.repository, prepared.entries[1], scratch, grant)?; + let artifact_file = checked_entry(&prepared.artifact, prepared.entries[2], scratch, grant)?; + let member_file = checked_entry(&prepared.member, prepared.entries[3], scratch, grant)?; + let root = RootedDirectory::open_fixed( + &prepared.root, + FixedFileStamp::capture(&root_file, Kind::Directory)?.identity(), + true, + scratch, + )?; + let artifact = RootedDirectory::open_fixed( + &prepared.artifact, + FixedFileStamp::capture(&artifact_file, Kind::Directory)?.identity(), + true, + scratch, + )?; + let member = RootedDirectory::open_fixed( + &prepared.member, + FixedFileStamp::capture(&member_file, Kind::Directory)?.identity(), + true, + scratch, + )?; + // Missing administrative controls require the actual captured human request. + let gate_entry = Entry::capture(&prepared.paths[0], Kind::File, true, scratch)?; + let state_entry = Entry::capture(&prepared.paths[1], Kind::File, true, scratch)?; + let owner_entry = Entry::capture(&prepared.paths[2], Kind::File, true, scratch)?; + if (gate_entry.stamp.is_none() || state_entry.stamp.is_none() || owner_entry.stamp.is_none()) + && confirmation.is_none() + && cohort.is_none() + && automatic.is_none() + { + return Err(invalid(Problem::InvalidRecord)); + } + let (writer, _) = lock_entry(&prepared.writer, prepared.entries[6], &root, scratch, grant)?; + // The actual writer excludes pointer publication. Compare the prepared + // noncurrent selection before accepting even a pending source enrollment. + if let Some((path, entry)) = &prepared.administration_selector { + drop(checked_entry(path, *entry, scratch, grant)?); + } + if let Some((path, entry)) = &prepared.enrollment_runs { + grant.take_entry()?; + match ( + locks::open_fixed_control(path, Kind::Directory, false, scratch), + entry.stamp, + ) { + (Ok(file), Some(stamp)) => stamp.recheck(&file)?, + (Err(FixedControlError::Io(e)), None) if e.kind() == std::io::ErrorKind::NotFound => {} + (Err(error), _) => return Err(error), + _ => return Err(invalid(Problem::Changed)), + } + } + let (gate, new_gate) = lock_entry(&prepared.paths[0], gate_entry, &root, scratch, grant)?; + if new_gate { + init_coordination(gate.file(), &prepared.coordination[0], grant)?; + } + let (owner, new_owner) = lock_entry(&prepared.paths[2], owner_entry, &root, scratch, grant)?; + if new_owner { + init_owner_coordination(owner.file(), &prepared.coordination[1], grant)?; + } + if raw_coordination(gate.file())? != prepared.coordination[0] + || raw_coordination(owner.file())? != prepared.coordination[1] + { + return Err(invalid(Problem::Changed)); + } + let gate_nonce = prepared.nonce; + let owner_nonce = prepared.owner_nonce; + let mut early_guards = if let Some(capability) = automatic.as_mut() { + let (family, _) = lock_entry( + &prepared.family, + prepared.entries[7], + &member, + scratch, + grant, + )?; + let (native, _) = lock_entry( + &prepared.native, + prepared.entries[8], + &member, + scratch, + grant, + )?; + let (root_use, _) = lock_entry( + &prepared.root_use, + prepared.entries[9], + &artifact, + scratch, + grant, + )?; + capability.verify(&root, &artifact, &writer, &root_use, grant)?; + Some((family, native, root_use)) + } else { + None + }; + grant.take_entry()?; + let (state, new_state) = if let Some(stamp) = state_entry.stamp { + let file = locks::open_fixed_control(&prepared.paths[1], Kind::File, true, scratch)?; + stamp.recheck(&file)?; + (file, false) + } else { + ( + root.create_new(if administrative { ADMIN_STATE } else { STATE })?, + true, + ) + }; + let mut binding = Binding { + kind: prepared.kind, + root: root.identity(), + gate: FixedFileStamp::capture(gate.file(), Kind::File)?.identity(), + state: FixedFileStamp::capture(&state, Kind::File)?.identity(), + owner: FixedFileStamp::capture(owner.file(), Kind::File)?.identity(), + nonce: gate_nonce, + owner_nonce, + key: prepared.key, + database: [0; 256], + database_len: 0, + cache: [0; 256], + cache_len: 0, + repository: FixedFileStamp::capture(&repository_file, Kind::Directory)?.identity(), + }; + let database_name = prepared + .database + .file_name() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .as_encoded_bytes(); + let cache_name = prepared + .artifact + .file_name() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .as_encoded_bytes(); + if !component(database_name) || !component(cache_name) { + return Err(invalid(Problem::InvalidRecord)); + } + binding.database[..database_name.len()].copy_from_slice(database_name); + binding.database_len = database_name.len(); + binding.cache[..cache_name.len()].copy_from_slice(cache_name); + binding.cache_len = cache_name.len(); + let mut record = Record { + phase: LegacyPhase::Active, + version: 1, + member: member.identity(), + database: prepared.entries[4].stamp, + manifest: prepared.entries[5].stamp, + cache: artifact.identity(), + root_use: prepared.entries[9] + .stamp + .map(|s| s.identity()) + .unwrap_or(FileIdentity::from_fixed_words(0, 0)), + confirmation: if administrative { + prepared.cohort_scope + } else { + prepared.binding.digest() + }, + job: prepared.job, + member_name: prepared.member_name, + member_len: prepared.member_len, + freeze_cache: prepared.freeze_cache && prepared.kind == LegacyScopeKind::DefaultDirect, + operator_generation: confirmation + .map(|c| c.generation()) + .unwrap_or_else(|| cohort.map(|c| c.operator_generation).unwrap_or(0)), + operator_pending: true, + pending_source_stamp: prepared.entries[2].stamp, + root_default_source: if administrative || prepared.kind == LegacyScopeKind::DefaultDirect { + confirmation + .and_then(|c| c.source().copied()) + .or_else(|| cohort.and_then(|c| c.default_source)) + } else { + None + }, + }; + // A rejected queued request must never leave an accepted permission for a + // substituted source. Repeat all request-time source facts before birth; + // the initial Active record claims permission, never quiescence. + if prepared.kind == LegacyScopeKind::DefaultDirect && confirmation.is_some() { + let file = checked_entry(&prepared.database, prepared.entries[4], scratch, grant)?; + drop(file); + let file = checked_entry(&prepared.manifest, prepared.entries[5], scratch, grant)?; + drop(file); + for (path, entry) in &prepared.confirmation_caches { + grant.take_entry()?; + match ( + locks::open_fixed_control(path, Kind::Directory, false, scratch), + entry.stamp, + ) { + (Ok(file), Some(stamp)) + if FixedFileStamp::capture(&file, Kind::Directory)?.identity() + == stamp.identity() => {} + (Err(FixedControlError::Io(e)), None) + if e.kind() == std::io::ErrorKind::NotFound => {} + _ => return Err(invalid(Problem::Changed)), + } + } + } + if new_state + && prepared.kind == LegacyScopeKind::DefaultDirect + && confirmation.is_some() + && prepared + .confirmation_source + .as_ref() + .is_none_or(|source| source.version.is_some()) + { + return Err(invalid(Problem::Changed)); + } + let mut commit = if new_state { + if prepared.entries[4].stamp.is_none() || prepared.entries[5].stamp.is_none() { + return Err(invalid(Problem::InvalidRecord)); + } + if confirmation.is_none() && cohort.is_none() && automatic.is_none() { + return Err(invalid(Problem::InvalidRecord)); + } + let birth = prepared + .birth + .as_mut() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + if let Some(confirmation) = confirmation { + if birth.capsule.mode != 1 + || birth.capsule.permission.operator_generation != confirmation.generation() + || birth.capsule.permission.root_default_source != confirmation.source().copied() + && matches!( + prepared.kind, + LegacyScopeKind::ManagedAdministration | LegacyScopeKind::DefaultDirect + ) + { + return Err(invalid(Problem::Changed)); + } + } else if automatic.is_some() { + if birth.capsule.mode != 3 { + return Err(invalid(Problem::Changed)); + } + } else if cohort.is_some() { + if birth.capsule.mode != 2 { + return Err(invalid(Problem::Changed)); + } + } else { + return Err(invalid(Problem::InvalidRecord)); + } + birth.publish_created(&root, &state, binding, record, grant)?; + return Err(invalid(Problem::Busy)); + } else if prepared.observation.is_none() { + let birth = prepared + .birth + .as_mut() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + match birth.capsule.mode { + 1 => { + if birth.capsule.permission.confirmation + != if administrative { + prepared.cohort_scope + } else { + prepared.binding.digest() + } + { + return Err(invalid(Problem::Changed)); + } + } + 2 => { + if cohort.is_none() { + return Err(invalid(Problem::InvalidRecord)); + } + } + 3 => { + if automatic.is_none() { + return Err(invalid(Problem::InvalidRecord)); + } + } + _ => return Err(invalid(Problem::InvalidRecord)), + } + birth.promote(&root, &state, gate.file(), owner.file(), grant)?; + return Err(invalid(Problem::Busy)); + } else { + let observed = prepared + .observation + .as_ref() + .ok_or_else(|| invalid(Problem::Changed))? + .verify(&root, gate.file(), owner.file(), &state)?; + if observed.binding != binding { + return Err(invalid(Problem::Changed)); + } + if prepared.kind == LegacyScopeKind::DefaultDirect && confirmation.is_some() { + let actual = super::maintenance_admission::legacy_confirmation_version_from_records( + &observed.binding, + &observed.record, + ); + if prepared + .confirmation_source + .as_ref() + .and_then(|source| source.version) + != Some(actual) + { + return Err(invalid(Problem::Changed)); + } + } + if prepared.kind == LegacyScopeKind::DefaultDirect + && cohort.is_some() + && observed.record.phase == LegacyPhase::Active + && !observed.record.operator_pending + { + return Err(invalid(Problem::Changed)); + } + if automatic.is_some() + && observed.record.phase == LegacyPhase::Active + && !observed.record.operator_pending + { + return Err(invalid(Problem::Changed)); + } + binding = observed.binding; + record = observed.record; + // Active permits upgraded producers. Reclosure captures their current + // source under all real exclusions; a closed version remains immutable. + if !(administrative || cohort.is_some() && record.phase == LegacyPhase::Removed) + && record.phase != LegacyPhase::Active + && (record.cache != artifact.identity() || record.member != member.identity()) + { + return Err(invalid(Problem::Changed)); + } + if !(administrative || cohort.is_some() && record.phase == LegacyPhase::Removed) + && record.phase != LegacyPhase::Active + && record.phase != LegacyPhase::Retiring + && (record.cache != artifact.identity() + || record.member != member.identity() + || record.database.map(|s| s.identity()) + != prepared.entries[4].stamp.map(|s| s.identity()) + || record.manifest.map(|s| s.identity()) + != prepared.entries[5].stamp.map(|s| s.identity())) + { + return Err(invalid(Problem::Changed)); + } + observed.commit + }; + if !administrative + && cohort.is_none() + && record.phase == LegacyPhase::Active + && record.operator_pending + && confirmation.is_none() + && (record.database != prepared.entries[4].stamp + || record.manifest != prepared.entries[5].stamp + || record.cache != artifact.identity() + || record.member != member.identity() + || record.pending_source_stamp != prepared.entries[2].stamp) + { + return Err(invalid(Problem::Changed)); + } + let (family, native, root_use) = if let Some(guards) = early_guards.take() { + guards + } else { + let (family, _) = lock_entry( + &prepared.family, + prepared.entries[7], + &member, + scratch, + grant, + )?; + let (native, _) = lock_entry( + &prepared.native, + prepared.entries[8], + &member, + scratch, + grant, + )?; + let (root_use, _) = lock_entry( + &prepared.root_use, + prepared.entries[9], + &artifact, + scratch, + grant, + )?; + (family, native, root_use) + }; + if let Some((path, entry)) = &prepared.administration_selector { + let file = checked_entry(path, *entry, scratch, grant)?; + drop(file); + } + let observe_payload = |path: &Path, + entry: Entry, + scratch: &mut FixedControlScratch, + grant: &mut MutationGrant| + -> Result, FixedControlError> { + if record.phase == LegacyPhase::Retiring { + return Ok(None); + } + let file = checked_entry(path, entry, scratch, grant)?; + Ok(Some(FixedFileStamp::capture(&file, Kind::File)?)) + }; + let database_stamp = observe_payload(&prepared.database, prepared.entries[4], scratch, grant)?; + let manifest_stamp = observe_payload(&prepared.manifest, prepared.entries[5], scratch, grant)?; + if prepared.kind == LegacyScopeKind::DefaultDirect && record.phase == LegacyPhase::Active { + if let Some(confirmation) = confirmation { + if confirmation.source() != prepared.confirmation_source.as_ref() { + return Err(invalid(Problem::Changed)); + } + for (path, entry) in &prepared.confirmation_caches { + grant.take_entry()?; + match ( + locks::open_fixed_control(path, Kind::Directory, false, scratch), + entry.stamp, + ) { + (Ok(file), Some(stamp)) + if FixedFileStamp::capture(&file, Kind::Directory)?.identity() + == stamp.identity() => {} + (Err(FixedControlError::Io(e)), None) + if e.kind() == std::io::ErrorKind::NotFound => {} + _ => return Err(invalid(Problem::Changed)), + } + } + } + } + if administrative && record.confirmation != prepared.cohort_scope { + return Err(invalid(Problem::Changed)); + } + if record.phase == LegacyPhase::Active + || (cohort.is_some() && record.phase == LegacyPhase::Removed) + { + if confirmation.is_none() + && cohort.is_none() + && automatic.is_none() + && (!record.operator_pending + || record.confirmation + != if administrative { + prepared.cohort_scope + } else { + prepared.binding.digest() + }) + { + return Err(invalid(Problem::InvalidRecord)); + } + if let Some(confirmation) = confirmation { + record.operator_generation = confirmation.generation(); + record.confirmation = if administrative { + prepared.cohort_scope + } else { + prepared.binding.digest() + }; + } + record.operator_pending = false; + record.pending_source_stamp = None; + record.phase = LegacyPhase::Preparing; + record.version = commit + .generation + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + record.member = member.identity(); + record.member_name = prepared.member_name; + record.member_len = prepared.member_len; + record.cache = artifact.identity(); + record.database = database_stamp; + record.manifest = manifest_stamp; + record.root_use = FixedFileStamp::capture(root_use.file(), Kind::File)?.identity(); + record.job = prepared.job; + let Some(publication) = prepared.publication.as_ref() else { + prepared.enrollment_waiting = true; + return Err(invalid(Problem::Busy)); + }; + commit = publication.publish(&state, binding, commit, record, grant)?; + } else if !matches!(record.phase, LegacyPhase::Preparing | LegacyPhase::Retiring) { + return Err(invalid(Problem::Busy)); + } + if root_use.file().metadata()?.len() == 0 { + init_coordination(root_use.file(), &prepared.coordination[2], grant)?; + } + if raw_coordination(root_use.file())? != prepared.coordination[2] { + return Err(invalid(Problem::Changed)); + } + let incarnation = prepared.coordination[2][8..40].try_into().unwrap(); + let authority = LegacyEnrollmentAuthority { + kind: prepared.kind, + directory: &root, + artifact: &artifact, + member: &member, + writer: &writer, + root_use: &root_use, + _family: &family, + _native: &native, + _gate: &gate, + _confirmation: confirmation, + incarnation, + }; + let result = apply(&authority, grant)?; + // The callback can initialize own control headers, never change source bytes. + if !administrative && record.phase == LegacyPhase::Preparing { + let file = checked_entry(&prepared.database, prepared.entries[4], scratch, grant)?; + record.database.unwrap().recheck(&file)?; + drop(file); + let file = checked_entry(&prepared.manifest, prepared.entries[5], scratch, grant)?; + record.manifest.unwrap().recheck(&file)?; + } + // Remember only coordination files genuinely created/held by this admitted + // enrollment, including controlled header initialization by the callback. + for (index, file) in [ + (6, writer.file()), + (7, family.file()), + (8, native.file()), + (9, root_use.file()), + ] { + prepared.entries[index].stamp = Some(FixedFileStamp::capture(file, Kind::File)?); + } + let _ = commit; + let owner = LegacySourceOwner { + state_stamp: FixedFileStamp::capture(&state, Kind::File)?, + state_pin: state.try_clone()?, + gate_pin: gate.file().try_clone()?, + gate_header: raw_coordination(gate.file())?, + artifact_header: prepared.coordination[2], + owner_header: raw_coordination(owner.file())?, + commit, + owner, + staging: false, + pending_publication: false, + _root_pin: root.pin_handle()?, + binding, + record, + state_path: prepared.paths[1].clone(), + gate_path: prepared.paths[0].clone(), + _floor: floor, + }; + Ok((result, owner)) +} + +/// Reacquire the actual source exclusions for a short effect. The dedicated +/// owner lease may live across preparation; reader-facing exclusions never do. +pub(crate) fn with_closed_source( + prepared: &mut PreparedLegacyScope, + owner: &LegacySourceOwner, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result { + grant.check()?; + let _floor = memory.reserve(24576).map_err(FixedControlError::Resource)?; + if prepared.kind == LegacyScopeKind::ManagedAdministration + || !prepared.binding.is_current()? + || !owner.is_current(memory)? + || owner.binding.root != prepared.entries[0].stamp.unwrap().identity() + || owner.pending_publication + || owner.record.phase == LegacyPhase::Active + { + return Err(invalid(Problem::Changed)); + } + let scratch = &mut prepared.scratch; + let root_file = checked_entry(&prepared.root, prepared.entries[0], scratch, grant)?; + let artifact_file = checked_entry(&prepared.artifact, prepared.entries[2], scratch, grant)?; + let member_file = checked_entry(&prepared.member, prepared.entries[3], scratch, grant)?; + let root = RootedDirectory::open( + &prepared.root, + FixedFileStamp::capture(&root_file, Kind::Directory)?.identity(), + )?; + let artifact = RootedDirectory::open( + &prepared.artifact, + FixedFileStamp::capture(&artifact_file, Kind::Directory)?.identity(), + )?; + let member = RootedDirectory::open( + &prepared.member, + FixedFileStamp::capture(&member_file, Kind::Directory)?.identity(), + )?; + let (writer, _) = lock_entry(&prepared.writer, prepared.entries[6], &root, scratch, grant)?; + let gate_file = locks::open_fixed_control(&prepared.paths[0], Kind::File, true, scratch)?; + let gate = FixedLockedFile::try_lock(gate_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + let state = locks::open_fixed_control(&prepared.paths[1], Kind::File, true, scratch)?; + owner.matches_current_files(&root, gate.file(), &state)?; + let (family, _) = lock_entry( + &prepared.family, + prepared.entries[7], + &member, + scratch, + grant, + )?; + let (native, _) = lock_entry( + &prepared.native, + prepared.entries[8], + &member, + scratch, + grant, + )?; + let (root_use, _) = lock_entry( + &prepared.root_use, + prepared.entries[9], + &artifact, + scratch, + grant, + )?; + if FixedFileStamp::capture(root_use.file(), Kind::File)?.identity() != owner.record.root_use { + return Err(invalid(Problem::Changed)); + } + if raw_coordination(root_use.file())? != owner.artifact_header { + return Err(invalid(Problem::Changed)); + } + if owner.record.phase == LegacyPhase::Preparing { + let db = checked_entry(&prepared.database, prepared.entries[4], scratch, grant)?; + owner + .record + .database + .ok_or_else(|| invalid(Problem::Changed))? + .recheck(&db)?; + drop(db); + let manifest = checked_entry(&prepared.manifest, prepared.entries[5], scratch, grant)?; + owner + .record + .manifest + .ok_or_else(|| invalid(Problem::Changed))? + .recheck(&manifest)?; + } + let authority = LegacyEnrollmentAuthority { + kind: prepared.kind, + directory: &root, + artifact: &artifact, + member: &member, + writer: &writer, + root_use: &root_use, + _family: &family, + _native: &native, + _gate: &gate, + _confirmation: None, + incarnation: owner.artifact_header[8..40].try_into().unwrap(), + }; + let result = apply(&authority, grant)?; + grant.check()?; + Ok(result) +} + +/// Abort is explicit, fallible and admitted. No filesystem mutation occurs in +/// Drop, and a stale worker cannot reopen a newer source version. +fn transition_source( + prepared: &mut PreparedLegacyScope, + owner: &mut LegacySourceOwner, + next: LegacyPhase, + grant: &mut MutationGrant, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + let mut updated = owner.record; + updated.version = updated + .version + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + updated.phase = next; + updated.operator_pending = false; + updated.pending_source_stamp = None; + let publication = prepared + .publication + .take() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + let result = with_closed_source(prepared, owner, grant, memory, &mut |authority, grant| { + let state = authority.directory.open_existing(STATE)?; + publication.publish(&state, owner.binding, owner.commit, updated, grant) + }); + prepared.publication = Some(publication); + owner.commit = result?; + owner.record = updated; + owner.state_stamp = FixedFileStamp::capture(&owner.state_pin, Kind::File)?; + Ok(()) +} +/// Matching producer facts may advance a genuinely closed source into effects. +/// Actual destination admission/reference exclusion remains with the consumer. +pub(crate) fn begin_legacy_retirement( + prepared: &mut PreparedLegacyScope, + owner: &mut LegacySourceOwner, + association: &super::maintenance_legacy_association::LegacyAssociation, + grant: &mut MutationGrant, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + if owner.is_administrative() + || owner.record.phase != LegacyPhase::Preparing + || owner.record.database != Some(association.database_stamp()) + || owner.record.manifest != Some(association.manifest_stamp()) + || owner.record.cache != association.artifact_root_identity() + { + return Err(invalid(Problem::Changed)); + } + transition_source(prepared, owner, LegacyPhase::Retiring, grant, memory) +} + +/// Orphan staging is an independent source scope; absent DB/manifest fields are +/// represented explicitly and cannot satisfy the pair enrollment interface. +pub(crate) struct PreparedLegacyStagingScope { + root: PathBuf, + staging: PathBuf, + paths: [PathBuf; 3], + root_stamp: FixedFileStamp, + staging_stamp: FixedFileStamp, + cohort_scope: [u8; 32], + repository: FileIdentity, + binding: super::maintenance_budget::MemoryBox, + nonce: [u8; 32], + owner_nonce: [u8; 32], + coordination: [[u8; locks::COORDINATION_HEADER_BYTES]; 3], + enrollment_waiting: bool, + birth: Option>, + observation: Option>, + publication: Option< + super::maintenance_budget::MemoryBox< + super::maintenance_legacy_guard::PreparedSourcePublication, + >, + >, + job: [u8; 32], + scratch: super::maintenance_budget::MemoryBox, + _floor: MemoryReservation, +} +pub(crate) struct LegacyStagingEnrollmentAuthority<'a> { + root: &'a RootedDirectory, + staging: &'a RootedDirectory, + _gate: &'a FixedLockedFile, + version: LegacyClosedVersion, +} +impl LegacyStagingEnrollmentAuthority<'_> { + pub(crate) fn staging_directory(&self) -> &RootedDirectory { + self.staging + } + pub(crate) fn closed_version(&self) -> LegacyClosedVersion { + self.version + } +} +// Preserve standalone value callers; joined-worker staging uses the charged +// core directly so no raw scope Result/value is copied above source observation. +#[allow(dead_code)] +pub(crate) fn prepare_legacy_staging_scope( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + staging_root: &Path, + memory: &CleanupMemory, +) -> Result { + prepare_legacy_staging_scope_boxed(target, binding, staging_root, memory) + .map(super::maintenance_budget::MemoryBox::into_inner) +} + +#[inline(never)] +fn staging_binding_boxed( + binding: &PreparedTargetBinding, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let binding = binding.duplicate(memory)?; + super::maintenance_budget::MemoryBox::new(binding, memory).map_err(FixedControlError::Resource) +} + +/// Identical independent staging-source preparation behind its charged owner; +/// parent/admin observations supply no origin or payload deletion authority. +#[inline(never)] +pub(crate) fn prepare_legacy_staging_scope_boxed( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + staging_root: &Path, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let charge = std::mem::size_of::() + .checked_add(32768) + .and_then(|v| { + v.checked_add( + target + .state_root + .as_os_str() + .as_encoded_bytes() + .len() + .checked_mul(8)?, + ) + }) + .ok_or(FixedControlError::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?; + let floor = memory + .reserve(charge) + .map_err(FixedControlError::Resource)?; + if target.state_root != target.repository_root.join(".codebaseGraph") + || staging_root != target.state_root.join("native-staging") + { + return Err(invalid(Problem::InvalidRecord)); + } + let mut scratch = source_observation_scratch_boxed(memory)?; + let root = locks::open_fixed_control(&target.state_root, Kind::Directory, false, &mut scratch)?; + let staging = locks::open_fixed_control(staging_root, Kind::Directory, false, &mut scratch)?; + let repository = locks::open_fixed_control( + &target.repository_root, + Kind::Directory, + false, + &mut scratch, + )?; + let mut nonce = [0; 32]; + let mut owner_nonce = [0; 32]; + let mut job = [0; 32]; + for bytes in [&mut nonce, &mut owner_nonce, &mut job] { + getrandom::fill(bytes).map_err(|_| invalid(Problem::InvalidRecord))?; + } + let configured = super::maintenance_projection::project_binding(target, memory) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let cohort_scope = legacy_cohort_scope( + target, + &configured.storage, + &configured.storage.join("artifacts"), + memory, + )?; + let paths = [ + target + .state_root + .join(std::str::from_utf8(STAGING_GATE).unwrap()), + target + .state_root + .join(std::str::from_utf8(STAGING_STATE).unwrap()), + target + .state_root + .join(std::str::from_utf8(STAGING_OWNER).unwrap()), + ]; + nonce = existing_coordination_nonce(&paths[0])?.unwrap_or(nonce); + owner_nonce = existing_coordination_nonce(&paths[2])?.unwrap_or(owner_nonce); + let coordination = [ + locks::encode_coordination_incarnation(nonce), + locks::encode_coordination_incarnation(owner_nonce), + locks::encode_coordination_incarnation(job), + ]; + let observation = PreparedSourceObservation::read(&target.state_root, &paths, None, memory)?; + super::maintenance_budget::MemoryBox::new( + PreparedLegacyStagingScope { + coordination, + publication: None, + cohort_scope, + root: target.state_root.clone(), + staging: staging_root.to_owned(), + enrollment_waiting: false, + birth: None, + observation, + paths, + root_stamp: FixedFileStamp::capture(&root, Kind::Directory)?, + staging_stamp: FixedFileStamp::capture(&staging, Kind::Directory)?, + repository: FixedFileStamp::capture(&repository, Kind::Directory)?.identity(), + binding: staging_binding_boxed(binding, memory)?, + nonce, + owner_nonce, + job, + scratch, + _floor: floor, + }, + memory, + ) + .map_err(FixedControlError::Resource) +} +fn with_legacy_staging_enrollment_inner( + prepared: &mut PreparedLegacyStagingScope, + confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + cohort: Option<&AcceptedLegacyRootCohort>, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyStagingEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result<(R, LegacySourceOwner), FixedControlError> { + if prepared.publication_recovery_pending() { + prepared.recover_publication(None, grant, memory)?; + prepared.enrollment_waiting = true; + return Err(invalid(Problem::Busy)); + } + if let Some(cohort) = cohort { + if prepared.cohort_scope != cohort.scope || !cohort.current(memory)? { + return Err(invalid(Problem::Changed)); + } + } + grant.check()?; + if !prepared.binding.is_current()? + || confirmation.is_some_and(|c| c.binding_digest() != prepared.binding.digest()) + { + return Err(invalid(Problem::Changed)); + } + let floor = memory + .reserve( + std::mem::size_of::() + + 24576 + + prepared.root.as_os_str().as_encoded_bytes().len() * 4, + ) + .map_err(FixedControlError::Resource)?; + let root = RootedDirectory::open_fixed( + &prepared.root, + prepared.root_stamp.identity(), + true, + &mut prepared.scratch, + )?; + let staging = RootedDirectory::open_fixed( + &prepared.staging, + prepared.staging_stamp.identity(), + true, + &mut prepared.scratch, + )?; + let gate_entry = Entry::capture(&prepared.paths[0], Kind::File, true, &mut prepared.scratch)?; + let state_entry = Entry::capture(&prepared.paths[1], Kind::File, true, &mut prepared.scratch)?; + let owner_entry = Entry::capture(&prepared.paths[2], Kind::File, true, &mut prepared.scratch)?; + if (state_entry.stamp.is_none() || owner_entry.stamp.is_none()) + && confirmation.is_none() + && cohort.is_none() + { + return Err(invalid(Problem::InvalidRecord)); + } + let (gate, created) = lock_entry( + &prepared.paths[0], + gate_entry, + &root, + &mut prepared.scratch, + grant, + )?; + if created { + init_coordination(gate.file(), &prepared.coordination[0], grant)?; + } + if raw_coordination(gate.file())? != prepared.coordination[0] { + return Err(invalid(Problem::Changed)); + } + let nonce = prepared.nonce; + let (owner, created) = lock_entry( + &prepared.paths[2], + owner_entry, + &root, + &mut prepared.scratch, + grant, + )?; + if created { + init_owner_coordination(owner.file(), &prepared.coordination[1], grant)?; + } + if raw_coordination(owner.file())? != prepared.coordination[1] { + return Err(invalid(Problem::Changed)); + } + let owner_nonce = prepared.owner_nonce; + grant.take_entry()?; + let (state, created) = match state_entry.stamp { + Some(stamp) => { + let file = locks::open_fixed_control( + &prepared.paths[1], + Kind::File, + true, + &mut prepared.scratch, + )?; + stamp.recheck(&file)?; + (file, false) + } + None => (root.create_new(STAGING_STATE)?, true), + }; + let mut cache = [0; 256]; + cache[..14].copy_from_slice(b"native-staging"); + let expected = Binding { + kind: LegacyScopeKind::NativeStaging, + root: root.identity(), + gate: FixedFileStamp::capture(gate.file(), Kind::File)?.identity(), + state: FixedFileStamp::capture(&state, Kind::File)?.identity(), + owner: FixedFileStamp::capture(owner.file(), Kind::File)?.identity(), + nonce, + owner_nonce, + key: [0; 64], + database: [0; 256], + database_len: 0, + cache, + cache_len: 14, + repository: prepared.repository, + }; + let (binding, mut record, mut commit) = if created { + if confirmation.is_none() && cohort.is_none() { + return Err(invalid(Problem::InvalidRecord)); + } + let record = Record { + phase: LegacyPhase::Active, + version: 1, + member: staging.identity(), + database: None, + manifest: None, + cache: staging.identity(), + root_use: expected.gate, + confirmation: prepared.binding.digest(), + job: prepared.job, + member_name: [0; 128], + member_len: 0, + freeze_cache: false, + operator_generation: confirmation + .map(|c| c.generation()) + .unwrap_or_else(|| cohort.map(|c| c.operator_generation).unwrap_or(0)), + operator_pending: true, + pending_source_stamp: Some(prepared.staging_stamp), + root_default_source: None, + }; + let birth = prepared + .birth + .as_mut() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + if let Some(confirmation) = confirmation { + if birth.capsule.mode != 1 + || birth.capsule.permission.operator_generation != confirmation.generation() + { + return Err(invalid(Problem::Changed)); + } + } else if cohort.is_none() || birth.capsule.mode != 2 { + return Err(invalid(Problem::InvalidRecord)); + } + birth.publish_created(&root, &state, expected, record, grant)?; + return Err(invalid(Problem::Busy)); + } else if prepared.observation.is_none() { + let birth = prepared + .birth + .as_mut() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + if birth.capsule.mode == 1 { + if birth.capsule.permission.confirmation != prepared.binding.digest() { + return Err(invalid(Problem::Changed)); + } + } else if birth.capsule.mode != 2 || cohort.is_none() { + return Err(invalid(Problem::InvalidRecord)); + } + birth.promote(&root, &state, gate.file(), owner.file(), grant)?; + return Err(invalid(Problem::Busy)); + } else { + let o = prepared + .observation + .as_ref() + .ok_or_else(|| invalid(Problem::Changed))? + .verify(&root, gate.file(), owner.file(), &state)?; + if o.binding != expected + || o.record.member != staging.identity() + || o.record.database.is_some() + || o.record.manifest.is_some() + { + return Err(invalid(Problem::Changed)); + } + (o.binding, o.record, o.commit) + }; + if record.phase == LegacyPhase::Active + && record.operator_pending + && confirmation.is_none() + && record.pending_source_stamp != Some(prepared.staging_stamp) + { + return Err(invalid(Problem::Changed)); + } + if record.phase == LegacyPhase::Active { + if confirmation.is_none() + && cohort.is_none() + && (!record.operator_pending || record.confirmation != prepared.binding.digest()) + { + return Err(invalid(Problem::InvalidRecord)); + } + if let Some(confirmation) = confirmation { + record.operator_generation = confirmation.generation(); + record.confirmation = prepared.binding.digest(); + } + record.operator_pending = false; + record.pending_source_stamp = None; + record.phase = LegacyPhase::Preparing; + record.version = record + .version + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + record.job = prepared.job; + let Some(publication) = prepared.publication.as_ref() else { + prepared.enrollment_waiting = true; + return Err(invalid(Problem::Busy)); + }; + commit = publication.publish(&state, binding, commit, record, grant)?; + } else if record.phase != LegacyPhase::Preparing { + return Err(invalid(Problem::Busy)); + } + let owner = LegacySourceOwner { + state_stamp: FixedFileStamp::capture(&state, Kind::File)?, + state_pin: state.try_clone()?, + gate_pin: gate.file().try_clone()?, + gate_header: raw_coordination(gate.file())?, + artifact_header: prepared.coordination[2], + owner_header: raw_coordination(owner.file())?, + commit, + owner, + staging: true, + pending_publication: false, + _root_pin: root.pin_handle()?, + binding, + record, + state_path: prepared.paths[1].clone(), + gate_path: prepared.paths[0].clone(), + _floor: floor, + }; + let authority = LegacyStagingEnrollmentAuthority { + root: &root, + staging: &staging, + _gate: &gate, + version: owner.closed_version(), + }; + let result = apply(&authority, grant)?; + let _ = commit; + Ok((result, owner)) +} + +pub(crate) fn with_closed_staging_source( + prepared: &mut PreparedLegacyStagingScope, + owner: &LegacySourceOwner, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyStagingEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result { + grant.check()?; + let _floor = memory.reserve(24576).map_err(FixedControlError::Resource)?; + if !owner.staging + || owner.pending_publication + || owner.record.phase == LegacyPhase::Active + || !prepared.binding.is_current()? + || !owner.is_current(memory)? + { + return Err(invalid(Problem::Changed)); + } + let root = RootedDirectory::open_fixed( + &prepared.root, + prepared.root_stamp.identity(), + true, + &mut prepared.scratch, + )?; + let staging = RootedDirectory::open_fixed( + &prepared.staging, + prepared.staging_stamp.identity(), + true, + &mut prepared.scratch, + )?; + grant.take_entry()?; + let gate_file = + locks::open_fixed_control(&prepared.paths[0], Kind::File, true, &mut prepared.scratch)?; + let gate = FixedLockedFile::try_lock(gate_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + let state = root.open_existing(STAGING_STATE)?; + owner.matches_current_files(&root, gate.file(), &state)?; + apply( + &LegacyStagingEnrollmentAuthority { + root: &root, + staging: &staging, + _gate: &gate, + version: owner.closed_version(), + }, + grant, + ) +} +#[cfg(test)] +pub(crate) fn abort_legacy_staging_preparation( + prepared: &mut PreparedLegacyStagingScope, + owner: &mut LegacySourceOwner, + grant: &mut MutationGrant, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + if owner.record.phase != LegacyPhase::Preparing { + return Err(invalid(Problem::InvalidRecord)); + } + transition_staging_source(prepared, owner, LegacyPhase::Active, grant, memory) +} +fn transition_staging_source( + prepared: &mut PreparedLegacyStagingScope, + owner: &mut LegacySourceOwner, + next: LegacyPhase, + grant: &mut MutationGrant, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + let mut updated = owner.record; + updated.phase = next; + updated.operator_pending = false; + updated.pending_source_stamp = None; + updated.version = updated + .version + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + let publication = prepared + .publication + .take() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + let result = + with_closed_staging_source(prepared, owner, grant, memory, &mut |authority, grant| { + let state = authority.root.open_existing(STAGING_STATE)?; + publication.publish(&state, owner.binding, owner.commit, updated, grant) + }); + prepared.publication = Some(publication); + owner.commit = result?; + owner.record = updated; + owner.state_stamp = FixedFileStamp::capture(&owner.state_pin, Kind::File)?; + Ok(()) +} +pub(crate) fn begin_legacy_staging_retirement( + prepared: &mut PreparedLegacyStagingScope, + owner: &mut LegacySourceOwner, + facts: &super::maintenance_legacy_staging::StagingAssociation, + grant: &mut MutationGrant, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + if owner.record.phase != LegacyPhase::Preparing + || facts.root_identity() != owner.record.member + || facts.repository_identity() != owner.binding.repository + || facts.source_context() != owner.closed_version().context_digest() + { + return Err(invalid(Problem::Changed)); + } + transition_staging_source(prepared, owner, LegacyPhase::Retiring, grant, memory) +} + +/// Resumption reacquires a real dedicated owner lease; serialized versions +/// alone remain facts and never become authority. No source version is relabeled. +fn reopen_owner( + root_path: &Path, + paths: &[PathBuf; 3], + expected: &LegacyClosedVersion, + staging: bool, + memory: &CleanupMemory, +) -> Result { + let floor = memory + .reserve( + std::mem::size_of::() + + 24576 + + root_path.as_os_str().as_encoded_bytes().len() * 4, + ) + .map_err(FixedControlError::Resource)?; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(FixedControlError::Resource)?, + )?; + let root = RootedDirectory::open_fixed(root_path, expected.root, false, &mut scratch)?; + let gate_file = locks::open_fixed_control(&paths[0], Kind::File, false, &mut scratch)?; + let gate = FixedLockedFile::try_lock(gate_file, LockMode::Shared)? + .ok_or_else(|| invalid(Problem::Busy))?; + let owner_file = locks::open_fixed_control(&paths[2], Kind::File, true, &mut scratch)?; + let owner = FixedLockedFile::try_lock(owner_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + let state = locks::open_fixed_control(&paths[1], Kind::File, false, &mut scratch)?; + let (observed, _) = super::maintenance_legacy_guard::prepare_source_observation( + &root, + gate.file(), + &state, + paths[2] + .file_name() + .map(|n| n.as_encoded_bytes()) + .ok_or_else(|| invalid(Problem::InvalidRecord))?, + owner.file(), + memory, + )?; + let result = LegacySourceOwner { + state_stamp: observed.state_stamp, + state_pin: state.try_clone()?, + gate_pin: gate.file().try_clone()?, + gate_header: raw_coordination(gate.file())?, + artifact_header: if staging { + [0; locks::COORDINATION_HEADER_BYTES] + } else { + let artifact = root + .open_directory_readonly(&observed.binding.cache[..observed.binding.cache_len])?; + raw_coordination(&artifact.open_readonly_file(b".use.lock")?)? + }, + owner_header: raw_coordination(owner.file())?, + commit: observed.commit, + owner, + staging, + pending_publication: observed.pending, + _root_pin: root.pin_handle()?, + binding: observed.binding, + record: observed.record, + state_path: paths[1].clone(), + gate_path: paths[0].clone(), + _floor: floor, + }; + if result.closed_version().context_digest() != expected.context_digest() + || FixedFileStamp::capture(result.owner.file(), Kind::File)?.identity() + != result.binding.owner + || locks::read_coordination_incarnation(result.owner.file()) + .map_err(|_| invalid(Problem::InvalidRecord))? + != Some(result.binding.owner_nonce) + { + return Err(invalid(Problem::Changed)); + } + Ok(result) +} +pub(crate) fn reopen_legacy_source_owner( + prepared: &mut PreparedLegacyScope, + expected: &LegacyClosedVersion, + memory: &CleanupMemory, +) -> Result { + if !prepared.binding.is_current()? { + return Err(invalid(Problem::Changed)); + } + reopen_owner(&prepared.root, &prepared.paths, expected, false, memory) +} +pub(crate) fn reopen_legacy_staging_owner( + prepared: &mut PreparedLegacyStagingScope, + expected: &LegacyClosedVersion, + memory: &CleanupMemory, +) -> Result { + if !prepared.binding.is_current()? { + return Err(invalid(Problem::Changed)); + } + reopen_owner(&prepared.root, &prepared.paths, expected, true, memory) +} +impl LegacyClosedVersion { + pub(crate) fn encode_checkpoint(&self, out: &mut [u8; 512]) { + out.fill(0); + out[..8].copy_from_slice(b"CGLGCV01"); + out[8] = match self.phase { + LegacyPhase::Active => 0, + LegacyPhase::Preparing => 1, + LegacyPhase::Retiring => 2, + LegacyPhase::Removed => 3, + }; + out[9] = u8::from(self.database.is_some()) | (u8::from(self.manifest.is_some()) << 1); + id(&mut out[16..32], self.root); + id(&mut out[32..48], self.state); + id(&mut out[48..64], self.gate); + out[64..96].copy_from_slice(&self.nonce); + out[96..104].copy_from_slice(&self.version.to_le_bytes()); + out[104..136].copy_from_slice(&self.job); + id(&mut out[136..152], self.member); + if let Some(s) = self.database { + s.encode_checkpoint((&mut out[152..232]).try_into().unwrap()); + } + if let Some(s) = self.manifest { + s.encode_checkpoint((&mut out[232..312]).try_into().unwrap()); + } + id(&mut out[312..328], self.cache); + super::maintenance_legacy_guard::checksum(out); + } + pub(crate) fn decode_checkpoint(bytes: &[u8; 512]) -> Result { + if &bytes[..8] != b"CGLGCV01" + || !super::maintenance_legacy_guard::checked(bytes) + || bytes[9] > 3 + || bytes[10..16] + .iter() + .chain(bytes[328..480].iter()) + .any(|b| *b != 0) + { + return Err(invalid(Problem::InvalidRecord)); + } + let phase = match bytes[8] { + 1 => LegacyPhase::Preparing, + 2 => LegacyPhase::Retiring, + 3 => LegacyPhase::Removed, + _ => return Err(invalid(Problem::InvalidRecord)), + }; + let stamp = + |present: bool, wire: &[u8]| -> Result, FixedControlError> { + if present { + Ok(Some(FixedFileStamp::decode_checkpoint( + wire.try_into().unwrap(), + )?)) + } else if wire.iter().any(|b| *b != 0) { + Err(invalid(Problem::InvalidRecord)) + } else { + Ok(None) + } + }; + let result = Self { + root: super::maintenance_legacy_guard::read_id(&bytes[16..32]), + state: super::maintenance_legacy_guard::read_id(&bytes[32..48]), + gate: super::maintenance_legacy_guard::read_id(&bytes[48..64]), + nonce: bytes[64..96].try_into().unwrap(), + version: u64::from_le_bytes(bytes[96..104].try_into().unwrap()), + job: bytes[104..136].try_into().unwrap(), + phase, + member: super::maintenance_legacy_guard::read_id(&bytes[136..152]), + database: stamp(bytes[9] & 1 != 0, &bytes[152..232])?, + manifest: stamp(bytes[9] & 2 != 0, &bytes[232..312])?, + cache: super::maintenance_legacy_guard::read_id(&bytes[312..328]), + }; + if result.version == 0 { + return Err(invalid(Problem::InvalidRecord)); + } + Ok(result) + } +} +impl LegacyClosedVersion { + /// Links a current retirement phase to the unchanged original preparation + /// source. This does not rename either version or validate payload facts. + pub(crate) fn same_job_source(&self, original: &Self) -> bool { + let phase_ok = (self.phase == original.phase && self.version == original.version) + || (original.phase == LegacyPhase::Preparing + && self.phase == LegacyPhase::Retiring + && original.version.checked_add(1) == Some(self.version)) + || (self.phase == LegacyPhase::Removed + && ((original.phase == LegacyPhase::Preparing + && original.version.checked_add(2) == Some(self.version)) + || (original.phase == LegacyPhase::Retiring + && original.version.checked_add(1) == Some(self.version)))); + phase_ok + && self.root == original.root + && self.state == original.state + && self.gate == original.gate + && self.nonce == original.nonce + && self.job == original.job + && self.member == original.member + && self.database == original.database + && self.manifest == original.manifest + && self.cache == original.cache + } +} + +#[cfg(test)] +mod tests { + use super::super::maintenance_budget::MemoryBox; + use super::*; + use std::cell::RefCell; + use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }; + fn target() -> MaintenanceTarget { + let mut nonce = [0; 16]; + getrandom::fill(&mut nonce).unwrap(); + let root = std::env::temp_dir().join(format!( + "legacy-enrollment-{:032x}", + u128::from_le_bytes(nonce) + )); + let state = root.join(".codebaseGraph"); + std::fs::create_dir_all(state.join("storage/artifacts")).unwrap(); + std::fs::create_dir(state.join("artifacts")).unwrap(); + std::fs::create_dir(state.join("native-staging")).unwrap(); + std::fs::write(state.join("config.json"), br#"{"schema_version":3}"#).unwrap(); + let root = std::fs::canonicalize(root).unwrap(); + let state = root.join(".codebaseGraph"); + let target = MaintenanceTarget { + repository_root: root, + state_root: state, + storage_root: None, + direct_pair: None, + config_path: None, + }; + let pair = default_direct(&target); + std::fs::write( + &pair.database, + b"control exclusion fixture, not origin evidence", + ) + .unwrap(); + std::fs::write(&pair.manifest, b"{}").unwrap(); + target + } + #[test] + fn dc_legacy_real_dispatcher_claim_enrolls_and_explicit_rebuild_invalidates_original_owner() { + struct Work { + scope: MemoryBox, + memory: CleanupMemory, + pair: DirectPair, + owner: Option, + done: bool, + } + let target = target(); + let cleanup = target.repository_root.clone(); + let seen = Arc::new(AtomicBool::new(false)); + let output = seen.clone(); + let request_target = target.clone(); + // A legacy target challenge belongs to the retained runtime witness; + // recapturing it would generate a new challenge on every preparation. + let mut retained_binding: Option> = None; + let snapshot = super::super::maintenance_scheduler::with_claimed_legacy_confirmation( + Some(&request_target), + Arc::new(crate::coordinator::ExecutionDispatcher::default()), + move |memory, request, parked: Option>>| { + let resuming = parked.is_some(); + let work = if let Some(work) = parked { + work + } else { + let binding = retained_binding.get_or_insert_with(|| { + MemoryBox::new( + super::super::maintenance_admission::prepare_target_binding( + &target, memory, + ) + .unwrap(), + memory, + ) + .unwrap() + }); + let pair = default_direct(&target); + let scope = prepare_legacy_scope( + &target, + binding, + LegacyScope::DefaultDirect { + pair: &pair, + artifact_root: &target.state_root.join("artifacts"), + }, + true, + memory, + ) + .unwrap(); + MemoryBox::new( + RefCell::new(Work { + scope, + memory: memory.clone(), + pair, + owner: None, + done: false, + }), + memory, + ) + .unwrap() + }; + let binding; + { + let mut work = work.borrow_mut(); + let Work { + scope, + memory, + pair, + owner, + done, + } = &mut *work; + binding = + super::super::maintenance_scheduler::TargetBinding(scope.binding.digest()); + if let Some(owner) = owner.as_ref() { + // All codec work and the actual primary rebuild hook run + // after the previous collector permit has been released. + let original = owner.closed_version(); + let mut wire = [0; 512]; + original.encode_checkpoint(&mut wire); + let restored = LegacyClosedVersion::decode_checkpoint(&wire).unwrap(); + assert!(restored.same_job_source(&original)); + assert!(super::super::maintenance_legacy_guard::before_native_open( + &pair.database + ) + .is_err()); + let writer = locks::open_locked( + DirectLayout::new(&pair.database, &pair.manifest).writer_lock_path(), + LockMode::Exclusive, + ) + .unwrap(); + super::super::maintenance_legacy_guard::reactivate_legacy_source( + &pair.database, + &pair.manifest, + &writer, + ) + .unwrap(); + drop(writer); + assert!(!owner.is_current(memory).unwrap()); + assert!(super::super::maintenance_legacy_guard::before_native_open( + &pair.database + ) + .unwrap() + .is_some()); + *done = true; + output.store(true, Ordering::Release); + } else if resuming { + scope.refresh_enrollment_preparation(memory).unwrap(); + } else if request.is_some() { + scope.prepare_enrollment_metadata(request, memory).unwrap(); + } + } + Ok((binding, work)) + }, + move |work, token, grant| { + let mut work = work.borrow_mut(); + let Work { + scope, + memory, + owner, + done, + .. + } = &mut *work; + if *done { + return Ok(super::super::maintenance_scheduler::AttemptOutcome::Complete); + } + match with_legacy_enrollment(scope, token, grant, memory, &mut |_, _| Ok(())) { + Ok((_, actual)) => { + *owner = Some(actual); + Ok(super::super::maintenance_scheduler::AttemptOutcome::Pending) + } + Err( + FixedControlError::Invalid(Problem::Busy) | FixedControlError::Grant(_), + ) if scope.enrollment_pending() => { + Ok(super::super::maintenance_scheduler::AttemptOutcome::Pending) + } + Err(error) => panic!("actual source enrollment failed: {error:?}"), + } + }, + ); + assert!(seen.load(Ordering::Acquire), "{snapshot:?}"); + assert_eq!( + snapshot.last_attempt.unwrap().outcome, + super::super::maintenance_scheduler::AttemptOutcome::Complete + ); + std::fs::remove_dir_all(cleanup).unwrap(); + } + #[test] + fn dc_legacy_real_dispatcher_claim_closes_and_aborts_only_staging_scope() { + struct Work { + scope: MemoryBox, + memory: CleanupMemory, + path: PathBuf, + owner: Option, + done: bool, + } + let target = target(); + let cleanup = target.repository_root.clone(); + let seen = Arc::new(AtomicBool::new(false)); + let output = seen.clone(); + let request_target = target.clone(); + // A legacy target challenge belongs to the retained runtime witness; + // recapturing it would generate a new challenge on every preparation. + let mut retained_binding: Option> = None; + let snapshot = super::super::maintenance_scheduler::with_claimed_legacy_confirmation( + Some(&request_target), + Arc::new(crate::coordinator::ExecutionDispatcher::default()), + move |memory, request, parked: Option>>| { + let resuming = parked.is_some(); + let work = if let Some(work) = parked { + work + } else { + let binding = retained_binding.get_or_insert_with(|| { + MemoryBox::new( + super::super::maintenance_admission::prepare_target_binding( + &target, memory, + ) + .unwrap(), + memory, + ) + .unwrap() + }); + let path = target.state_root.join("native-staging"); + let scope = + prepare_legacy_staging_scope(&target, binding, &path, memory).unwrap(); + MemoryBox::new( + RefCell::new(Work { + scope: MemoryBox::new(scope, memory).unwrap(), + memory: memory.clone(), + path, + owner: None, + done: false, + }), + memory, + ) + .unwrap() + }; + let binding; + { + let mut work = work.borrow_mut(); + let Work { + scope, + memory, + path, + owner, + done, + } = &mut *work; + binding = + super::super::maintenance_scheduler::TargetBinding(scope.binding.digest()); + if let Some(owner) = owner.as_ref() { + if owner.closed_version().phase() == LegacyPhase::Active { + assert!( + super::super::maintenance_legacy_guard::before_staging_producer( + path + ) + .unwrap() + .is_some() + ); + *done = true; + output.store(true, Ordering::Release); + } else { + assert!( + super::super::maintenance_legacy_guard::before_staging_producer( + path + ) + .is_err() + ); + scope + .prepare_transition(owner, LegacyPhase::Active, memory) + .unwrap(); + } + } else if resuming { + scope.refresh_enrollment_preparation(memory).unwrap(); + } else if request.is_some() { + scope.prepare_enrollment_metadata(request, memory).unwrap(); + } + } + Ok((binding, work)) + }, + move |work, token, grant| { + let mut work = work.borrow_mut(); + let Work { + scope, + memory, + owner, + done, + .. + } = &mut *work; + if *done { + return Ok(super::super::maintenance_scheduler::AttemptOutcome::Complete); + } + if let Some(owner) = owner.as_mut() { + abort_legacy_staging_preparation(scope, owner, grant, memory).unwrap(); + return Ok(super::super::maintenance_scheduler::AttemptOutcome::Pending); + } + match with_legacy_staging_enrollment( + scope, + token, + grant, + memory, + &mut |_, _| Ok(()), + ) { + Ok((_, actual)) => { + *owner = Some(actual); + Ok(super::super::maintenance_scheduler::AttemptOutcome::Pending) + } + Err( + FixedControlError::Invalid(Problem::Busy) | FixedControlError::Grant(_), + ) if scope.enrollment_pending() => { + Ok(super::super::maintenance_scheduler::AttemptOutcome::Pending) + } + Err(error) => panic!("actual staging enrollment failed: {error:?}"), + } + }, + ); + assert!(seen.load(Ordering::Acquire), "{snapshot:?}"); + assert_eq!( + snapshot.last_attempt.unwrap().outcome, + super::super::maintenance_scheduler::AttemptOutcome::Complete + ); + std::fs::remove_dir_all(cleanup).unwrap(); + } +} + +#[inline(never)] +fn source_observation_scratch_boxed( + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(FixedControlError::Resource)?, + )?; + super::maintenance_budget::MemoryBox::new(scratch, memory).map_err(FixedControlError::Resource) +} +#[inline(never)] +fn accepted_source_observation( + root_path: &Path, + paths: &[PathBuf; 3], + _staging: bool, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let _floor = memory.reserve(24576).map_err(FixedControlError::Resource)?; + let mut scratch = source_observation_scratch_boxed(memory)?; + let root_file = match locks::open_fixed_control(root_path, Kind::Directory, false, &mut scratch) + { + Ok(f) => f, + Err(FixedControlError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(None) + } + Err(e) => return Err(e), + }; + let root = RootedDirectory::open_fixed( + root_path, + FixedFileStamp::capture(&root_file, Kind::Directory)?.identity(), + false, + &mut scratch, + )?; + let gate = match locks::open_fixed_control(&paths[0], Kind::File, false, &mut scratch) { + Ok(f) => f, + Err(FixedControlError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(None) + } + Err(e) => return Err(e), + }; + let gate = + FixedLockedFile::try_lock(gate, LockMode::Shared)?.ok_or_else(|| invalid(Problem::Busy))?; + let state = match locks::open_fixed_control(&paths[1], Kind::File, false, &mut scratch) { + Ok(f) => f, + Err(FixedControlError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(None) + } + Err(e) => return Err(e), + }; + match super::maintenance_legacy_guard::prepare_source_observation_boxed( + &root, + gate.file(), + &state, + paths[2] + .file_name() + .map(|n| n.as_encoded_bytes()) + .ok_or_else(|| invalid(Problem::InvalidRecord))?, + memory, + ) { + Ok(data) => super::maintenance_budget::MemoryBox::new( + (data.observed.binding, data.observed.record), + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource), + Err(error) => { + if let Some(capsule) = + super::maintenance_legacy_guard::read_creation_capsule(&root, &state)? + { + if locks::created_file_nonce(&state)? == Some(capsule.permission.job) { + return super::maintenance_budget::MemoryBox::new( + (capsule.template, capsule.permission), + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource); + } + } + match error { + FixedControlError::Invalid(Problem::Busy) => Err(invalid(Problem::Busy)), + _ => Ok(None), + } + } + } +} +impl PreparedLegacyScope { + pub(crate) fn has_accepted_job( + &mut self, + memory: &CleanupMemory, + ) -> Result { + if !self.binding.is_current()? { + return Ok(false); + } + let Some(accepted) = accepted_source_observation(&self.root, &self.paths, false, memory)? + else { + return Ok(false); + }; + let (binding, record) = *accepted; + if self.kind == LegacyScopeKind::ManagedAdministration { + return Ok(binding.kind == self.kind + && binding.root == self.entries[0].stamp.unwrap().identity() + && record.cache == self.entries[2].stamp.unwrap().identity() + && record.confirmation == self.cohort_scope + && matches!(record.phase, LegacyPhase::Preparing | LegacyPhase::Active)); + } + if binding.kind != self.kind + || binding.key != self.key + || binding.root != self.entries[0].stamp.unwrap().identity() + || self.entries[3] + .stamp + .is_some_and(|s| record.member != s.identity()) + || (self.entries[3].stamp.is_none() && record.phase != LegacyPhase::Retiring) + || (self.kind == LegacyScopeKind::ManagedGeneration + && record.member_name[..record.member_len] != self.member_name[..self.member_len]) + || record.cache != self.entries[2].stamp.unwrap().identity() + { + return Ok(false); + } + let same = + record.database == self.entries[4].stamp && record.manifest == self.entries[5].stamp; + Ok(match record.phase { + LegacyPhase::Preparing => same, + LegacyPhase::Retiring => true, + LegacyPhase::Active => { + same && record.operator_pending + && record.confirmation == self.binding.digest() + && record.pending_source_stamp == self.entries[2].stamp + } + LegacyPhase::Removed => false, + }) + } +} +impl PreparedLegacyStagingScope {} + +/// Completion authority deliberately has no fabricated member/native guard when +/// a previous admitted removal already made that member or its locks absent. +pub(crate) struct LegacyCompletionAuthority<'a> { + root: &'a RootedDirectory, + parent: &'a RootedDirectory, + member_name: &'a [u8], + _writer: &'a FixedLockedFile, + _gate: &'a FixedLockedFile, + artifact_use: Option<&'a FixedLockedFile>, +} +impl LegacyCompletionAuthority<'_> { + pub(crate) fn directory(&self) -> &RootedDirectory { + self.root + } + pub(crate) fn member_parent(&self) -> &RootedDirectory { + self.parent + } + pub(crate) fn member_name(&self) -> &[u8] { + self.member_name + } + pub(crate) fn artifact_use_guard(&self) -> Option<&FixedLockedFile> { + self.artifact_use + } +} +pub(crate) fn with_legacy_root_completion( + prepared: &mut PreparedLegacyScope, + owner: &LegacySourceOwner, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyCompletionAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result { + let _floor = memory.reserve(32768).map_err(FixedControlError::Resource)?; + if !prepared.binding.is_current()? + || !owner.is_current(memory)? + || owner.pending_publication + || owner.record.phase != LegacyPhase::Retiring + { + return Err(invalid(Problem::Changed)); + } + let scratch = &mut prepared.scratch; + let root = RootedDirectory::open_fixed(&prepared.root, owner.binding.root, true, scratch)?; + let (writer, _) = lock_entry(&prepared.writer, prepared.entries[6], &root, scratch, grant)?; + let gate = FixedLockedFile::try_lock(root.open_existing(GATE)?, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + let state = root.open_existing(STATE)?; + owner.matches_current_files(&root, gate.file(), &state)?; + let artifact = + match RootedDirectory::open_fixed(&prepared.artifact, owner.record.cache, true, scratch) { + Ok(d) => Some(d), + Err(FixedControlError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e), + }; + let artifact_use = if let Some(artifact) = artifact.as_ref() { + let guard = + FixedLockedFile::try_lock(artifact.open_existing(b".use.lock")?, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + if FixedFileStamp::capture(guard.file(), Kind::File)?.identity() != owner.record.root_use { + return Err(invalid(Problem::Changed)); + } + Some(guard) + } else { + None + }; + let parent = if prepared.kind == LegacyScopeKind::ManagedGeneration { + root.open_directory(b"generations")? + } else { + RootedDirectory::open_fixed(&prepared.root, owner.binding.root, true, scratch)? + }; + let name = if prepared.kind == LegacyScopeKind::ManagedGeneration { + &owner.record.member_name[..owner.record.member_len] + } else { + &owner.binding.database[..owner.binding.database_len] + }; + apply( + &LegacyCompletionAuthority { + root: &root, + parent: &parent, + member_name: name, + _writer: &writer, + _gate: &gate, + artifact_use: artifact_use.as_ref(), + }, + grant, + ) +} +/// Operational completion only: no data is removed here. The role's opaque +/// cohort/journal proof authorizes its own callback; this final publication +/// checks actual named absence again under the retained exclusion kernel. +pub(crate) fn finish_legacy_retirement( + prepared: &mut PreparedLegacyScope, + owner: &mut LegacySourceOwner, + grant: &mut MutationGrant, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + let mut next = owner.record; + next.phase = LegacyPhase::Removed; + next.version = next + .version + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + let publication = prepared + .publication + .take() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + let result = + with_legacy_root_completion(prepared, owner, grant, memory, &mut |authority, grant| { + let absent = |result: std::io::Result| -> Result<(), FixedControlError> { + match result { + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), + Err(e) => Err(e.into()), + Ok(_) => Err(invalid(Problem::Changed)), + } + }; + if owner.binding.kind == LegacyScopeKind::ManagedGeneration { + match authority + .parent + .open_directory_readonly(authority.member_name) + { + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(e.into()), + Ok(_) => return Err(invalid(Problem::Changed)), + } + } else { + absent( + authority + .root + .open_readonly_file(&owner.binding.database[..owner.binding.database_len]), + )?; + absent(authority.root.open_readonly_file(b"manifest.json"))?; + } + let state = authority.root.open_existing(STATE)?; + publication.publish(&state, owner.binding, owner.commit, next, grant) + }); + prepared.publication = Some(publication); + owner.commit = result?; + owner.record = next; + owner.state_stamp = FixedFileStamp::capture(&owner.state_pin, Kind::File)?; + Ok(()) +} +fn observed_version(binding: Binding, record: Record) -> LegacyClosedVersion { + LegacyClosedVersion { + root: binding.root, + state: binding.state, + gate: binding.gate, + nonce: binding.nonce, + version: record.version, + job: record.job, + phase: record.phase, + member: record.member, + database: record.database, + manifest: record.manifest, + cache: record.cache, + } +} +pub(crate) fn observe_legacy_source_version( + prepared: &mut PreparedLegacyScope, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + if !prepared.binding.is_current()? { + return Ok(None); + } + let Some(accepted) = + accepted_source_observation(&prepared.root, &prepared.paths, false, memory)? + else { + return Ok(None); + }; + let (binding, record) = *accepted; + if binding.kind != prepared.kind + || binding.root != prepared.entries[0].stamp.unwrap().identity() + || binding.key != prepared.key + || !matches!( + record.phase, + LegacyPhase::Preparing | LegacyPhase::Retiring | LegacyPhase::Removed + ) + { + return Ok(None); + } + Ok(Some(observed_version(binding, record))) +} +pub(crate) fn observe_legacy_staging_source_version( + prepared: &mut PreparedLegacyStagingScope, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + if !prepared.binding.is_current()? { + return Ok(None); + } + let Some(accepted) = + accepted_source_observation(&prepared.root, &prepared.paths, true, memory)? + else { + return Ok(None); + }; + let (binding, record) = *accepted; + if binding.kind != LegacyScopeKind::NativeStaging + || binding.root != prepared.root_stamp.identity() + || record.member != prepared.staging_stamp.identity() + || !matches!( + record.phase, + LegacyPhase::Preparing | LegacyPhase::Retiring | LegacyPhase::Removed + ) + { + return Ok(None); + } + Ok(Some(observed_version(binding, record))) +} + +fn legacy_cohort_scope( + target: &MaintenanceTarget, + store: &Path, + artifact: &Path, + memory: &CleanupMemory, +) -> Result<[u8; 32], FixedControlError> { + let _floor = memory.reserve(8192).map_err(FixedControlError::Resource)?; + let configured = super::maintenance_projection::project_binding(target, memory) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(FixedControlError::Resource)?, + )?; + let mut hash = Sha256::new(); + hash.update(b"CGLegacyRootCohortScope01"); + for path in [ + &target.repository_root, + &target.state_root, + &configured.storage, + ] { + hash.update(path.as_os_str().as_encoded_bytes()); + hash.update([0]); + } + for path in [store, artifact] { + let file = locks::open_fixed_control(path, Kind::Directory, false, &mut scratch)?; + let identity = FixedFileStamp::capture(&file, Kind::Directory)?.identity(); + let (a, b) = identity.fixed_words(); + hash.update(a.to_le_bytes()); + hash.update(b.to_le_bytes()); + } + for identity in [configured.repository, configured.state] { + let (a, b) = identity.fixed_words(); + hash.update(a.to_le_bytes()); + hash.update(b.to_le_bytes()); + } + hash.update(configured.config_path.as_os_str().as_encoded_bytes()); + if let Some(stamp) = configured.config_stamp { + hash.update(stamp.binding_digest()); + } + Ok(hash.finalize().into()) +} +/// Only replay of genuine root-administration controls plus verified future-use +/// coverage constructs this capability. It carries no payload origin or finality. +pub(crate) struct AcceptedLegacyRootCohort { + target_binding: std::cell::RefCell>, + gate_header: [u8; locks::COORDINATION_HEADER_BYTES], + owner_header: [u8; locks::COORDINATION_HEADER_BYTES], + artifact_identity: FileIdentity, + operator_generation: u64, + scope: [u8; 32], + binding: Binding, + record_stamp: FixedFileStamp, + cohort_stamp: FixedFileStamp, + scope_stamp: FixedFileStamp, + default_source: Option, + root: PathBuf, + _root_pin: File, + _state_pin: File, + _artifact_pin: File, + _floor: MemoryReservation, +} +impl AcceptedLegacyRootCohort { + fn matches( + &self, + prepared: &PreparedLegacyScope, + memory: &CleanupMemory, + ) -> Result { + if prepared.cohort_scope != self.scope + || prepared.entries[0].stamp.map(|s| s.identity()) != Some(self.binding.root) + { + return Ok(false); + } + self.current(memory) + } + fn current(&self, memory: &CleanupMemory) -> Result { + let _floor = memory.reserve(24576).map_err(FixedControlError::Resource)?; + if !self.target_binding.borrow_mut().is_current()? { + return Ok(false); + } + let root = RootedDirectory::open_readonly_root(&self.root, self.binding.root)?; + let gate = locks::try_open_existing_locked( + &self.root.join(std::str::from_utf8(ADMIN_GATE).unwrap()), + LockMode::Shared, + ) + .map_err(|_| invalid(Problem::Changed))? + .ok_or_else(|| invalid(Problem::Busy))?; + let gate_file = root.open_readonly_file(ADMIN_GATE)?; + let state = root.open_readonly_file(ADMIN_STATE)?; + self.record_stamp.recheck(&state)?; + self.record_stamp.recheck(&self._state_pin)?; + let owner = root.open_readonly_file(ADMIN_OWNER)?; + if FixedFileStamp::capture(&gate_file, Kind::File)?.identity() != self.binding.gate + || FixedFileStamp::capture(&owner, Kind::File)?.identity() != self.binding.owner + || gate.identity().map_err(|_| invalid(Problem::Changed))? != self.binding.gate + || raw_coordination(&gate_file)? != self.gate_header + || raw_coordination(&owner)? != self.owner_header + { + return Ok(false); + } + let artifact = root.open_directory_readonly(b"artifacts")?; + if artifact.identity() != self.artifact_identity { + return Ok(false); + } + self.cohort_stamp + .recheck(&artifact.open_readonly_file(b".use-cohort.json")?)?; + self.scope_stamp + .recheck(&artifact.open_readonly_file(b".publication.binding")?)?; + Ok(true) + } +} +// Preserve the existing value API for standalone callers; worker preparation +// returns the charged opaque cohort directly without copying its raw Result. +#[allow(dead_code)] +pub(crate) fn observe_accepted_root_cohort( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + observe_accepted_root_cohort_boxed(target, binding, memory) + .map(|cohort| cohort.map(super::maintenance_budget::MemoryBox::into_inner)) +} + +#[inline(never)] +fn duplicate_cohort_binding_boxed( + binding: &PreparedTargetBinding, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let binding = binding.duplicate(memory)?; + super::maintenance_budget::MemoryBox::new(binding, memory).map_err(FixedControlError::Resource) +} + +/// Same administrative enrollment-only observations, placed behind a charged +/// owner before returning to any joined-worker preparation stage. +#[inline(never)] +pub(crate) fn observe_accepted_root_cohort_boxed( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + memory: &CleanupMemory, +) -> Result>, FixedControlError> +{ + let floor = memory + .reserve( + std::mem::size_of::() + + 65536 + + target.repository_root.as_os_str().as_encoded_bytes().len() * 16, + ) + .map_err(FixedControlError::Resource)?; + let mut current = duplicate_cohort_binding_boxed(binding, memory)?; + if !current.is_current()? { + return Ok(None); + } + let configured = super::maintenance_projection::project_binding(target, memory) + .map_err(|_| invalid(Problem::InvalidRecord))?; + if configured.direct.is_some() { + return Ok(None); + } + let root_path = configured.storage; + let root = RootedDirectory::open_readonly_root( + &root_path, + locks::path_identity(&root_path).map_err(|_| invalid(Problem::Changed))?, + )?; + let state = match root.open_readonly_file(ADMIN_STATE) { + Ok(f) => f, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + let gate = match root.open_readonly_file(ADMIN_GATE) { + Ok(f) => f, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + let observed = match super::maintenance_legacy_guard::observe_files_named( + &root, + &gate, + &state, + ADMIN_OWNER, + ) { + Ok(o) => o, + Err(_) => return Ok(None), + }; + if observed.binding.kind != LegacyScopeKind::ManagedAdministration + || observed.record.phase != LegacyPhase::Preparing + { + return Ok(None); + } + let artifact = root.open_directory_readonly(b"artifacts")?; + if artifact.identity() != observed.record.cache { + return Ok(None); + } + let scope = legacy_cohort_scope(target, &root_path, &root_path.join("artifacts"), memory)?; + if scope != observed.record.confirmation { + return Ok(None); + } + let layout = super::layout::ManagedLayout::new(&root_path); + let domain = super::maintenance_ownership::ReferenceDomain::managed(&layout) + .map_err(|_| invalid(Problem::Changed))?; + if !matches!( + super::maintenance_ownership::observe_reference_epoch(&domain) + .map_err(|_| invalid(Problem::Changed))?, + super::maintenance_ownership::EpochObservation::Verified(_) + ) { + return Ok(None); + } + let cohort_stamp = FixedFileStamp::capture( + &artifact.open_readonly_file(b".use-cohort.json")?, + Kind::File, + )?; + let scope_stamp = FixedFileStamp::capture( + &artifact.open_readonly_file(b".publication.binding")?, + Kind::File, + )?; + super::maintenance_budget::MemoryBox::new( + AcceptedLegacyRootCohort { + target_binding: std::cell::RefCell::new(duplicate_cohort_binding_boxed( + binding, memory, + )?), + gate_header: raw_coordination(&gate)?, + owner_header: raw_coordination(&root.open_readonly_file(ADMIN_OWNER)?)?, + artifact_identity: artifact.identity(), + operator_generation: observed.record.operator_generation, + scope, + binding: observed.binding, + record_stamp: observed.state_stamp, + cohort_stamp, + scope_stamp, + default_source: observed.record.root_default_source, + root: root_path, + _root_pin: root.pin_handle()?, + _state_pin: state, + _artifact_pin: artifact.pin_handle()?, + _floor: floor, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) +} +pub(crate) fn with_legacy_enrollment( + prepared: &mut PreparedLegacyScope, + confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result<(R, LegacySourceOwner), FixedControlError> { + with_legacy_enrollment_inner(prepared, confirmation, None, None, grant, memory, apply) +} +pub(crate) fn with_legacy_enrollment_in_cohort( + prepared: &mut PreparedLegacyScope, + cohort: &AcceptedLegacyRootCohort, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result<(R, LegacySourceOwner), FixedControlError> { + with_legacy_enrollment_inner(prepared, None, Some(cohort), None, grant, memory, apply) +} +impl LegacySourceOwner { + pub(crate) fn is_administrative(&self) -> bool { + self.binding.kind == LegacyScopeKind::ManagedAdministration + } +} + +#[cfg(test)] +pub(crate) fn with_legacy_staging_enrollment( + prepared: &mut PreparedLegacyStagingScope, + confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyStagingEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result<(R, LegacySourceOwner), FixedControlError> { + with_legacy_staging_enrollment_inner(prepared, confirmation, None, grant, memory, apply) +} +pub(crate) fn with_legacy_staging_enrollment_in_cohort( + prepared: &mut PreparedLegacyStagingScope, + cohort: &AcceptedLegacyRootCohort, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyStagingEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result<(R, LegacySourceOwner), FixedControlError> { + with_legacy_staging_enrollment_inner(prepared, None, Some(cohort), grant, memory, apply) +} + +pub(crate) fn finish_legacy_staging_cohort( + prepared: &mut PreparedLegacyStagingScope, + owner: &mut LegacySourceOwner, + done: &super::maintenance_legacy_engine::LegacyStagingCohortDone, + grant: &mut MutationGrant, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + if owner.record.phase != LegacyPhase::Retiring + || !owner + .closed_version() + .same_job_source(done.original_source()) + || done.directory_stamp().identity() != owner.record.member + { + return Err(invalid(Problem::Changed)); + } + with_closed_staging_source(prepared, owner, grant, memory, &mut |authority, grant| { + grant.take_entry()?; + done.directory_stamp() + .recheck(&authority.staging_directory().pin_handle()?)?; + Ok(()) + })?; + transition_staging_source(prepared, owner, LegacyPhase::Removed, grant, memory) +} + +impl super::maintenance_ownership::administrative_coverage_seal::Sealed + for LegacyEnrollmentAuthority<'_> +{ +} +impl super::maintenance_ownership::AdministrativeCoverageAuthority + for LegacyEnrollmentAuthority<'_> +{ + fn directory(&self) -> &RootedDirectory { + self.directory() + } + fn artifact_directory(&self) -> &RootedDirectory { + self.artifact_directory() + } + fn writer_identity(&self) -> Result { + self.writer_identity() + } + fn artifact_use_identity(&self) -> Result { + self.artifact_use_identity() + } + fn proposed_artifact_incarnation(&self) -> [u8; 32] { + self.proposed_artifact_incarnation() + } + fn is_administrative(&self) -> bool { + self.kind == LegacyScopeKind::ManagedAdministration + } +} + +pub(crate) fn reopen_completed_legacy_staging( + prepared: &mut PreparedLegacyStagingScope, + owner: &mut LegacySourceOwner, + grant: &mut MutationGrant, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + if owner.record.phase != LegacyPhase::Removed { + return Err(invalid(Problem::Changed)); + } + transition_staging_source(prepared, owner, LegacyPhase::Active, grant, memory) +} + +fn raw_coordination( + file: &File, +) -> Result<[u8; locks::COORDINATION_HEADER_BYTES], FixedControlError> { + let mut header = [0; locks::COORDINATION_HEADER_BYTES]; + locks::read_fixed_range(file, 0, &mut header)?; + Ok(header) +} +impl LegacySourceOwner { + fn matches_current_files( + &self, + root: &RootedDirectory, + gate: &File, + state: &File, + ) -> Result<(), FixedControlError> { + for (name, file) in [("named state", state), ("retained state", &self.state_pin)] { + if let Err(error) = self.state_stamp.recheck(file) { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "legacy {name} stamp failed at {}: expected {:?}, actual {:?}: {error:?}", + self.state_path.display(), + self.state_stamp, + FixedFileStamp::capture(file, Kind::File) + ); + } + let _ = name; + return Err(error); + } + } + let owner_name = if self.staging { + STAGING_OWNER + } else if self.binding.kind == LegacyScopeKind::ManagedAdministration { + ADMIN_OWNER + } else { + OWNER + }; + let named_owner = root.open_metadata_file(owner_name)?; + if FixedFileStamp::capture(&named_owner, Kind::File)?.identity() != self.binding.owner + || FixedFileStamp::capture(&self.gate_pin, Kind::File)?.identity() != self.binding.gate + { + return Err(invalid(Problem::Changed)); + } + if root.identity() != self.binding.root + || FixedFileStamp::capture(gate, Kind::File)?.identity() != self.binding.gate + || FixedFileStamp::capture(self.owner.file(), Kind::File)?.identity() + != self.binding.owner + || raw_coordination(gate)? != self.gate_header + || raw_coordination(self.owner.file())? != self.owner_header + || self.owner.mode() != LockMode::Exclusive + { + return Err(invalid(Problem::Changed)); + } + Ok(()) + } +} +impl LegacySourceOwner {} + +impl PreparedLegacyScope { + pub(crate) fn prepare_transition( + &mut self, + owner: &LegacySourceOwner, + next: LegacyPhase, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + if !owner.is_current(memory)? { + return Err(invalid(Problem::Changed)); + } + let mut after = owner.record; + after.phase = next; + after.operator_pending = false; + after.pending_source_stamp = None; + after.version = after + .version + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + self.publication = Some( + super::maintenance_legacy_guard::PreparedSourcePublication::prepare( + owner.binding, + owner.commit, + after, + memory, + )?, + ); + Ok(()) + } +} +impl PreparedLegacyStagingScope { + pub(crate) fn prepare_transition( + &mut self, + owner: &LegacySourceOwner, + next: LegacyPhase, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + if !owner.is_current(memory)? { + return Err(invalid(Problem::Changed)); + } + let mut after = owner.record; + after.phase = next; + after.operator_pending = false; + after.pending_source_stamp = None; + after.version = after + .version + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + self.publication = Some( + super::maintenance_legacy_guard::PreparedSourcePublication::prepare( + owner.binding, + owner.commit, + after, + memory, + )?, + ); + Ok(()) + } +} + +pub(crate) fn observe_legacy_member_name( + prepared: &mut PreparedLegacyScope, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + if prepared.kind != LegacyScopeKind::ManagedGeneration || !prepared.binding.is_current()? { + return Ok(None); + } + let Some(accepted) = + accepted_source_observation(&prepared.root, &prepared.paths, false, memory)? + else { + return Ok(None); + }; + let (binding, record) = *accepted; + if binding.kind != prepared.kind + || binding.root != prepared.entries[0].stamp.unwrap().identity() + || record.member_len == 0 + || record.member_len > 128 + { + return Ok(None); + } + Ok(Some((record.member_name, record.member_len as u8))) +} + +struct PreparedSourceObservation { + observed: super::maintenance_budget::MemoryBox, + recovery: Option< + super::maintenance_budget::MemoryBox< + super::maintenance_legacy_guard::PreparedSourceRecovery, + >, + >, + state: File, + gate: File, + owner: File, + gate_header: [u8; locks::COORDINATION_HEADER_BYTES], + owner_header: [u8; locks::COORDINATION_HEADER_BYTES], + _floor: MemoryReservation, +} +impl PreparedSourceObservation { + fn read( + root_path: &Path, + paths: &[PathBuf; 3], + held_owner: Option<&File>, + memory: &CleanupMemory, + ) -> Result>, FixedControlError> { + let floor = memory + .reserve(std::mem::size_of::() * 2 + 16384) + .map_err(FixedControlError::Resource)?; + let root = RootedDirectory::open_readonly_root( + root_path, + locks::path_identity(root_path).map_err(|_| invalid(Problem::Changed))?, + )?; + let state = match root.open_readonly_file( + paths[1] + .file_name() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .as_encoded_bytes(), + ) { + Ok(f) => f, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + let gate = root.open_readonly_file( + paths[0] + .file_name() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .as_encoded_bytes(), + )?; + let owner = root.open_metadata_file( + paths[2] + .file_name() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .as_encoded_bytes(), + )?; + let opened; + let owner_file = if let Some(held) = held_owner { + held + } else { + opened = root.open_readonly_file(paths[2].file_name().unwrap().as_encoded_bytes())?; + &opened + }; + let data = + match super::maintenance_legacy_guard::prepare_source_observation_with_owner_boxed( + &root, + &gate, + &state, + paths[2].file_name().unwrap().as_encoded_bytes(), + owner_file, + memory, + ) { + Ok(o) => o, + Err(FixedControlError::Invalid(Problem::Busy)) => { + if let Some(capsule) = + super::maintenance_legacy_guard::read_creation_capsule(&root, &state)? + { + if locks::created_file_nonce(&state)? == Some(capsule.permission.job) { + return Ok(None); + } + } + return Err(invalid(Problem::Busy)); + } + Err(_) => return Ok(None), + }; + let super::maintenance_legacy_guard::PreparedSourceObservationData { observed, recovery } = + data.into_inner(); + let gate_header = raw_coordination(&gate)?; + let owner_header = raw_coordination(owner_file)?; + super::maintenance_budget::MemoryBox::new( + Self { + observed, + recovery, + state, + gate, + owner, + gate_header, + owner_header, + _floor: floor, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) + } + fn verify( + &self, + root: &RootedDirectory, + gate: &File, + owner: &File, + state: &File, + ) -> Result<&super::maintenance_legacy_guard::Observation, FixedControlError> { + self.observed.state_stamp.recheck(state)?; + self.observed.state_stamp.recheck(&self.state)?; + if root.identity() != self.observed.binding.root + || FixedFileStamp::capture(gate, Kind::File)?.identity() != self.observed.binding.gate + || FixedFileStamp::capture(&self.gate, Kind::File)?.identity() + != self.observed.binding.gate + || FixedFileStamp::capture(&self.owner, Kind::File)?.identity() + != self.observed.binding.owner + || FixedFileStamp::capture(owner, Kind::File)?.identity() != self.observed.binding.owner + || raw_coordination(gate)? != self.gate_header + || raw_coordination(owner)? != self.owner_header + { + return Err(invalid(Problem::Changed)); + } + Ok(&self.observed) + } +} + +/// A separate upgraded-protocol lane. No user permission or root cohort is +/// fabricated: the actual guarded-creation E capability is verified under the +/// same real writer/native/root guards before any source-state publication. +pub(crate) fn with_legacy_enrollment_automatic( + prepared: &mut PreparedLegacyScope, + automatic: &mut super::maintenance_ownership::PreparedAutomaticDirect, + grant: &mut MutationGrant, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &LegacyEnrollmentAuthority<'_>, + &mut MutationGrant, + ) -> Result, +) -> Result<(R, LegacySourceOwner), FixedControlError> { + with_legacy_enrollment_inner(prepared, None, None, Some(automatic), grant, memory, apply) +} + +struct PreparedSourceBirth { + capsule: super::maintenance_legacy_guard::SourceCreationCapsule, + capsule_image: [u8; super::maintenance_legacy_guard::SOURCE_CAPSULE_BYTES], + promotion: Option>, + pending: bool, + _floor: MemoryReservation, +} +struct SourceBirthPromotion { + image: [u8; STATE_BYTES as usize], + binding: Binding, + state_stamp: FixedFileStamp, + state_pin: File, + final_nonce: [u8; 32], + _floor: MemoryReservation, +} +impl PreparedSourceBirth { + #[inline(never)] + fn prepare( + root_path: &Path, + paths: &[PathBuf; 3], + capsule: super::maintenance_legacy_guard::SourceCreationCapsule, + memory: &CleanupMemory, + ) -> Result, FixedControlError> { + let floor = memory + .reserve(std::mem::size_of::() * 2 + 16384) + .map_err(FixedControlError::Resource)?; + let capsule_image = capsule.encode(); + let root = RootedDirectory::open_readonly_root(root_path, capsule.template.root)?; + let state = match root.open_readonly_file(paths[1].file_name().unwrap().as_encoded_bytes()) + { + Ok(f) => Some(f), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e.into()), + }; + let promotion = if let Some(state) = state { + let state_before = FixedFileStamp::capture(&state, Kind::File)?; + let actual = super::maintenance_legacy_guard::read_creation_capsule(&root, &state)? + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + if actual.encode() != capsule_image + || locks::created_file_nonce(&state)? != Some(capsule.permission.job) + { + return Err(invalid(Problem::Changed)); + } + let gate = root.open_readonly_file(paths[0].file_name().unwrap().as_encoded_bytes())?; + let owner = + root.open_readonly_file(paths[2].file_name().unwrap().as_encoded_bytes())?; + let gate_before = FixedFileStamp::capture(&gate, Kind::File)?; + let owner_before = FixedFileStamp::capture(&owner, Kind::File)?; + let mut binding = capsule.template; + binding.gate = FixedFileStamp::capture(&gate, Kind::File)?.identity(); + binding.owner = FixedFileStamp::capture(&owner, Kind::File)?.identity(); + binding.state = FixedFileStamp::capture(&state, Kind::File)?.identity(); + binding.nonce = locks::read_coordination_incarnation(&gate) + .map_err(|_| invalid(Problem::Changed))? + .ok_or_else(|| invalid(Problem::Changed))?; + binding.owner_nonce = locks::read_coordination_incarnation(&owner) + .map_err(|_| invalid(Problem::Changed))? + .ok_or_else(|| invalid(Problem::Changed))?; + let mut record = capsule.permission; + if binding.kind == LegacyScopeKind::NativeStaging { + record.root_use = binding.gate; + } + let mut image = [0; STATE_BYTES as usize]; + image[..BINDING_BYTES].copy_from_slice(&binding.encode()); + let mut slot = [0; codec::SLOT_BYTES]; + let fact = codec::encode_slot(&mut slot, &binding.nonce, 1, &record.encode()) + .map_err(|_| invalid(Problem::InvalidRecord))?; + image[SLOT0 as usize..SLOT1 as usize].copy_from_slice(&slot); + codec::encode_commit( + &mut image[SELECTOR as usize..SELECTOR as usize + codec::COMMIT_BYTES], + &binding.nonce, + codec::CommitFact { + generation: 1, + active: 0, + slot_digest: fact.digest, + }, + ) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let mut raw = [0; STATE_BYTES as usize]; + locks::read_fixed_range(&state, 0, &mut raw)?; + let mut unwritten = false; + for (actual, expected) in raw.iter().zip(image.iter()) { + if *actual != *expected { + if *actual != 0 { + return Err(invalid(Problem::Changed)); + } + unwritten = true; + } else if unwritten && *actual != 0 { + return Err(invalid(Problem::Changed)); + } + } + let mut hash = Sha256::new(); + hash.update(b"CGLegacySourceBirthCommitted01"); + hash.update(capsule.permission.job); + hash.update(&image[..BINDING_BYTES]); + let final_nonce = hash.finalize().into(); + state_before.recheck(&state)?; + gate_before.recheck(&gate)?; + owner_before.recheck(&owner)?; + Some( + super::maintenance_budget::MemoryBox::new( + SourceBirthPromotion { + image, + binding, + state_stamp: state_before, + state_pin: state, + final_nonce, + _floor: memory + .reserve(std::mem::size_of::() * 2) + .map_err(FixedControlError::Resource)?, + }, + memory, + ) + .map_err(FixedControlError::Resource)?, + ) + } else { + None + }; + super::maintenance_budget::MemoryBox::new( + Self { + capsule, + capsule_image, + promotion, + pending: false, + _floor: floor, + }, + memory, + ) + .map_err(FixedControlError::Resource) + } + fn publish_created( + &mut self, + root: &RootedDirectory, + state: &File, + binding: Binding, + record: Record, + grant: &mut MutationGrant, + ) -> Result<(), FixedControlError> { + let mut template = binding; + template.gate = FileIdentity::from_fixed_words(0, 0); + template.owner = FileIdentity::from_fixed_words(0, 0); + template.state = FileIdentity::from_fixed_words(0, 0); + template.nonce = record.job; + template.owner_nonce = self.capsule.template.owner_nonce; + let mut permission = record; + permission.root_use = FileIdentity::from_fixed_words(0, 0); + if template != self.capsule.template || permission != self.capsule.permission { + return Err(invalid(Problem::Changed)); + } + if state.metadata()?.len() != 0 { + return Err(invalid(Problem::Changed)); + } + grant.take_entry()?; + locks::set_created_file_nonce(state, &record.job)?; + write_range( + state, + super::maintenance_legacy_guard::SOURCE_CAPSULE_OFFSET, + &self.capsule_image, + grant, + )?; + grant.take_entry()?; + root.sync_namespace()?; + self.pending = true; + Ok(()) + } + fn promote( + &mut self, + root: &RootedDirectory, + state: &File, + gate: &File, + owner: &File, + grant: &mut MutationGrant, + ) -> Result<(), FixedControlError> { + let promotion = self + .promotion + .as_ref() + .ok_or_else(|| invalid(Problem::Changed))?; + promotion.state_stamp.recheck(state)?; + promotion.state_stamp.recheck(&promotion.state_pin)?; + if promotion.binding.root != root.identity() + || promotion.binding.gate != FixedFileStamp::capture(gate, Kind::File)?.identity() + || promotion.binding.owner != FixedFileStamp::capture(owner, Kind::File)?.identity() + || promotion.binding.state != FixedFileStamp::capture(state, Kind::File)?.identity() + || locks::created_file_nonce(state)? != Some(self.capsule.permission.job) + { + return Err(invalid(Problem::Changed)); + } + for (offset, end) in [ + (0, SLOT0), + (SLOT0, SLOT1), + (SLOT1, SELECTOR), + (SELECTOR, STATE_BYTES), + ] { + write_range( + state, + offset, + &promotion.image[offset as usize..end as usize], + grant, + )?; + } + grant.take_entry()?; + locks::replace_created_file_nonce( + state, + &self.capsule.permission.job, + &promotion.final_nonce, + )?; + grant.take_entry()?; + state.sync_all()?; + self.pending = true; + Ok(()) + } +} + +impl PreparedLegacyScope { + pub(crate) fn prepare_enrollment_metadata( + &mut self, + request: Option<( + u64, + Option, + )>, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + self.prepare_enrollment_images(request.map(|(g, s)| (1, g, s)), memory) + } + pub(crate) fn prepare_enrollment_in_cohort( + &mut self, + cap: &AcceptedLegacyRootCohort, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + self.prepare_enrollment_images( + Some(( + 2, + cap.operator_generation, + if self.kind == LegacyScopeKind::DefaultDirect { + cap.default_source + } else { + None + }, + )), + memory, + ) + } + pub(crate) fn prepare_enrollment_automatic( + &mut self, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + self.prepare_enrollment_images(Some((3, 0, None)), memory) + } + pub(crate) fn refresh_enrollment_preparation( + &mut self, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + self.prepare_enrollment_images(None, memory) + } + pub(crate) fn enrollment_pending(&self) -> bool { + self.enrollment_waiting || self.birth.as_ref().is_some_and(|b| b.pending) + } + fn prepare_enrollment_images( + &mut self, + request: Option<( + u8, + u64, + Option, + )>, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + if self.kind == LegacyScopeKind::ManagedGeneration { + self.enrollment_runs = Some(prepare_empty_runs(&self.root, memory)?); + } + self.observation = PreparedSourceObservation::read(&self.root, &self.paths, None, memory)?; + for (i, path) in [ + (6, &self.writer), + (7, &self.family), + (8, &self.native), + (9, &self.root_use), + ] { + self.entries[i] = Entry::capture(path, Kind::File, true, &mut self.scratch)?; + } + let root = RootedDirectory::open_readonly_root( + &self.root, + self.entries[0].stamp.unwrap().identity(), + )?; + let state = root.open_readonly_file(self.paths[1].file_name().unwrap().as_encoded_bytes()); + if self.observation.is_none() { + let saved = match state { + Ok(file) => super::maintenance_legacy_guard::read_creation_capsule(&root, &file)?, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e.into()), + }; + let capsule = if let Some(saved) = saved { + self.job = saved.permission.job; + if saved.template.kind != self.kind + || saved.template.key != self.key + || saved.template.repository != self.entries[1].stamp.unwrap().identity() + || saved.permission.cache != self.entries[2].stamp.unwrap().identity() + || self.kind != LegacyScopeKind::ManagedAdministration + && (saved.permission.database != self.entries[4].stamp + || saved.permission.manifest != self.entries[5].stamp + || saved.permission.member + != self.entries[3] + .stamp + .ok_or_else(|| invalid(Problem::Changed))? + .identity()) + { + return Err(invalid(Problem::Changed)); + } + saved + } else { + let Some((mode, generation, source)) = request else { + return Ok(()); + }; + let mut database = [0; 256]; + let name = self.database.file_name().unwrap().as_encoded_bytes(); + database[..name.len()].copy_from_slice(name); + let mut cache = [0; 256]; + let cache_name = self.artifact.file_name().unwrap().as_encoded_bytes(); + cache[..cache_name.len()].copy_from_slice(cache_name); + let zero = FileIdentity::from_fixed_words(0, 0); + super::maintenance_legacy_guard::SourceCreationCapsule { + mode, + template: Binding { + kind: self.kind, + root: root.identity(), + gate: zero, + state: zero, + owner: zero, + nonce: self.job, + owner_nonce: [0; 32], + key: self.key, + database, + database_len: name.len(), + cache, + cache_len: cache_name.len(), + repository: self.entries[1].stamp.unwrap().identity(), + }, + permission: Record { + phase: LegacyPhase::Active, + version: 1, + member: self.entries[3] + .stamp + .ok_or_else(|| invalid(Problem::Changed))? + .identity(), + database: self.entries[4].stamp, + manifest: self.entries[5].stamp, + cache: self.entries[2].stamp.unwrap().identity(), + root_use: zero, + confirmation: if self.kind == LegacyScopeKind::ManagedAdministration { + self.cohort_scope + } else { + self.binding.digest() + }, + job: self.job, + member_name: self.member_name, + member_len: self.member_len, + freeze_cache: self.freeze_cache + && self.kind == LegacyScopeKind::DefaultDirect, + operator_generation: generation, + operator_pending: true, + pending_source_stamp: self.entries[2].stamp, + root_default_source: if matches!( + self.kind, + LegacyScopeKind::ManagedAdministration | LegacyScopeKind::DefaultDirect + ) { + source + } else { + None + }, + }, + } + }; + self.birth = Some(PreparedSourceBirth::prepare( + &self.root, + &self.paths, + capsule, + memory, + )?); + return Ok(()); + } + let observed = &self.observation.as_ref().unwrap().observed; + if !matches!( + observed.record.phase, + LegacyPhase::Active | LegacyPhase::Removed + ) { + return Ok(()); + } + let Some(root_use) = self.entries[9].stamp else { + return Ok(()); + }; + let mut after = observed.record; + after.phase = LegacyPhase::Preparing; + after.operator_pending = false; + after.pending_source_stamp = None; + after.version = observed + .commit + .generation + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + if let Some((_, generation, _)) = request { + after.operator_generation = generation; + after.confirmation = if self.kind == LegacyScopeKind::ManagedAdministration { + self.cohort_scope + } else { + self.binding.digest() + }; + } + after.member = self.entries[3] + .stamp + .ok_or_else(|| invalid(Problem::Changed))? + .identity(); + after.member_name = self.member_name; + after.member_len = self.member_len; + after.cache = self.entries[2].stamp.unwrap().identity(); + after.database = self.entries[4].stamp; + after.manifest = self.entries[5].stamp; + after.root_use = root_use.identity(); + after.job = self.job; + self.publication = Some( + super::maintenance_legacy_guard::PreparedSourcePublication::prepare( + observed.binding, + observed.commit, + after, + memory, + )?, + ); + Ok(()) + } +} + +fn existing_coordination_nonce(path: &Path) -> Result, FixedControlError> { + let parent = path + .parent() + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + let root = RootedDirectory::open_readonly_root( + parent, + locks::path_identity(parent).map_err(|_| invalid(Problem::Changed))?, + )?; + let file = match root.open_readonly_file( + path.file_name() + .ok_or_else(|| invalid(Problem::InvalidRecord))? + .as_encoded_bytes(), + ) { + Ok(f) => f, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + locks::read_coordination_incarnation(&file).map_err(|_| invalid(Problem::InvalidRecord)) +} + +impl PreparedLegacyStagingScope { + #[cfg(test)] + pub(crate) fn prepare_enrollment_metadata( + &mut self, + request: Option<( + u64, + Option, + )>, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + self.prepare_enrollment_images(request.map(|(generation, _)| (1, generation)), memory) + } + pub(crate) fn prepare_enrollment_in_cohort( + &mut self, + cap: &AcceptedLegacyRootCohort, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + self.prepare_enrollment_images(Some((2, cap.operator_generation)), memory) + } + pub(crate) fn refresh_enrollment_preparation( + &mut self, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + self.prepare_enrollment_images(None, memory) + } + pub(crate) fn enrollment_pending(&self) -> bool { + self.enrollment_waiting || self.birth.as_ref().is_some_and(|b| b.pending) + } + fn prepare_enrollment_images( + &mut self, + request: Option<(u8, u64)>, + memory: &CleanupMemory, + ) -> Result<(), FixedControlError> { + self.observation = PreparedSourceObservation::read(&self.root, &self.paths, None, memory)?; + if let Some(observation) = self.observation.as_ref() { + if !matches!( + observation.observed.record.phase, + LegacyPhase::Active | LegacyPhase::Removed + ) { + return Ok(()); + } + let mut after = observation.observed.record; + after.phase = LegacyPhase::Preparing; + after.operator_pending = false; + after.pending_source_stamp = None; + after.version = after + .version + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + after.job = self.job; + if let Some((_, generation)) = request { + after.operator_generation = generation; + after.confirmation = self.binding.digest(); + } + self.publication = Some( + super::maintenance_legacy_guard::PreparedSourcePublication::prepare( + observation.observed.binding, + observation.observed.commit, + after, + memory, + )?, + ); + return Ok(()); + } + let root = RootedDirectory::open_readonly_root(&self.root, self.root_stamp.identity())?; + let state = root.open_readonly_file(STAGING_STATE); + let saved = match state { + Ok(file) => super::maintenance_legacy_guard::read_creation_capsule(&root, &file)?, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e.into()), + }; + let capsule = if let Some(saved) = saved { + if saved.template.kind != LegacyScopeKind::NativeStaging + || saved.template.repository != self.repository + || saved.permission.member != self.staging_stamp.identity() + || saved.permission.database.is_some() + || saved.permission.manifest.is_some() + { + return Err(invalid(Problem::Changed)); + } + self.job = saved.permission.job; + saved + } else { + let Some((mode, generation)) = request else { + return Ok(()); + }; + let zero = FileIdentity::from_fixed_words(0, 0); + let mut cache = [0; 256]; + cache[..14].copy_from_slice(b"native-staging"); + super::maintenance_legacy_guard::SourceCreationCapsule { + mode, + template: Binding { + kind: LegacyScopeKind::NativeStaging, + root: root.identity(), + gate: zero, + state: zero, + owner: zero, + nonce: self.job, + owner_nonce: [0; 32], + key: [0; 64], + database: [0; 256], + database_len: 0, + cache, + cache_len: 14, + repository: self.repository, + }, + permission: Record { + phase: LegacyPhase::Active, + version: 1, + member: self.staging_stamp.identity(), + database: None, + manifest: None, + cache: self.staging_stamp.identity(), + root_use: zero, + confirmation: self.binding.digest(), + job: self.job, + member_name: [0; 128], + member_len: 0, + freeze_cache: false, + operator_generation: generation, + operator_pending: true, + pending_source_stamp: Some(self.staging_stamp), + root_default_source: None, + }, + } + }; + self.birth = Some(PreparedSourceBirth::prepare( + &self.root, + &self.paths, + capsule, + memory, + )?); + Ok(()) + } +} + +fn prepare_empty_runs( + root: &Path, + memory: &CleanupMemory, +) -> Result<(PathBuf, Entry), FixedControlError> { + let _floor = memory + .reserve(16384 + root.as_os_str().as_encoded_bytes().len() * 2) + .map_err(FixedControlError::Resource)?; + let path = root.join("runs"); + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(FixedControlError::Resource)?, + )?; + let entry = Entry::capture(&path, Kind::Directory, true, &mut scratch)?; + if let Some(stamp) = entry.stamp { + let directory = RootedDirectory::open_readonly_root(&path, stamp.identity())?; + let mut bytes = [0; 4096]; + let mut nonempty = false; + directory.visit_entries(&mut bytes, &mut |_| { + nonempty = true; + Ok(()) + })?; + stamp.recheck(&directory.pin_handle()?)?; + if nonempty { + return Err(invalid(Problem::Busy)); + } + } + Ok((path, entry)) +} + +impl PreparedLegacyScope { + pub(crate) fn publication_recovery_pending(&self) -> bool { + self.observation + .as_ref() + .is_some_and(|o| o.recovery.is_some()) + } + pub(crate) fn refresh_publication_recovery( + &mut self, + memory: &CleanupMemory, + owner: Option<&LegacySourceOwner>, + ) -> Result { + self.observation = PreparedSourceObservation::read( + &self.root, + &self.paths, + owner.map(|o| o.owner.file()), + memory, + )?; + Ok(self.publication_recovery_pending()) + } + pub(crate) fn recover_publication( + &mut self, + owner: Option<&mut LegacySourceOwner>, + grant: &mut MutationGrant, + memory: &CleanupMemory, + ) -> Result { + let Some(plan) = self.observation.as_ref().and_then(|o| o.recovery.as_ref()) else { + return Ok(false); + }; + if !self.binding.is_current()? { + return Err(invalid(Problem::Changed)); + } + let _floor = memory.reserve(24576).map_err(FixedControlError::Resource)?; + grant.take_entries(12)?; + let root = RootedDirectory::open_fixed( + &self.root, + plan.before.binding.root, + true, + &mut self.scratch, + )?; + let writer_file = checked_entry(&self.writer, self.entries[6], &mut self.scratch, grant)?; + let _writer = FixedLockedFile::try_lock(writer_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + let gate_file = + locks::open_fixed_control(&self.paths[0], Kind::File, true, &mut self.scratch)?; + let gate = FixedLockedFile::try_lock(gate_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + // Existing pair/cache locks only: recovery cannot create/adopt a missing namespace. + let mut held = [None, None, None]; + for (slot, path) in held + .iter_mut() + .zip([&self.family, &self.native, &self.root_use]) + { + match locks::open_fixed_control(path, Kind::File, true, &mut self.scratch) { + Ok(file) => { + *slot = Some( + FixedLockedFile::try_lock(file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?, + ) + } + Err(FixedControlError::Io(e)) + if e.kind() == std::io::ErrorKind::NotFound + && plan.after.phase == LegacyPhase::Removed => {} + Err(e) => return Err(e), + } + } + recover_with_owner( + &root, + &gate, + &self.paths, + plan, + owner, + &mut self.scratch, + grant, + ) + } +} +impl PreparedLegacyStagingScope { + pub(crate) fn publication_recovery_pending(&self) -> bool { + self.observation + .as_ref() + .is_some_and(|o| o.recovery.is_some()) + } + pub(crate) fn refresh_publication_recovery( + &mut self, + memory: &CleanupMemory, + owner: Option<&LegacySourceOwner>, + ) -> Result { + self.observation = PreparedSourceObservation::read( + &self.root, + &self.paths, + owner.map(|o| o.owner.file()), + memory, + )?; + Ok(self.publication_recovery_pending()) + } + pub(crate) fn recover_publication( + &mut self, + owner: Option<&mut LegacySourceOwner>, + grant: &mut MutationGrant, + memory: &CleanupMemory, + ) -> Result { + let Some(plan) = self.observation.as_ref().and_then(|o| o.recovery.as_ref()) else { + return Ok(false); + }; + if !self.binding.is_current()? { + return Err(invalid(Problem::Changed)); + } + let _floor = memory.reserve(24576).map_err(FixedControlError::Resource)?; + grant.take_entries(12)?; + let root = RootedDirectory::open_fixed( + &self.root, + plan.before.binding.root, + true, + &mut self.scratch, + )?; + let staging = RootedDirectory::open_fixed( + &self.staging, + self.staging_stamp.identity(), + true, + &mut self.scratch, + )?; + if staging.identity() != plan.before.record.member { + return Err(invalid(Problem::Changed)); + } + let gate_file = + locks::open_fixed_control(&self.paths[0], Kind::File, true, &mut self.scratch)?; + let gate = FixedLockedFile::try_lock(gate_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + recover_with_owner( + &root, + &gate, + &self.paths, + plan, + owner, + &mut self.scratch, + grant, + ) + } +} +#[inline(never)] +fn recover_with_owner( + root: &RootedDirectory, + gate: &FixedLockedFile, + paths: &[PathBuf; 3], + plan: &super::maintenance_legacy_guard::PreparedSourceRecovery, + owner: Option<&mut LegacySourceOwner>, + scratch: &mut FixedControlScratch, + grant: &mut MutationGrant, +) -> Result { + grant.take_entries(4)?; + let state = locks::open_fixed_control(&paths[1], Kind::File, true, scratch)?; + let temporary; + let held = if let Some(owner) = owner.as_ref() { + if owner.binding != plan.before.binding + || owner.commit != plan.before.commit + || owner.record != plan.before.record + { + return Err(invalid(Problem::Changed)); + } + &owner.owner + } else { + let file = locks::open_fixed_control(&paths[2], Kind::File, true, scratch)?; + temporary = FixedLockedFile::try_lock(file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + &temporary + }; + plan.apply(root, gate, held, &state, grant)?; + if let Some(owner) = owner { + owner.commit = plan.commit; + owner.record = plan.after; + owner.state_stamp = FixedFileStamp::capture(&state, Kind::File)?; + owner.pending_publication = false; + } + Ok(true) +} + +#[cfg(test)] +#[path = "maintenance_legacy_source_recovery_tests.rs"] +mod source_recovery_tests; + +#[cfg(test)] +mod cohort_preparation_layout_tests { + use super::*; + #[test] + fn accepted_administrative_cohort_keeps_binding_image_out_of_fixed_owner_layout() { + // A small enrollment-only owner crosses preparation stages. Original + // inline binding copies exceeded this bound; its image stays charged. + assert!(std::mem::size_of::() <= 2048); + let _: fn(AcceptedLegacyRootCohort) = |cohort| { + let _: std::cell::RefCell< + super::super::maintenance_budget::MemoryBox, + > = cohort.target_binding; + }; + } +} + +#[cfg(test)] +mod retained_source_observation_layout_tests { + use super::*; + #[test] + fn retained_source_observer_keeps_snapshot_image_out_of_fixed_owner_layout() { + // Measured fixed owner is224B locally; keep its actual source facts in + // charged storage without imposing relative sizes on other protocols. + assert!(std::mem::size_of::() <= 512); + let _: fn(PreparedSourceObservation) = |observation| { + let _: super::super::maintenance_budget::MemoryBox< + super::super::maintenance_legacy_guard::Observation, + > = observation.observed; + }; + } +} + +#[cfg(test)] +mod staging_scope_layout_tests { + use super::*; + #[test] + fn prepared_staging_scope_keeps_binding_and_scratch_images_boxed() { + assert!(std::mem::size_of::() <= 2048); + let _: fn(PreparedLegacyStagingScope) = |scope| { + let _: super::super::maintenance_budget::MemoryBox = + scope.binding; + let _: super::super::maintenance_budget::MemoryBox = scope.scratch; + }; + } +} diff --git a/src/storage/maintenance_legacy_coverage.rs b/src/storage/maintenance_legacy_coverage.rs new file mode 100644 index 00000000..dea2a2d7 --- /dev/null +++ b/src/storage/maintenance_legacy_coverage.rs @@ -0,0 +1,729 @@ +//! One control-image step of an explicitly enrolled old reference domain. +//! Preparation owns codecs. The guarded callback only checks and writes bytes. +use super::*; +use crate::storage::locks::{ + self, FixedControlError as Error, FixedControlKind as Kind, FixedControlProblem as Problem, + FixedFileStamp, RootedDirectory, +}; +use crate::storage::maintenance_budget::{ + CleanupMemory, MemoryBox, MemoryReservation, MutationGrant, ReservedBytes, +}; +use crate::storage::maintenance_controls as c; + +const LEDGER: &[u8] = b".legacy-coverage"; +const BYTES: usize = 1024; +const COUNT: usize = 20; +const NAMES: [&[u8]; COUNT] = [ + c::DATA_NAMES[0], + c::DATA_NAMES[1], + c::SELECTOR_NAME, + c::STREAM_NAME, + c::BINDING_NAME, + b".use-cohort.json", + b".workspace-startup.lock", + b".workspace-removal.data0", + b".workspace-removal.data1", + b".workspace-removal.selector", + b".workspace-removal.binding", + b".generation-startup.lock", + b".generation-startup.data0", + b".generation-startup.data1", + b".generation-startup.selector", + b".generation-startup.binding", + b".reference-activity.lock", + b".reference-epoch.lock", + b"worker.lock", + b".reference-epoch", +]; +fn destination(index: usize) -> bool { + matches!(index, 6 | 11..=15 | 18) +} +fn bad() -> Error { + Error::Invalid(Problem::Changed) +} +fn native(error: NativeError) -> Error { + match error { + NativeError::Io(e) => e.into(), + _ => bad(), + } +} +fn put(b: &mut [u8], id: FileIdentity) { + let (a, c) = id.fixed_words(); + b[..8].copy_from_slice(&a.to_le_bytes()); + b[8..16].copy_from_slice(&c.to_le_bytes()); +} +fn get(b: &[u8]) -> FileIdentity { + FileIdentity::from_fixed_words( + u64::from_le_bytes(b[..8].try_into().unwrap()), + u64::from_le_bytes(b[8..16].try_into().unwrap()), + ) +} +#[derive(Clone, Copy)] +struct Ledger { + managed: FileIdentity, + artifact: FileIdentity, + writer: FileIdentity, + coordination: FileIdentity, + nonce: [u8; 32], + operation: [u8; 32], + scope: [u8; 32], + index: usize, + ids: [FileIdentity; COUNT], +} +impl Ledger { + fn encode(self) -> [u8; BYTES] { + let mut b = [0; BYTES]; + b[..8].copy_from_slice(b"CGLCOV01"); + b[8] = self.index as u8; + for (off, id) in [ + (16, self.managed), + (32, self.artifact), + (48, self.writer), + (64, self.coordination), + ] { + put(&mut b[off..off + 16], id); + } + b[80..112].copy_from_slice(&self.nonce); + b[112..144].copy_from_slice(&self.operation); + b[144..176].copy_from_slice(&self.scope); + for (i, id) in self.ids.into_iter().enumerate() { + put(&mut b[176 + i * 16..192 + i * 16], id); + } + let digest = Sha256::digest(&b[..BYTES - 32]); + b[BYTES - 32..].copy_from_slice(&digest); + b + } + fn decode(b: &[u8; BYTES]) -> Result { + if &b[..8] != b"CGLCOV01" + || b[8] as usize > COUNT + || b[9..16].iter().chain(&b[496..BYTES - 32]).any(|x| *x != 0) + || Sha256::digest(&b[..BYTES - 32]).as_slice() != &b[BYTES - 32..] + { + return Err(bad()); + } + Ok(Self { + managed: get(&b[16..32]), + artifact: get(&b[32..48]), + writer: get(&b[48..64]), + coordination: get(&b[64..80]), + nonce: b[80..112].try_into().unwrap(), + operation: b[112..144].try_into().unwrap(), + scope: b[144..176].try_into().unwrap(), + index: b[8] as usize, + ids: std::array::from_fn(|i| get(&b[176 + i * 16..192 + i * 16])), + }) + } +} +enum Step { + Seed, + Create, + Fill(FixedFileStamp), + Acknowledge(FixedFileStamp), + Done(FixedFileStamp), +} +pub(crate) struct PreparedLegacyCoverage { + ledger: Ledger, + expected: Option<([u8; BYTES], FixedFileStamp)>, + next: [u8; BYTES], + image: ReservedBytes, + len: usize, + step: Step, + _floor: MemoryReservation, +} +fn bounded_image(file: &std::fs::File, buffer: &mut [u8]) -> Result { + let stamp = FixedFileStamp::capture(file, Kind::File)?; + let len = usize::try_from(stamp.len()).map_err(|_| bad())?; + if len > buffer.len() { + return Err(bad()); + } + locks::read_fixed_range(file, 0, &mut buffer[..len])?; + stamp.recheck(file)?; + Ok(len) +} +fn object_image( + domain: &ReferenceDomain, + ledger: Ledger, + own: FileIdentity, + index: usize, + out: &mut [u8], +) -> Result { + let nonce = ledger.nonce; + let slot = |out: &mut [u8], initial: &[u8]| { + c::encode_slot(&mut out[..c::SLOT_BYTES], &nonce, 0, initial) + .map_err(|_| bad()) + .map(|_| c::SLOT_BYTES) + }; + let selector = |out: &mut [u8], initial: &[u8]| { + let mut first = [0; c::SLOT_BYTES]; + let fact = c::encode_slot(&mut first, &nonce, 0, initial).map_err(|_| bad())?; + out[..c::SELECTOR_BYTES].fill(0); + c::encode_commit( + &mut out[..c::COMMIT_BYTES], + &nonce, + c::CommitFact { + generation: 0, + active: 0, + slot_digest: fact.digest, + }, + ) + .map_err(|_| bad())?; + Ok(c::SELECTOR_BYTES) + }; + let publication = + |out: &mut [u8], data: [FileIdentity; 2], selector: FileIdentity, stream: FileIdentity| { + c::PublisherBinding { + root: ledger.artifact, + coordination: ledger.coordination, + data, + selector, + stream: Some(stream), + incarnation: nonce, + } + .encode(&mut out[..c::BINDING_BYTES]) + .map_err(|_| bad())?; + Ok(c::BINDING_BYTES) + }; + match index { + 0 => slot(out, b"CGBUILD1"), + 1 => slot(out, b""), + 2 => selector(out, b"CGBUILD1"), + 3 => Ok(0), + 4 => publication( + out, + [ledger.ids[0], ledger.ids[1]], + ledger.ids[2], + ledger.ids[3], + ), + 5 => { + let record = ArtifactCohortRecord { + version: 2, + root: ledger.artifact, + coordination: ledger.coordination, + coordination_incarnation: nonce, + origin: CohortOrigin::ControlledMigration, + }; + let bytes = serde_json::to_vec(&record).map_err(|_| bad())?; + out[..bytes.len()].copy_from_slice(&bytes); + Ok(bytes.len()) + } + 6 | 11 => { + let bytes = locks::encode_coordination_incarnation(nonce); + out[..bytes.len()].copy_from_slice(&bytes); + Ok(bytes.len()) + } + 7 => slot( + out, + &crate::storage::maintenance_workspace_gate::legacy_bootstrap_idle_image(), + ), + 8 | 13 => slot(out, b""), + 9 => selector( + out, + &crate::storage::maintenance_workspace_gate::legacy_bootstrap_idle_image(), + ), + 10 => publication( + out, + [ledger.ids[7], ledger.ids[8]], + ledger.ids[9], + ledger.ids[6], + ), + 12 => slot( + out, + &crate::storage::maintenance_generation_gate::legacy_bootstrap_idle_image(), + ), + 14 => selector( + out, + &crate::storage::maintenance_generation_gate::legacy_bootstrap_idle_image(), + ), + 15 => { + use crate::storage::maintenance_generation_gate::{GateBinding, GenerationOriginScope}; + let generations = locks::path_identity(&domain.destination_root.join("generations")) + .map_err(native)?; + let bytes = GateBinding { + managed: ledger.managed, + generations, + writer: ledger.writer, + gate: ledger.ids[11], + binding: own, + data: [ledger.ids[12], ledger.ids[13]], + selector: ledger.ids[14], + parent: GenerationOriginScope { + root: ledger.artifact, + coordination: ledger.coordination, + cohort: ledger.ids[5], + publication_binding: ledger.ids[4], + incarnation: nonce, + }, + nonce, + } + .encode(); + out[..bytes.len()].copy_from_slice(&bytes); + Ok(bytes.len()) + } + 16..=18 => Ok(0), + 19 => { + let mut incarnation = [0; 32]; // Stable per enrolled bootstrap, not a new epoch on replay. + incarnation.copy_from_slice(&Sha256::digest(ledger.operation)); + let bytes = encode_reference_epoch(&ReferenceEpochRecord { + version: 2, + root: ledger.artifact, + use_lock: ledger.coordination, + use_incarnation: nonce, + cohort: ledger.ids[5], + scope: domain.scope.clone(), + incarnation, + activity: ledger.ids[16], + serial: ledger.ids[17], + counter: 0, + covered: true, + }) + .map_err(native)?; + out[..bytes.len()].copy_from_slice(&bytes); + Ok(bytes.len()) + } + _ => Err(bad()), + } +} +/// Requires a preceding genuine administrative enrollment to have initialized +/// the stable root-use header. Merely observing an old root cannot write it. +pub(crate) fn prepare_legacy_coverage( + domain: &ReferenceDomain, + memory: &CleanupMemory, +) -> Result, Error> { + let floor = memory + .reserve( + std::mem::size_of::() + + 65536 + + domain.artifact_root.as_os_str().as_encoded_bytes().len() * 24, + ) + .map_err(Error::Resource)?; + if domain.scope.kind != 1 || !domain.revalidate().map_err(native)? { + return Err(bad()); + } + let root = RootedDirectory::open_readonly_root( + &domain.artifact_root, + domain.artifact_identity.ok_or_else(bad)?, + )?; + let managed = + RootedDirectory::open_readonly_root(&domain.destination_root, domain.scope.destination)?; + let use_file = root.open_readonly_file(b".use.lock")?; + let coordination = locks::file_identity(&use_file).map_err(native)?; + let nonce = locks::read_coordination_incarnation(&use_file) + .map_err(native)? + .ok_or_else(bad)?; + let scope: [u8; 32] = + Sha256::digest(serde_json::to_vec(&domain.scope).map_err(|_| bad())?).into(); + let mut read = [0; 4096]; + let (mut ledger, expected, seed) = match root.open_readonly_file(LEDGER) { + Ok(file) => { + let stamp = FixedFileStamp::capture(&file, Kind::File)?; + if bounded_image(&file, &mut read)? != BYTES { + return Err(bad()); + } + let bytes: [u8; BYTES] = read[..BYTES].try_into().unwrap(); + let ledger = Ledger::decode(&bytes)?; + if locks::created_file_nonce(&file)? != Some(ledger.operation) { + return Err(bad()); + } + (ledger, Some((bytes, stamp)), false) + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + let mut operation = [0; 32]; + getrandom::fill(&mut operation).map_err(|_| bad())?; + ( + Ledger { + managed: domain.scope.destination, + artifact: root.identity(), + writer: domain.scope.writer, + coordination, + nonce, + operation, + scope, + index: 0, + ids: [FileIdentity::from_fixed_words(0, 0); COUNT], + }, + None, + true, + ) + } + Err(e) => return Err(e.into()), + }; + if ledger.managed != managed.identity() + || ledger.artifact != root.identity() + || ledger.writer != domain.scope.writer + || ledger.coordination != coordination + || ledger.nonce != nonce + || ledger.scope != scope + { + return Err(bad()); + } + for (i, control_name) in NAMES.iter().enumerate().take(ledger.index) { + let parent = if destination(i) { &managed } else { &root }; + let file = parent.open_readonly_file(control_name)?; + if locks::file_identity(&file).map_err(native)? != ledger.ids[i] + || locks::created_file_nonce(&file)? != Some(ledger.operation) + { + return Err(bad()); + } + } + let mut image = memory + .reserve(4096) + .map_err(Error::Resource)? + .bytes(4096) + .map_err(Error::Resource)?; + let mut len = 0; + let mut next = ledger.encode(); + let step = if seed { + Step::Seed + } else if ledger.index == COUNT { + let file = root.open_readonly_file(NAMES[19])?; + let epoch = read_reference_epoch(&domain.artifact_root).map_err(native)?; + if !epoch.record.covered + || epoch.record.scope != domain.scope + || !epoch_root_matches(&domain.artifact_root, &epoch.record, None).map_err(native)? + { + return Err(bad()); + } + Step::Done(FixedFileStamp::capture(&file, Kind::File)?) + } else { + let i = ledger.index; + let parent = if destination(i) { &managed } else { &root }; + match parent.open_readonly_file(NAMES[i]) { + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Step::Create, + Err(e) => return Err(e.into()), + Ok(file) => { + let stamp = FixedFileStamp::capture(&file, Kind::File)?; + if locks::created_file_nonce(&file)? != Some(ledger.operation) { + return Err(bad()); + } + len = object_image(domain, ledger, stamp.identity(), i, image.as_mut_slice())?; + let found = bounded_image(&file, &mut read)?; + if found > len || read[..found] != image.as_slice()[..found] { + return Err(bad()); + } + if found != len { + Step::Fill(stamp) + } else { + ledger.ids[i] = stamp.identity(); + ledger.index += 1; + next = ledger.encode(); + Step::Acknowledge(stamp) + } + } + } + }; + MemoryBox::new( + PreparedLegacyCoverage { + ledger, + expected, + next, + image, + len, + step, + _floor: floor, + }, + memory, + ) + .map_err(Error::Resource) +} +impl ReferenceDomain { + /// Recognize only the genuine, still-unactivated generation-gate bootstrap. + /// This is read continuity, never creation, retirement or recovery authority. + pub(crate) fn staged_generation_gate(&self, managed: &RootedDirectory) -> Result { + if self.scope.kind != 1 || !self.revalidate().map_err(native)? { + return Err(bad()); + } + let root = RootedDirectory::open_readonly_root( + &self.artifact_root, + self.artifact_identity.ok_or_else(bad)?, + )?; + let ledger_file = match root.open_readonly_file(LEDGER) { + Ok(file) => file, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(false), + Err(e) => return Err(e.into()), + }; + let ledger_stamp = FixedFileStamp::capture(&ledger_file, Kind::File)?; + let mut read = [0; 4096]; + if bounded_image(&ledger_file, &mut read)? != BYTES { + return Err(bad()); + } + let ledger = Ledger::decode(read[..BYTES].try_into().unwrap())?; + if !(11..=15).contains(&ledger.index) { + return Ok(false); + } + let use_file = root.open_readonly_file(b".use.lock")?; + let scope: [u8; 32] = + Sha256::digest(serde_json::to_vec(&self.scope).map_err(|_| bad())?).into(); + if ledger.managed != managed.identity() + || ledger.managed != self.scope.destination + || ledger.artifact != root.identity() + || ledger.writer != self.scope.writer + || ledger.coordination != locks::file_identity(&use_file).map_err(native)? + || locks::read_coordination_incarnation(&use_file).map_err(native)? + != Some(ledger.nonce) + || ledger.scope != scope + || ledger.operation.iter().all(|byte| *byte == 0) + || locks::created_file_nonce(&ledger_file)? != Some(ledger.operation) + { + return Err(bad()); + } + // Every acknowledged object must retain its actual producer birth. + for (i, control_name) in NAMES.iter().enumerate().take(ledger.index) { + let parent = if destination(i) { managed } else { &root }; + let file = parent.open_readonly_file(control_name)?; + if locks::file_identity(&file).map_err(native)? != ledger.ids[i] + || locks::created_file_nonce(&file)? != Some(ledger.operation) + { + return Err(bad()); + } + } + let mut expected = [0; 4096]; + for (i, control_name) in NAMES.iter().enumerate().take(16).skip(11) { + let file = match managed.open_readonly_file(control_name) { + Ok(file) => file, + Err(e) if e.kind() == std::io::ErrorKind::NotFound && i >= ledger.index => { + continue; + } + Err(e) => return Err(e.into()), + }; + let stamp = FixedFileStamp::capture(&file, Kind::File)?; + if i > ledger.index + || locks::created_file_nonce(&file)? != Some(ledger.operation) + || (i < ledger.index && stamp.identity() != ledger.ids[i]) + { + return Err(bad()); + } + let len = object_image(self, ledger, stamp.identity(), i, &mut expected)?; + let found = bounded_image(&file, &mut read)?; + if found > len + || read[..found] != expected[..found] + || (i < ledger.index && found != len) + { + return Err(bad()); + } + // The complete binding is activation, even before ledger acknowledgement. + if i == 15 && found == len { + return Ok(false); + } + stamp.recheck(&file)?; + } + ledger_stamp.recheck(&ledger_file)?; + if !self.revalidate().map_err(native)? { + return Err(bad()); + } + Ok(true) + } +} +impl PreparedLegacyCoverage { + pub(crate) fn advance( + &self, + authority: &impl AdministrativeCoverageAuthority, + grant: &mut MutationGrant, + ) -> Result { + grant.check()?; + if !authority.is_administrative() + || authority.directory().identity() != self.ledger.managed + || authority.artifact_directory().identity() != self.ledger.artifact + || authority.writer_identity()? != self.ledger.writer + || authority.artifact_use_identity()? != self.ledger.coordination + || authority.proposed_artifact_incarnation() != self.ledger.nonce + { + return Err(bad()); + } + let root = authority.artifact_directory(); + let managed = authority.directory(); + for (i, control_name) in NAMES.iter().enumerate().take(self.ledger.index) { + grant.take_entry()?; + let parent = if destination(i) { managed } else { root }; + let file = parent.open_readonly_file(control_name)?; + if locks::file_identity(&file).map_err(native)? != self.ledger.ids[i] + || locks::created_file_nonce(&file)? != Some(self.ledger.operation) + { + return Err(bad()); + } + } + let mut read = [0; BYTES]; + let ledger_file = if let Some((bytes, stamp)) = &self.expected { + grant.take_entry()?; + let file = root.open_existing(LEDGER)?; + stamp.recheck(&file)?; + locks::read_fixed_range(&file, 0, &mut read)?; + if &read != bytes { + return Err(bad()); + } + Some(file) + } else { + None + }; + match self.step { + Step::Seed => { + grant.take_entry()?; + grant.check_time()?; + let file = root.create_new(LEDGER)?; + grant.check_time()?; + locks::set_created_file_nonce(&file, &self.ledger.operation)?; + locks::write_fixed_range(&file, 0, &self.next, grant)?; + grant.check_time()?; + file.sync_all()?; + grant.check_time()?; + root.sync_namespace()?; + } + Step::Create => { + let i = self.ledger.index; + let parent = if destination(i) { managed } else { root }; + grant.take_entry()?; + grant.check_time()?; + let file = parent.create_new(NAMES[i])?; + grant.check_time()?; + locks::set_created_file_nonce(&file, &self.ledger.operation)?; + grant.check_time()?; + file.sync_all()?; + grant.check_time()?; + parent.sync_namespace()?; + } + Step::Fill(stamp) => { + let i = self.ledger.index; + let parent = if destination(i) { managed } else { root }; + grant.take_entry()?; + let file = parent.open_existing(NAMES[i])?; + stamp.recheck(&file)?; + if locks::created_file_nonce(&file)? != Some(self.ledger.operation) { + return Err(bad()); + } + grant.check_time()?; + locks::write_fixed_range(&file, 0, &self.image.as_slice()[..self.len], grant)?; + grant.check_time()?; + file.sync_all()?; + grant.check_time()?; + parent.sync_namespace()?; + } + Step::Acknowledge(stamp) => { + let i = self.ledger.index - 1; + let parent = if destination(i) { managed } else { root }; + grant.take_entry()?; + let file = parent.open_readonly_file(NAMES[i])?; + stamp.recheck(&file)?; + drop(file); + let file = ledger_file.ok_or_else(bad)?; + grant.check_time()?; + locks::write_fixed_range(&file, 0, &self.next, grant)?; + grant.check_time()?; + file.sync_all()?; + } + Step::Done(stamp) => { + grant.take_entry()?; + let file = root.open_readonly_file(NAMES[19])?; + stamp.recheck(&file)?; + return Ok(true); + } + } + grant.check_time()?; + Ok(false) + } +} + +#[cfg(test)] +mod staged_gate_tests { + use super::*; + use std::io::{Seek, SeekFrom, Write}; + + struct Fixture(PathBuf); + impl Drop for Fixture { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.0); + } + } + + #[test] + fn staged_generation_gate_authenticates_each_restart_prefix_until_activation() { + let mut operation = [0; 32]; + getrandom::fill(&mut operation).unwrap(); + let suffix = operation + .iter() + .map(|b| format!("{b:02x}")) + .collect::(); + let fixture = Fixture(std::env::temp_dir().join(format!("cgraph-gate-stage-{suffix}"))); + let layout = ManagedLayout::new(&fixture.0); + // Build an old, unenrolled namespace. ensure_roots() is a producer + // factory and would already create the fresh artifact coordination. + for path in [ + layout.storage_root().to_path_buf(), + layout.generations_root(), + layout.runs_root(), + layout.artifacts_root(), + ] { + std::fs::create_dir_all(path).unwrap(); + } + std::fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(layout.writer_lock_path()) + .unwrap(); + let managed = + RootedDirectory::open(&fixture.0, path_identity(&fixture.0).unwrap()).unwrap(); + let root = managed.open_directory(b"artifacts").unwrap(); + let nonce = [42; 32]; + let mut use_file = root.create_new(b".use.lock").unwrap(); + use_file + .write_all(&locks::encode_coordination_incarnation(nonce)) + .unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + let mut ledger = Ledger { + managed: managed.identity(), + artifact: root.identity(), + writer: domain.scope.writer, + coordination: locks::file_identity(&use_file).unwrap(), + nonce, + operation, + scope: Sha256::digest(serde_json::to_vec(&domain.scope).unwrap()).into(), + index: 0, + ids: [FileIdentity::from_fixed_words(0, 0); COUNT], + }; + let mut ledger_file = root.create_new(LEDGER).unwrap(); + locks::set_created_file_nonce(&ledger_file, &operation).unwrap(); + let persist = |file: &mut std::fs::File, ledger: Ledger| { + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(&ledger.encode()).unwrap(); + file.sync_all().unwrap(); + }; + let mut bytes = [0; 4096]; + for (i, control_name) in NAMES.iter().enumerate().take(16) { + let parent = if destination(i) { &managed } else { &root }; + let mut file = parent.create_new(control_name).unwrap(); + locks::set_created_file_nonce(&file, &operation).unwrap(); + let own = locks::file_identity(&file).unwrap(); + let len = object_image(&domain, ledger, own, i, &mut bytes).unwrap(); + if i >= 11 { + persist(&mut ledger_file, ledger); + assert!( + domain.staged_generation_gate(&managed).unwrap(), + "empty {i}" + ); + let split = len / 2; + file.write_all(&bytes[..split]).unwrap(); + assert!( + domain.staged_generation_gate(&managed).unwrap(), + "prefix {i}" + ); + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(b"!").unwrap(); + assert!( + domain.staged_generation_gate(&managed).is_err(), + "corrupt {i}" + ); + file.seek(SeekFrom::Start(0)).unwrap(); + } + file.write_all(&bytes[..len]).unwrap(); + if i >= 11 { + assert_eq!( + domain.staged_generation_gate(&managed).unwrap(), + i != 15, + "full image {i}" + ); + } + ledger.ids[i] = own; + ledger.index += 1; + persist(&mut ledger_file, ledger); + } + assert!(!domain.staged_generation_gate(&managed).unwrap()); + } +} diff --git a/src/storage/maintenance_legacy_engine.rs b/src/storage/maintenance_legacy_engine.rs new file mode 100644 index 00000000..f8ba2558 --- /dev/null +++ b/src/storage/maintenance_legacy_engine.rs @@ -0,0 +1,1518 @@ +//! Joined-worker legacy orchestration. Only prebound short work enters the real +//! execution permit; source/profile preparation and disposal remain offpermit. +use super::inventory::MaintenanceTarget; +use super::locks::{ + self, FixedControlError, FixedControlKind, FixedControlProblem, FixedFileStamp, RootedDirectory, +}; +use super::maintenance_admission::{prepare_canonical, PreparedTargetBinding}; +use super::maintenance_budget::{CleanupMemory, MemoryBox, MemoryReservation, MutationGrant}; +use super::maintenance_candidate::CandidateReport; +use super::maintenance_legacy_bootstrap::{ + self as source, LegacyClosedVersion, LegacyScope, LegacySourceOwner, PreparedLegacyScope, +}; +use super::maintenance_ownership::{ReferenceDomain, ReferenceEpoch}; +use super::maintenance_scheduler::LegacyQuiescenceConfirmation; +use std::io::Read; +use std::path::PathBuf; +#[track_caller] +fn invalid() -> FixedControlError { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "legacy preparation invalid at {}", + std::panic::Location::caller() + ); + } + FixedControlError::Invalid(FixedControlProblem::InvalidRecord) +} +#[derive(Default)] +pub(crate) struct LegacyPreparationState { + interest: bool, + operator_required: bool, + request_metadata: Option<( + u64, + Option, + )>, + staging: Option>, + native: super::maintenance_legacy_generations_engine::LegacyNativePreparationState, +} +impl LegacyPreparationState { + pub(crate) fn clear(&mut self) { + self.interest = false; + self.operator_required = false; + self.request_metadata = None; + drop(self.staging.take()); + self.native.clear(); + } + pub(crate) fn needs_operator(&self) -> bool { + self.operator_required + } + pub(crate) fn set_request_metadata( + &mut self, + metadata: Option<( + u64, + Option, + )>, + ) { + self.request_metadata = metadata; + } + pub(crate) fn has_parked_payloads(&self) -> bool { + self.native.has_parked_payloads() + } + pub(crate) fn set_legacy_interest(&mut self, interest: bool) { + self.interest |= interest; + } +} +enum Action { + Native(MemoryBox), + Pending, + Staging(MemoryBox), + Enrollment { + scope: MemoryBox, + owner: Option, + needs_confirmation: bool, + }, + Administration { + scope: MemoryBox, + coverage: Option>, + needs_confirmation: bool, + owner: Option, + covered: bool, + }, +} +pub(crate) struct LegacyWork { + action: Action, + memory: CleanupMemory, + report: CandidateReport, + _floor: MemoryReservation, +} +impl LegacyWork { + pub(crate) fn requires_legacy_confirmation(&self) -> bool { + match &self.action { + Action::Native(work) => work.requires_legacy_confirmation(), + Action::Pending => false, + Action::Staging(_) => false, + Action::Enrollment { + needs_confirmation, .. + } => *needs_confirmation, + Action::Administration { + needs_confirmation, .. + } => *needs_confirmation, + } + } + pub(crate) fn advance( + &mut self, + grant: &mut MutationGrant, + confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + match &mut self.action { + Action::Native(work) => work.advance(grant, confirmation), + Action::Pending => Ok(self.report), + Action::Staging(work) => { + work.advance(&self.memory, grant)?; + self.report.pending = true; + self.report.mutated_entries = 1; + Ok(self.report) + } + Action::Enrollment { + scope, + owner, + needs_confirmation, + } => { + if *needs_confirmation && confirmation.is_none() { + return Err(invalid()); + } + let outcome = source::with_legacy_enrollment( + scope, + confirmation, + grant, + &self.memory, + &mut |_, _| Ok(()), + ); + match outcome { + Ok((_, actual_owner)) => *owner = Some(actual_owner), + Err(FixedControlError::Invalid(FixedControlProblem::Busy)) + if scope.enrollment_pending() => {} + Err(e) => return Err(e), + }; + self.report.pending = true; + self.report.mutated_entries = 1; + Ok(self.report) + } + Action::Administration { + scope, + coverage, + needs_confirmation, + owner, + covered, + } => { + if *needs_confirmation && confirmation.is_none() { + return Err(invalid()); + } + let outcome = source::with_legacy_enrollment( + scope, + confirmation, + grant, + &self.memory, + &mut |authority, grant| { + if let Some(coverage) = coverage.as_mut() { + coverage.advance(authority, grant) + } else { + Ok(false) + } + }, + ); + match outcome { + Ok((done, actual_owner)) => { + *owner = Some(actual_owner); + *covered = done; + } + Err(FixedControlError::Invalid(FixedControlProblem::Busy)) + if scope.enrollment_pending() => {} + Err(e) => return Err(e), + }; + self.report.pending = !*covered; + self.report.mutated_entries = 1; + Ok(self.report) + } + } + } +} +#[inline(never)] +fn active_generation( + target: &MaintenanceTarget, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let _floor = memory.reserve(32768).map_err(FixedControlError::Resource)?; + let configured = + super::maintenance_projection::project_binding(target, memory).map_err(|_| invalid())?; + if configured.direct.is_some() { + return Ok(None); + } + let storage = configured.storage; + let root = RootedDirectory::open_readonly_root( + &storage, + locks::path_identity(&storage).map_err(|_| invalid())?, + )?; + let mut pointer = match root.open_readonly_file(b"active.json") { + Ok(f) => f, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + let stamp = FixedFileStamp::capture(&pointer, FixedControlKind::File)?; + if stamp.len() > 1024 { + return Err(invalid()); + } + let mut bytes = [0; 1024]; + pointer.read_exact(&mut bytes[..stamp.len() as usize])?; + let active: super::managed::ActiveGeneration = + serde_json::from_slice(&bytes[..stamp.len() as usize]).map_err(|_| invalid())?; + if active.schema_version != 2 { + return Err(invalid()); + } + super::layout::validate_generation_id(&active.generation_id).map_err(|_| invalid())?; + stamp.recheck(&pointer)?; + stamp.recheck(&root.open_readonly_file(b"active.json")?)?; + let generation = super::layout::ManagedLayout::new(&storage) + .generation(&active.generation_id) + .map_err(|_| invalid())? + .root() + .to_owned(); + Ok(Some((storage, generation))) +} +/// Runs before observing E: original stores may have no E/cohort controls yet. +/// Initial confirmation is consumed solely by this actual administrative role. +#[allow(clippy::too_many_arguments)] +pub(crate) fn prepare_bootstrap_work( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + state: &mut LegacyPreparationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + legacy_interest: bool, +) -> Result>, FixedControlError> { + if cancel() { + return Ok(None); + } + state.interest |= legacy_interest; + state.operator_required = discover_operator_interest(target, memory, cancel)?; + if super::maintenance_legacy_generations_engine::prepare_root_cohort_boxed( + target, binding, memory, + )? + .is_some() + { + state.operator_required = false; + if let Some(work) = prepare_default_enrollment(target, binding, state, memory)? { + return Ok(Some(work)); + } + if !state.interest { + return Ok(None); + } + } + let Some((storage, generation)) = active_generation(target, memory)? else { + return Ok(None); + }; + let mut scope = source::prepare_legacy_scope( + target, + binding, + LegacyScope::ManagedAdministration { + storage_root: &storage, + generation_root: &generation, + }, + false, + memory, + )?; + scope.prepare_enrollment_metadata(state.request_metadata, memory)?; + let accepted = scope.has_accepted_job(memory)?; + if accepted { + state.operator_required = false; + } + if !state.interest && !accepted { + return Ok(None); + } + let domain = ReferenceDomain::managed(&super::layout::ManagedLayout::new(&storage)).ok(); + let root_use = std::fs::File::open(storage.join("artifacts/.use.lock")); + let has_header = match root_use { + Ok(file) => locks::read_coordination_incarnation(&file) + .map_err(|_| invalid())? + .is_some(), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => false, + Err(e) => return Err(e.into()), + }; + let coverage = if let Some(domain) = domain.as_ref().filter(|_| has_header) { + Some(super::maintenance_ownership::prepare_legacy_coverage( + domain, memory, + )?) + } else { + None + }; + let floor = memory + .reserve(std::mem::size_of::() + 8192) + .map_err(FixedControlError::Resource)?; + MemoryBox::new( + LegacyWork { + action: Action::Administration { + scope, + coverage, + needs_confirmation: state.interest || !accepted, + owner: None, + covered: false, + }, + memory: memory.clone(), + report: CandidateReport { + pending: true, + ..CandidateReport::default() + }, + _floor: floor, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) +} +pub(crate) fn is_legacy_payload(payload: &[u8]) -> bool { + payload.starts_with(b"CGLEG001") || payload.starts_with(b"CGLGEN01") +} +/// Shared slot dispatch entry. Dataset roles are prepared only after future-use +/// coverage and the accepted physical root cohort are both available. +#[allow(clippy::too_many_arguments)] +pub(crate) fn prepare_work( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut LegacyPreparationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + if cancel() { + return Ok(None); + } + let cohort = super::maintenance_legacy_generations_engine::prepare_root_cohort_boxed( + target, binding, memory, + )?; + let origin = staging_origin(target, domain, expected, memory)?; + let metadata = staging_metadata(target, &origin, memory)?; + let header = boxed_staging_header(&metadata, memory)?; + let native_mode = metadata + .payload() + .filter(|p| super::maintenance_legacy_generations_engine::is_native_payload(p)) + .map(super::maintenance_legacy_generations_engine::native_payload_mode) + .transpose()?; + let native = metadata + .payload() + .is_some_and(super::maintenance_legacy_generations_engine::is_native_payload) + || header + .as_ref() + .is_some_and(|h| h.kind() == stream::LegacyStreamKind::Association); + drop(metadata); + if native { + let mode = if let Some(mode) = native_mode { + mode + } else if header + .as_ref() + .and_then(|h| h.source_version()) + .is_some_and(|s| { + locks::path_identity(&target.state_root).ok() == Some(s.root_identity()) + }) + { + super::maintenance_legacy_generations_engine::LegacyNativeScope::DefaultDirect + } else { + super::maintenance_legacy_generations_engine::LegacyNativeScope::ManagedGeneration + }; + return prepare_native_work( + target, binding, domain, expected, state, mode, memory, cancel, + ); + } + if let Some(cohort) = cohort { + if let Some(work) = prepare_staging_work( + target, binding, domain, expected, state, cohort, memory, cancel, + )? { + return Ok(Some(work)); + } + } + prepare_native_work( + target, + binding, + domain, + expected, + state, + super::maintenance_legacy_generations_engine::LegacyNativeScope::DefaultDirect, + memory, + cancel, + ) +} +/// Called only after the execution permit and claimed request pins are released. +pub(crate) fn finish_work( + work: &mut LegacyWork, + state: &mut LegacyPreparationState, +) -> Result<(), FixedControlError> { + match &mut work.action { + Action::Native(native) => { + super::maintenance_legacy_generations_engine::finish_work(native, &mut state.native)?; + } + Action::Pending => {} + Action::Staging(work) => work.finish(state)?, + Action::Enrollment { + owner, + needs_confirmation, + scope, + .. + } => { + if owner.is_some() || scope.has_accepted_job(&work.memory)? { + state.interest = false; + *needs_confirmation = false; + } + drop(owner.take()); + } + Action::Administration { + owner, + needs_confirmation, + scope, + .. + } => { + if owner.is_some() || scope.has_accepted_job(&work.memory)? { + state.interest = false; + *needs_confirmation = false; + } + drop(owner.take()); + } + } + Ok(()) +} + +/// This phase consumes request-time source pins before any native/cache proof +/// job is opened. Durable Preparing is the only continuation after the claim. +#[inline(never)] +fn prepare_default_enrollment( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + state: &mut LegacyPreparationState, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let pair = super::inventory::default_direct(target); + if !pair.database.is_file() || !pair.manifest.is_file() { + return Ok(None); + } + let conventional = target.state_root.join("artifacts"); + let keyed = + super::layout::DirectLayout::new(&pair.database, &pair.manifest).artifact_root_path(); + let artifact = if conventional.is_dir() { + conventional + } else { + keyed + }; + if !artifact.is_dir() { + return Ok(None); + } + let mut scope = source::prepare_legacy_scope( + target, + binding, + LegacyScope::DefaultDirect { + pair: &pair, + artifact_root: &artifact, + }, + true, + memory, + )?; + scope.prepare_enrollment_metadata(state.request_metadata, memory)?; + scope.has_accepted_job(memory)?; + if !state.interest { + return Ok(None); + } + let floor = memory + .reserve(std::mem::size_of::() + 8192) + .map_err(FixedControlError::Resource)?; + MemoryBox::new( + LegacyWork { + action: Action::Enrollment { + scope, + owner: None, + needs_confirmation: true, + }, + memory: memory.clone(), + report: CandidateReport { + pending: true, + ..CandidateReport::default() + }, + _floor: floor, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) +} +pub(crate) struct LegacyStagingCohortLeaf { + association: super::maintenance_legacy_staging::StagingAssociation, + certificate: super::maintenance_legacy_staging::StagingPayloadCertificate, + original_source: super::maintenance_legacy_bootstrap::LegacyClosedVersion, +} +impl LegacyStagingCohortLeaf { + pub(crate) fn association(&self) -> &super::maintenance_legacy_staging::StagingAssociation { + &self.association + } + pub(crate) fn certificate( + &self, + ) -> &super::maintenance_legacy_staging::StagingPayloadCertificate { + &self.certificate + } + pub(crate) fn original_source( + &self, + ) -> &super::maintenance_legacy_bootstrap::LegacyClosedVersion { + &self.original_source + } +} + +use super::maintenance_legacy_staging as staging; +use super::maintenance_legacy_stream as stream; +use staging::ReplayStagingCertificates; + +struct StagingJob { + scope: MemoryBox, + owner: Option, + root: RootedDirectory, + original: Option, + projection: Option>, + pending: Option>, + seal: Option, + _floor: MemoryReservation, +} +// These fixed effect descriptors are retained in charged work and consumed without allocation under authority. +#[allow(clippy::large_enum_variant)] +enum StagingAction { + Recover, + Enroll(MemoryBox), + Stream(MemoryBox), + Retire(staging::StagingAssociation), + Leaf(MemoryBox), + Reopen(LegacyStagingCohortDone), + Activate, +} +struct StagingWork { + job: Option>, + action: StagingAction, + result: MemoryBox>, +} +fn staging_error(error: staging::StagingIssue) -> FixedControlError { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("legacy staging validation rejected: {error:?}"); + } + match error { + staging::StagingIssue::Resource(e) => FixedControlError::Resource(e), + staging::StagingIssue::Io(kind) => FixedControlError::Io(std::io::Error::from(kind)), + _ => invalid(), + } +} +fn staging_trace(operation: &str, error: FixedControlError) -> FixedControlError { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("legacy staging {operation}: {error:?}"); + } + let _ = operation; + error +} +impl StagingWork { + fn advance( + &mut self, + memory: &CleanupMemory, + grant: &mut MutationGrant, + ) -> Result<(), FixedControlError> { + let job: &mut StagingJob = self.job.as_mut().ok_or_else(invalid)?; + match &mut self.action { + StagingAction::Recover => { + job.scope + .recover_publication(job.owner.as_mut(), grant, memory)?; + } + StagingAction::Enroll(cohort) => { + match source::with_legacy_staging_enrollment_in_cohort( + &mut job.scope, + cohort, + grant, + memory, + &mut |_, _| Ok(()), + ) { + Ok((_, owner)) => { + job.original = Some(owner.closed_version()); + job.owner = Some(owner); + } + Err(FixedControlError::Invalid(FixedControlProblem::Busy)) + if job.scope.enrollment_pending() => {} + Err(e) => return Err(e), + } + } + StagingAction::Stream(step) => { + *self.result = Some(step.advance(grant)?); + } + StagingAction::Retire(association) => { + source::begin_legacy_staging_retirement( + &mut job.scope, + job.owner.as_mut().ok_or_else(invalid)?, + association, + grant, + memory, + )?; + } + StagingAction::Leaf(leaf) => { + leaf.with_admitted( + &mut job.scope, + job.owner.as_ref().ok_or_else(invalid)?, + grant, + 3, + memory, + &mut |actual, grant| { + grant.check_time()?; + actual + .source_parent() + .unlink_file(actual.name(), actual.payload_identity())?; + grant.check_time()?; + actual.source_parent().sync_namespace()?; + Ok(()) + }, + )?; + } + StagingAction::Activate => { + source::reopen_completed_legacy_staging( + &mut job.scope, + job.owner.as_mut().ok_or_else(invalid)?, + grant, + memory, + )?; + } + StagingAction::Reopen(done) => { + source::finish_legacy_staging_cohort( + &mut job.scope, + job.owner.as_mut().ok_or_else(invalid)?, + done, + grant, + memory, + )?; + } + } + Ok(()) + } + fn finish(&mut self, state: &mut LegacyPreparationState) -> Result<(), FixedControlError> { + if let Some(stream::CommittedLegacyStreamStep::Checkpoint { ack, .. }) = self.result.take() + { + let job: &mut StagingJob = self.job.as_mut().ok_or_else(invalid)?; + if let Some(batch) = job.pending.as_ref() { + if batch.checkpoint.digest() != ack { + return Err(invalid()); + } + job.projection + .as_mut() + .ok_or_else(invalid)? + .acknowledge(ack) + .map_err(staging_error)?; + job.pending = None; + } + } + if matches!(self.action, StagingAction::Activate) + || matches!(self.action, StagingAction::Recover) + && self + .job + .as_ref() + .and_then(|j| j.owner.as_ref()) + .is_some_and(|o| { + o.closed_version().phase() + == super::maintenance_legacy_guard::LegacyPhase::Active + }) + { + drop(self.job.take()); + state.staging = None; + } else { + state.staging = self.job.take(); + } + Ok(()) + } +} +#[inline(never)] +fn staging_work( + job: MemoryBox, + action: StagingAction, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let floor = memory + .reserve(std::mem::size_of::() + 8192) + .map_err(FixedControlError::Resource)?; + let staging = MemoryBox::new( + StagingWork { + job: Some(job), + action, + result: MemoryBox::new(None, memory).map_err(FixedControlError::Resource)?, + }, + memory, + ) + .map_err(FixedControlError::Resource)?; + MemoryBox::new( + LegacyWork { + action: Action::Staging(staging), + memory: memory.clone(), + report: CandidateReport { + pending: true, + ..CandidateReport::default() + }, + _floor: floor, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +fn staging_step( + target: &MaintenanceTarget, + scope: &super::maintenance_admission::RootOriginBinding, + action: stream::LegacyStreamAction, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + stream::prepare_legacy_stream_step(target, scope, action, memory) +} + +#[inline(never)] +pub(crate) fn staging_origin( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + memory: &CleanupMemory, +) -> Result { + let canonical = prepare_canonical(target, domain, expected, memory)?; + Ok(canonical.origin_scope()) +} +#[inline(never)] +pub(crate) fn staging_metadata( + target: &MaintenanceTarget, + scope: &super::maintenance_admission::RootOriginBinding, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + super::maintenance_admission::prepare_private_metadata_boxed(target, scope, memory) +} + +#[inline(never)] +fn boxed_staging_header( + metadata: &super::maintenance_admission::PreparedPrivateMetadata, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + metadata + .payload() + .and_then(|payload| stream::LegacyStreamHeader::decode(payload).ok()) + .map(|header| MemoryBox::new(header, memory).map_err(FixedControlError::Resource)) + .transpose() +} +#[inline(never)] +fn boxed_staging_source_scope( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + path: &std::path::Path, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + source::prepare_legacy_staging_scope_boxed(target, binding, path, memory) +} + +#[allow(clippy::too_many_arguments)] +#[inline(never)] +fn prepare_staging_work( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut LegacyPreparationState, + cohort: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let _temporary = memory.reserve(65536).map_err(FixedControlError::Resource)?; + let scope = staging_origin(target, domain, expected, memory)?; + let metadata = staging_metadata(target, &scope, memory)?; + let header = boxed_staging_header(&metadata, memory)?; + if header + .as_ref() + .is_some_and(|h| h.kind() != stream::LegacyStreamKind::Staging) + { + return Ok(None); + } + if state.staging.is_none() { + state.staging = prepare_staging_job(target, binding, &cohort, header.as_deref(), memory)?; + if state.staging.is_none() { + return Ok(None); + } + } + let job = state.staging.take().ok_or_else(invalid)?; + continue_staging_work(target, job, cohort, scope, metadata, header, memory, cancel) +} + +#[inline(never)] +fn prepare_staging_job( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + cohort: &source::AcceptedLegacyRootCohort, + header: Option<&stream::LegacyStreamHeader>, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let path = target.state_root.join("native-staging"); + let identity = match locks::path_identity(&path) { + Ok(id) => id, + Err(_) if !path.exists() => return Ok(None), + Err(_) => return Err(invalid()), + }; + let root = RootedDirectory::open_readonly_root(&path, identity)?; + let mut source_scope = boxed_staging_source_scope(target, binding, &path, memory) + .map_err(|e| staging_trace("scope preparation", e))?; + source_scope + .prepare_enrollment_in_cohort(cohort, memory) + .map_err(|e| staging_trace("cohort enrollment preparation", e))?; + let actual = source::observe_legacy_staging_source_version(&mut source_scope, memory) + .map_err(|e| staging_trace("source version observation", e))?; + let owner = match actual { + Some(_) if source_scope.publication_recovery_pending() => None, + Some(actual) => Some( + source::reopen_legacy_staging_owner(&mut source_scope, &actual, memory) + .map_err(|e| staging_trace("owner reopen", e))?, + ), + None => None, + }; + if owner.is_none() { + let mut known = false; + let mut scratch = [0; 4096]; + root.visit_entries(&mut scratch, &mut |name| { + known |= staging::staging_profile(name).is_some(); + Ok(()) + })?; + if !known { + return Ok(None); + } + } + let original = header.and_then(|h| h.source_version()).or(actual); + if let (Some(original), Some(owner)) = (original, owner.as_ref()) { + if !owner.closed_version().same_job_source(&original) { + return Err(invalid()); + } + } + let job = MemoryBox::new( + StagingJob { + scope: source_scope, + owner, + root, + original, + projection: None, + pending: None, + seal: None, + _floor: memory + .reserve(std::mem::size_of::() + 32768) + .map_err(FixedControlError::Resource)?, + }, + memory, + ) + .map_err(FixedControlError::Resource)?; + Ok(Some(job)) +} + +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn continue_staging_work( + target: &MaintenanceTarget, + mut job: MemoryBox, + cohort: MemoryBox, + scope: super::maintenance_admission::RootOriginBinding, + metadata: MemoryBox, + header: Option>, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let recovering = { + let job = &mut *job; + job.scope + .refresh_publication_recovery(memory, job.owner.as_ref()) + .map_err(|e| staging_trace("publication recovery preparation", e))? + }; + if recovering { + return staging_work(job, StagingAction::Recover, memory); + } + if job.owner.is_none() { + job.scope.refresh_enrollment_preparation(memory)?; + return staging_work(job, StagingAction::Enroll(cohort), memory); + } + if !job.owner.as_ref().ok_or_else(invalid)?.is_current(memory)? { + return Err(staging_trace("retained owner changed", invalid())); + } + let original = job.original.ok_or_else(invalid)?; + if let Some(header) = header.as_ref() { + if header + .source_version() + .is_none_or(|s| !s.same_job_source(&original)) + || header.context_digest() != original.context_digest() + { + return Err(invalid()); + } + } else { + if job + .owner + .as_ref() + .ok_or_else(invalid)? + .closed_version() + .phase() + == super::maintenance_legacy_guard::LegacyPhase::Removed + { + if super::maintenance_candidate::idle_control_cursor( + metadata.payload().ok_or_else(invalid)?, + scope, + ) + .map_err(|_| invalid())? + .is_none() + || metadata.stream_len() != 0 + { + return Err(invalid()); + } + { + let StagingJob { scope, owner, .. } = &mut *job; + scope.prepare_transition( + owner.as_ref().ok_or_else(invalid)?, + super::maintenance_legacy_guard::LegacyPhase::Active, + memory, + )?; + } + return staging_work(job, StagingAction::Activate, memory); + } + if metadata.stream_len() != 0 { + return Err(invalid()); + } + let cursor = super::maintenance_candidate::idle_control_cursor( + metadata.payload().ok_or_else(invalid)?, + scope, + ) + .map_err(|_| invalid())? + .ok_or_else(invalid)? + .encode(); + drop(metadata); + let action = stream::LegacyStreamAction::Start { + cursor, + source: original, + context: original.context_digest(), + kind: stream::LegacyStreamKind::Staging, + }; + return staging_work( + job, + StagingAction::Stream(staging_step(target, &scope, action, memory)?), + memory, + ); + } + let header = header.unwrap(); + if job + .owner + .as_ref() + .ok_or_else(invalid)? + .closed_version() + .phase() + == super::maintenance_legacy_guard::LegacyPhase::Removed + { + let action = if header.resetting() { + stream::LegacyStreamAction::FinishReset + } else if header.done() { + stream::LegacyStreamAction::BeginReset + } else { + return Err(invalid()); + }; + drop(metadata); + return staging_work( + job, + StagingAction::Stream(staging_step(target, &scope, action, memory)?), + memory, + ); + } + let seal = { + let replay = stream::LegacyStreamReplay::new( + &metadata, + &original, + original.context_digest(), + memory, + )?; + replay.staging_seal()? + }; + if let Some(seal) = seal { + return prepare_sealed_staging_work( + target, job, scope, metadata, header, original, seal, memory, cancel, + ); + } + prepare_staging_projection( + target, job, scope, metadata, header, original, memory, cancel, + ) +} + +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn prepare_sealed_staging_work( + target: &MaintenanceTarget, + mut job: MemoryBox, + scope: super::maintenance_admission::RootOriginBinding, + metadata: MemoryBox, + header: MemoryBox, + original: LegacyClosedVersion, + seal: staging::StagingAssociation, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let replay = + stream::LegacyStreamReplay::new(&metadata, &original, original.context_digest(), memory)?; + authenticate_staging_membership(&replay, &seal, &original, &job.root, memory, cancel) + .map_err(|e| staging_trace("sealed membership authentication", e))?; + if !header.done() { + drop(metadata); + return staging_work( + job, + StagingAction::Stream(staging_step( + target, + &scope, + stream::LegacyStreamAction::Done, + memory, + )?), + memory, + ); + } + match job + .owner + .as_ref() + .ok_or_else(invalid)? + .closed_version() + .phase() + { + super::maintenance_legacy_guard::LegacyPhase::Preparing => { + { + let StagingJob { scope, owner, .. } = &mut *job; + scope.prepare_transition( + owner.as_ref().ok_or_else(invalid)?, + super::maintenance_legacy_guard::LegacyPhase::Retiring, + memory, + )?; + } + staging_work(job, StagingAction::Retire(seal), memory) + } + super::maintenance_legacy_guard::LegacyPhase::Retiring => { + let mut chosen = None; + replay + .replay(&mut |event| { + if cancel() { + return Err(staging::StagingIssue::Cancelled); + } + if let staging::StagingReplayEvent::Payload(certificate) = event { + let address = certificate.witness_address(); + match job + .root + .open_readonly_file(&address.name[..usize::from(address.name_len)]) + { + Ok(file) => { + address + .stamp + .recheck(&file) + .map_err(|_| staging::StagingIssue::Changed)?; + if chosen.is_none() { + chosen = Some(certificate); + } + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(e.into()), + } + } + Ok(()) + }) + .map_err(staging_error)?; + if let Some(certificate) = chosen { + let membership = LegacyStagingCohortLeaf { + association: seal, + certificate, + original_source: original, + }; + let leaf = staging::prepare_staging_leaf( + &membership, + job.owner.as_ref().ok_or_else(invalid)?, + &job.root, + memory, + cancel, + ) + .map_err(staging_error)?; + return staging_work(job, StagingAction::Leaf(leaf), memory); + } + let stamp = + FixedFileStamp::capture(&job.root.pin_handle()?, FixedControlKind::Directory)?; + let done = LegacyStagingCohortDone { + original, + directory: stamp, + }; + { + let StagingJob { scope, owner, .. } = &mut *job; + scope.prepare_transition( + owner.as_ref().ok_or_else(invalid)?, + super::maintenance_legacy_guard::LegacyPhase::Removed, + memory, + )?; + } + staging_work(job, StagingAction::Reopen(done), memory) + } + _ => Err(invalid()), + } +} + +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn prepare_staging_projection( + target: &MaintenanceTarget, + mut job: MemoryBox, + scope: super::maintenance_admission::RootOriginBinding, + metadata: MemoryBox, + header: MemoryBox, + original: LegacyClosedVersion, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let action = prepare_staging_projection_action( + target, &mut job, &metadata, &header, original, memory, cancel, + )?; + // Keep the exact captured metadata that authenticated the projection. + // The eventual guarded mutation rechecks these stamps and slot images; + // no fresh metadata can relabel an action from an older source prefix. + let step = stream::prepare_stream_step_from_metadata(metadata, &scope, action, memory)?; + staging_work(job, StagingAction::Stream(step), memory) +} + +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn prepare_staging_projection_action( + target: &MaintenanceTarget, + job: &mut StagingJob, + metadata: &super::maintenance_admission::PreparedPrivateMetadata, + header: &stream::LegacyStreamHeader, + original: LegacyClosedVersion, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + let replay = + stream::LegacyStreamReplay::new(metadata, &original, original.context_digest(), memory)?; + if job.projection.is_none() { + if let Ok(bytes) = replay.checkpoint() { + let checkpoint = staging::StagingProjectionCheckpoint::decode( + bytes.as_slice().try_into().map_err(|_| invalid())?, + ) + .map_err(staging_error)?; + job.projection = Some( + staging::StagingProjection::restore( + &job.root, + checkpoint, + original.context_digest(), + memory, + ) + .map_err(staging_error)?, + ); + } + } + let previous = job.projection.as_ref().map(|p| p.checkpoint()); + if job.projection.is_none() || previous.as_ref().is_some_and(|p| p.finished()) { + let mut chosen = [0; 255]; + let mut chosen_len = 0usize; + let mut scratch = [0; 4096]; + job.root.visit_entries(&mut scratch, &mut |name| { + if staging::staging_profile(name).is_none() + || previous.as_ref().is_some_and(|p| name <= p.payload_name()) + || chosen_len > 0 && name >= &chosen[..chosen_len] + { + return Ok(()); + } + chosen[..name.len()].copy_from_slice(name); + chosen_len = name.len(); + Ok(()) + })?; + if chosen_len == 0 { + let association = staging::associate_staging_bundle( + &job.root, + &target.repository_root, + original.context_digest(), + &replay, + 256, + memory, + cancel, + &|| { + if job + .owner + .as_ref() + .ok_or(staging::StagingIssue::Changed)? + .is_current(memory) + .map_err(|_| staging::StagingIssue::Changed)? + { + Ok(()) + } else { + Err(staging::StagingIssue::Changed) + } + }, + ) + .map_err(staging_error)?; + job.seal = Some(association); + } else { + job.projection = Some( + staging::StagingProjection::new( + &job.root, + &chosen[..chosen_len], + original.context_digest(), + memory, + ) + .map_err(staging_error)?, + ); + } + } + if let Some(association) = job.seal { + let mut bytes = [0; 512]; + association.encode(&mut bytes); + let frame = stream::LegacyStreamFrame::new( + stream::LegacyFrameKind::StagingSeal, + original.context_digest(), + header.sequence(), + header.chain_digest(), + &bytes, + memory, + )?; + let action = if header.pending() { + stream::LegacyStreamAction::FinishAppend(frame) + } else { + stream::LegacyStreamAction::BeginAppend(frame) + }; + return Ok(action); + } + if job.pending.is_none() { + let StagingJob { + projection, root, .. + } = job; + let pending = projection + .as_mut() + .ok_or_else(invalid)? + .advance(root, memory, cancel) + .map_err(staging_error)?; + job.pending = pending; + } + let batch = job.pending.as_ref().ok_or_else(invalid)?; + let mut bytes = [0; 3072]; + let kind = header.pending_kind().unwrap_or_else(|| { + if header.awaits_checkpoint() { + stream::LegacyFrameKind::StagingCheckpoint + } else if batch.certificate.is_some() { + stream::LegacyFrameKind::StagingPayload + } else if batch.rows[0].is_some() { + stream::LegacyFrameKind::StagingRows + } else { + stream::LegacyFrameKind::StagingCheckpoint + } + }); + let len = match kind { + stream::LegacyFrameKind::StagingRows => { + let mut len = 0; + for row in batch.rows.iter().flatten() { + row.encode((&mut bytes[len..len + 768]).try_into().unwrap()); + len += 768; + } + len + } + stream::LegacyFrameKind::StagingPayload => { + batch + .certificate + .ok_or_else(invalid)? + .encode((&mut bytes[..768]).try_into().unwrap()); + 768 + } + stream::LegacyFrameKind::StagingCheckpoint => { + batch + .checkpoint + .encode((&mut bytes[..768]).try_into().unwrap()); + 768 + } + _ => return Err(invalid()), + }; + let frame = stream::LegacyStreamFrame::new( + kind, + original.context_digest(), + header.sequence(), + header.chain_digest(), + &bytes[..len], + memory, + )?; + let action = if header.pending() { + stream::LegacyStreamAction::FinishAppend(frame) + } else { + stream::LegacyStreamAction::BeginAppend(frame) + }; + Ok(action) +} + +/// Authenticate the complete immutable certificate set and row-chain before +/// reconstructing membership from a controlled journal. Parsed facts alone +/// cannot reach this constructor: actual rooted source custody is also checked. +fn authenticate_staging_membership( + replay: &stream::LegacyStreamReplay<'_>, + association: &staging::StagingAssociation, + original: &LegacyClosedVersion, + root: &RootedDirectory, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result<(), FixedControlError> { + use sha2::{Digest, Sha256}; + let _floor = memory.reserve(16384).map_err(FixedControlError::Resource)?; + if association.root_identity() != root.identity() + || association.source_context() != original.context_digest() + { + return Err(invalid()); + } + let mut digest = Sha256::new(); + let mut payloads = 0u64; + let mut rows = 0u64; + let mut current = None; + let mut chain = [0u8; 32]; + let mut ordinal = 0u64; + let mut previous = [0; 255]; + let mut previous_len = 0usize; + replay + .replay(&mut |event| { + if cancel() { + return Err(staging::StagingIssue::Cancelled); + } + match event { + staging::StagingReplayEvent::Payload(certificate) => { + let address = certificate.witness_address(); + let name = &address.name[..usize::from(address.name_len)]; + if current.is_some() + || certificate.rows == 0 + || certificate.source_context != association.source_context() + || address.parent != root.identity() + || previous_len > 0 && name <= &previous[..previous_len] + { + return Err(staging::StagingIssue::Unknown); + } + previous[..name.len()].copy_from_slice(name); + previous_len = name.len(); + current = Some(certificate); + ordinal = 0; + chain = [0; 32]; + } + staging::StagingReplayEvent::Row(row) => { + let certificate = current.as_ref().ok_or(staging::StagingIssue::Unknown)?; + let address = certificate.witness_address(); + if row.ordinal != ordinal + || row.parent != address.parent + || row.name != address.name + || row.name_len != address.name_len + || row.stamp != address.stamp + || row.profile != address.profile + { + return Err(staging::StagingIssue::Unknown); + } + let mut bytes = [0; 768]; + row.encode(&mut bytes); + let mut next = Sha256::new(); + next.update(b"CGLegacyStagingRowChain01"); + next.update(chain); + next.update(bytes); + chain = next.finalize().into(); + ordinal = ordinal + .checked_add(1) + .ok_or(staging::StagingIssue::Unknown)?; + } + staging::StagingReplayEvent::EndPayload => { + let certificate = current.take().ok_or(staging::StagingIssue::Unknown)?; + if certificate.rows != ordinal || certificate.witness_chain != chain { + return Err(staging::StagingIssue::Unknown); + } + digest.update(certificate.certificate_digest()); + rows = rows + .checked_add(ordinal) + .ok_or(staging::StagingIssue::Unknown)?; + payloads = payloads + .checked_add(1) + .ok_or(staging::StagingIssue::Unknown)?; + } + } + Ok(()) + }) + .map_err(staging_error)?; + if current.is_some() + || payloads != association.payload_count() + || rows != association.row_count() + || <[u8; 32]>::from(digest.finalize()) != association.certificate_set_digest() + { + return Err(invalid()); + } + Ok(()) +} + +/// Minted only after authenticating the completed exact private cohort and +/// observing every recorded payload absent under the unchanged closed source. +pub(crate) struct LegacyStagingCohortDone { + original: LegacyClosedVersion, + directory: FixedFileStamp, +} +impl LegacyStagingCohortDone { + pub(crate) fn original_source(&self) -> &LegacyClosedVersion { + &self.original + } + pub(crate) fn directory_stamp(&self) -> FixedFileStamp { + self.directory + } +} + +#[allow(clippy::too_many_arguments)] +#[inline(never)] +pub(crate) fn prepare_native_work( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut LegacyPreparationState, + mode: super::maintenance_legacy_generations_engine::LegacyNativeScope, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let work = super::maintenance_legacy_generations_engine::prepare_work( + target, + binding, + domain, + expected, + mode, + &mut state.native, + memory, + cancel, + )?; + let action = if let Some(work) = work { + Action::Native(work) + } else if state.native.has_parked_payloads() { + Action::Pending + } else { + return Ok(None); + }; + let floor = memory + .reserve(std::mem::size_of::() + 8192) + .map_err(FixedControlError::Resource)?; + MemoryBox::new( + LegacyWork { + action, + memory: memory.clone(), + report: CandidateReport { + pending: true, + ..CandidateReport::default() + }, + _floor: floor, + }, + memory, + ) + .map(Some) + .map_err(FixedControlError::Resource) +} + +/// Names/context are advisory discovery only. No bytes or scope flag here can +/// construct a source owner, origin fact, or an eligibility capability. +#[inline(never)] +fn discover_operator_interest( + target: &MaintenanceTarget, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + let _floor = memory.reserve(65536).map_err(FixedControlError::Resource)?; + let configured = + super::maintenance_projection::project_binding(target, memory).map_err(|_| invalid())?; + if configured.direct.is_some() { + return Ok(false); + } + let staging = target.state_root.join("native-staging"); + if let Ok(id) = locks::path_identity(&staging) { + let root = RootedDirectory::open_readonly_root(&staging, id)?; + let mut found = false; + let mut scratch = [0; 4096]; + root.visit_entries(&mut scratch, &mut |name| { + if !cancel() + && staging::staging_profile(name).is_some() + && root.open_readonly_file(name).is_ok() + { + found = true; + } + Ok(()) + })?; + if found { + return Ok(true); + } + } + if cancel() { + return Ok(false); + } + let pair = super::inventory::default_direct(target); + if pair.database.is_file() && pair.manifest.is_file() { + let layout = super::layout::DirectLayout::new(&pair.database, &pair.manifest); + if super::maintenance_ownership::prepare_automatic_direct(&layout, memory)?.is_none() { + return Ok(true); + } + } + let generations = configured.storage.join("generations"); + let Some(identity) = locks::path_identity(&generations).ok() else { + return Ok(false); + }; + let root = RootedDirectory::open_readonly_root(&generations, identity)?; + let mut found = false; + let mut scratch = [0; 4096]; + root.visit_entries(&mut scratch, &mut |name| { + if cancel() || !name.starts_with(b"gen-") || name.len() > 132 { + return Ok(()); + } + let Ok(text) = std::str::from_utf8(&name[4..]) else { + return Ok(()); + }; + if super::layout::validate_generation_id(text).is_err() { + return Ok(()); + } + let Ok(member) = root.open_directory_readonly(name) else { + return Ok(()); + }; + let database = member.open_readonly_file(b"graph.ldb").is_ok(); + let manifest = member.open_readonly_file(b"manifest.json").is_ok(); + if database + && manifest + && member + .open_readonly_file(super::maintenance_generation_gate::ORIGIN_NAME) + .is_err() + { + found = true; + } + Ok(()) + })?; + Ok(found) +} diff --git a/src/storage/maintenance_legacy_generations_engine.rs b/src/storage/maintenance_legacy_generations_engine.rs new file mode 100644 index 00000000..25e0298a --- /dev/null +++ b/src/storage/maintenance_legacy_generations_engine.rs @@ -0,0 +1,2345 @@ +//! Historical native cohorts. A sealed full association is retained verbatim +//! until actual source completion; no decoded bytes construct producer origin. +use super::inventory::MaintenanceTarget; +use super::locks::{ + self, FileIdentity, FixedControlError, FixedControlKind, FixedControlProblem, FixedFileStamp, + FixedLockedFile, LockMode, RootedDirectory, +}; +use super::maintenance_admission::{ + self as admission, PreparedControlUpdate, PreparedPrivateMetadata, PreparedTargetBinding, +}; +use super::maintenance_budget::{CleanupMemory, MemoryBox, MutationGrant, ReservedBytes}; +use super::maintenance_candidate::CandidateReport; +use super::maintenance_legacy_association::{ + LegacyAssociation, LegacyCacheCertificate, ReplayLegacyCertificates, + SelectedBackupAssociationState, +}; +use super::maintenance_legacy_bootstrap::{ + self as source, AcceptedLegacyRootCohort, LegacyClosedVersion, LegacyScope, LegacySourceOwner, + PreparedLegacyScope, +}; +use super::maintenance_legacy_guard::LegacyPhase; +use super::maintenance_legacy_stream::{ + self as stream, AssociationSourceProgress, CommittedLegacyStreamStep, LegacyFrameKind, + LegacyStreamAction, LegacyStreamFrame, LegacyStreamHeader, LegacyStreamReplay, + PreparedLegacyStreamStep, +}; +use super::maintenance_ownership::{ReferenceDomain, ReferenceEpoch}; +use super::maintenance_scheduler::LegacyQuiescenceConfirmation; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom}; +use std::path::PathBuf; + +const MAGIC: &[u8; 8] = b"CGLGEN01"; +const CONTROL: usize = 2048; +const INSPECT: u8 = 0; +const UNLINK: u8 = 1; +const ROOT: u8 = 2; +const FINISH: u8 = 3; +const RESET: u8 = 4; +const CACHE: u16 = 100; +const NAMES: [&[u8]; 14] = [ + b"graph.ldb", + b"manifest.json", + b"metadata.json", + b"READY", + b"graph.ldb.wal", + b"graph.ldb.tmp", + b"graph.ldb.search.meta.json", + b"graph.ldb.search.lexicon.bin", + b"graph.ldb.search.postings.bin", + b"graph.ldb.search.doc_lengths.bin", + b"graph.ldb.search.documents.bin", + b"graph.ldb.search.document_offsets.bin", + b"retired.json", + b".generation-legacy-final", +]; +fn invalid() -> FixedControlError { + FixedControlError::Invalid(FixedControlProblem::Changed) +} +fn boxed(value: T, memory: &CleanupMemory) -> Result, FixedControlError> { + MemoryBox::new(value, memory).map_err(FixedControlError::Resource) +} +fn prepare_transition( + job: &mut Job, + next: LegacyPhase, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + job.scope + .prepare_transition(job.owner.as_ref().ok_or_else(invalid)?, next, memory) +} +fn pending() -> CandidateReport { + CandidateReport { + pending: true, + ..CandidateReport::default() + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum LegacyNativeScope { + ManagedGeneration, + DefaultDirect, +} +#[derive(Default)] +pub(crate) struct LegacyNativePreparationState { + job: Option>, + association: SelectedBackupAssociationState, +} +impl LegacyNativePreparationState { + pub(crate) fn clear(&mut self) { + self.association.clear(); + drop(self.job.take()); + } + pub(crate) fn has_parked_payloads(&self) -> bool { + !self.association.needs_source_handles() + || self.job.as_ref().is_some_and(|j| j.proof.is_some()) + } +} +struct Job { + mode: LegacyNativeScope, + scope: MemoryBox, + owner: Option, + cohort: Option>, + automatic: Option>, + member: PathBuf, + storage: PathBuf, + member_name: [u8; 128], + member_len: u8, + proof: Option<(LegacyAssociation, File, File)>, + current: Option, +} +struct Control { + mode: LegacyNativeScope, + stage: u8, + cache_started: bool, + index: u16, + source: LegacyClosedVersion, + member: [u8; 128], + member_len: u8, + offset: u64, + frame: [u8; 32], + key: [u8; 32], + stamp: Option, + digest: [u8; 32], + parent: Option, + directory: Option, + cursor: [u8; stream::CURSOR_BYTES], +} +impl Control { + fn encode(&self) -> [u8; CONTROL] { + let mut b = [0; CONTROL]; + b[..8].copy_from_slice(MAGIC); + b[8] = self.mode as u8; + b[9] = self.stage; + b[10] = self.member_len; + b[11] = u8::from(self.cache_started); + b[12..14].copy_from_slice(&self.index.to_le_bytes()); + self.source + .encode_checkpoint((&mut b[16..528]).try_into().unwrap()); + b[528..656].copy_from_slice(&self.member); + b[656..664].copy_from_slice(&self.offset.to_le_bytes()); + b[664..696].copy_from_slice(&self.frame); + b[696..728].copy_from_slice(&self.key); + if let Some(stamp) = self.stamp { + stamp.encode_checkpoint((&mut b[728..808]).try_into().unwrap()); + } + b[808..840].copy_from_slice(&self.digest); + for (at, id) in [(840, self.parent), (856, self.directory)] { + if let Some(id) = id { + let (a, c) = id.fixed_words(); + b[at..at + 8].copy_from_slice(&a.to_le_bytes()); + b[at + 8..at + 16].copy_from_slice(&c.to_le_bytes()); + } + } + b[872..872 + stream::CURSOR_BYTES].copy_from_slice(&self.cursor); + let digest = Sha256::digest(&b[..CONTROL - 32]); + b[CONTROL - 32..].copy_from_slice(&digest); + b + } + fn decode(b: &[u8]) -> Result { + if b.len() != CONTROL + || &b[..8] != MAGIC + || Sha256::digest(&b[..CONTROL - 32])[..] != b[CONTROL - 32..] + || b[8] > 1 + || b[9] > RESET + || b[10] > 128 + || b[11] > 1 + || b[14..16] + .iter() + .chain(b[872 + stream::CURSOR_BYTES..CONTROL - 32].iter()) + .any(|v| *v != 0) + { + return Err(invalid()); + } + let id = |at: usize| { + let a = u64::from_le_bytes(b[at..at + 8].try_into().unwrap()); + let c = u64::from_le_bytes(b[at + 8..at + 16].try_into().unwrap()); + if a == 0 && c == 0 { + None + } else { + Some(FileIdentity::from_fixed_words(a, c)) + } + }; + let result = Self { + mode: if b[8] == 0 { + LegacyNativeScope::ManagedGeneration + } else { + LegacyNativeScope::DefaultDirect + }, + stage: b[9], + cache_started: b[11] != 0, + member_len: b[10], + index: u16::from_le_bytes(b[12..14].try_into().unwrap()), + source: LegacyClosedVersion::decode_checkpoint(b[16..528].try_into().unwrap())?, + member: b[528..656].try_into().unwrap(), + offset: u64::from_le_bytes(b[656..664].try_into().unwrap()), + frame: b[664..696].try_into().unwrap(), + key: b[696..728].try_into().unwrap(), + stamp: if b[728..808].iter().all(|x| *x == 0) { + None + } else { + Some(FixedFileStamp::decode_checkpoint( + b[728..808].try_into().unwrap(), + )?) + }, + digest: b[808..840].try_into().unwrap(), + parent: id(840), + directory: id(856), + cursor: b[872..872 + stream::CURSOR_BYTES].try_into().unwrap(), + }; + if result.member[usize::from(result.member_len)..] + .iter() + .any(|b| *b != 0) + || result.mode == LegacyNativeScope::ManagedGeneration + && !super::maintenance_generation_gate::component( + &result.member[..usize::from(result.member_len)], + ) + { + return Err(invalid()); + } + Ok(result) + } +} +pub(crate) fn is_native_payload(bytes: &[u8]) -> bool { + bytes.starts_with(MAGIC) +} +pub(crate) fn native_payload_mode(bytes: &[u8]) -> Result { + Control::decode(bytes).map(|control| control.mode) +} +enum Action { + Recover, + Enroll, + Association(MemoryBox), + Retire, + Custody(MemoryBox), + Metadata(MemoryBox), + Leaf(MemoryBox), + Root(MemoryBox), + Finish, + Complete, +} +struct MetadataAction { + metadata: MemoryBox, + update: MemoryBox, + reset: Option, +} +struct LeafAction { + metadata: MemoryBox, + update: MemoryBox, + control: Control, + name: Vec, + file: Option, + invalidation: Option>, +} +struct RootAction { + metadata: MemoryBox, + update: MemoryBox, + controls: [Option; 5], + held: [Option; 5], +} +pub(crate) struct LegacyNativeWork { + job: Option>, + action: Action, + memory: CleanupMemory, + committed: Option, + report: CandidateReport, + done: bool, +} +impl LegacyNativeWork { + pub(crate) fn requires_legacy_confirmation(&self) -> bool { + false + } +} +fn work( + job: MemoryBox, + action: Action, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + boxed( + LegacyNativeWork { + job: Some(job), + action, + memory: memory.clone(), + committed: None, + report: pending(), + done: false, + }, + memory, + ) + .map(Some) +} +#[inline(never)] +fn scope_box( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + mode: LegacyNativeScope, + storage: &std::path::Path, + member: &std::path::Path, + artifact: &std::path::Path, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let prepared = match mode { + LegacyNativeScope::ManagedGeneration => source::prepare_legacy_scope( + target, + binding, + LegacyScope::ManagedGeneration { + storage_root: storage, + generation_root: member, + }, + false, + memory, + )?, + LegacyNativeScope::DefaultDirect => source::prepare_legacy_scope( + target, + binding, + LegacyScope::DefaultDirect { + pair: &super::inventory::default_direct(target), + artifact_root: artifact, + }, + true, + memory, + )?, + }; + Ok(prepared) +} +// Presence is an advisory early exit only. Existing bytes still go through +// the complete rooted cohort observer before they can supply any permission. +#[inline(never)] +fn root_cohort_control_exists(domain: &ReferenceDomain) -> Result { + let path = domain.artifact_root().parent().ok_or_else(invalid)?; + let root = RootedDirectory::open_readonly_root( + path, + locks::path_identity(path).map_err(|_| invalid())?, + )?; + match root.open_readonly_file(super::maintenance_legacy_guard::ADMIN_STATE) { + Ok(_) => Ok(true), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(error.into()), + } +} +#[inline(never)] +pub(crate) fn prepare_root_cohort_boxed( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + source::observe_accepted_root_cohort_boxed(target, binding, memory) +} +// Advisory selection runs off-permit and retains only the smallest eligible +// name. The selected source still needs the full enrollment and mutation proofs. +fn select_generation_name( + generations: &RootedDirectory, + root: FileIdentity, + current: &[u8], + active_source: Option, + name: &mut [u8; 128], + len: &mut usize, + cancel: &dyn Fn() -> bool, +) -> Result<(), std::io::Error> { + let mut scratch = [0; 4096]; + generations.visit_entries(&mut scratch, &mut |entry| { + if cancel() { + return Err(std::io::ErrorKind::Interrupted.into()); + } + if !entry.starts_with(b"gen-") + || entry.len() > 128 + || !super::maintenance_generation_gate::component(entry) + { + return Ok(()); + } + if entry[4..] == *current { + return Ok(()); + } + let dir = match generations.open_directory_readonly(entry) { + Ok(d) => d, + Err(_) => return Ok(()), + }; + if active_source + .is_some_and(|s| s.root_identity() != root || s.member_identity() != dir.identity()) + { + return Ok(()); + } + match dir.open_readonly_file(b".generation-origin") { + Ok(_) => return Ok(()), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(e), + } + if *len == 0 || entry < &name[..*len] { + name.fill(0); + name[..entry.len()].copy_from_slice(entry); + *len = entry.len(); + } + Ok(()) + })?; + Ok(()) +} +#[inline(never)] +fn select_job( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + mode: LegacyNativeScope, + metadata: &PreparedPrivateMetadata, + cohort: Option>, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + if cohort.is_none() && mode == LegacyNativeScope::ManagedGeneration { + return Ok(None); + } + let config = + super::maintenance_projection::project_binding(target, memory).map_err(|_| invalid())?; + let mut name = [0; 128]; + let mut len = 0usize; + let active_control = metadata + .payload() + .filter(|p| is_native_payload(p)) + .map(Control::decode) + .transpose()?; + if active_control.as_ref().is_some_and(|c| c.mode != mode) { + return Ok(None); + } + let active_source = if let Some(c) = active_control.as_ref() { + name = c.member; + len = usize::from(c.member_len); + Some(c.source) + } else { + metadata + .payload() + .and_then(|p| LegacyStreamHeader::decode(p).ok()) + .and_then(|h| h.source_version()) + }; + let (storage, mut member, mut artifact) = match mode { + LegacyNativeScope::ManagedGeneration => { + if config.direct.is_some() { + return Ok(None); + } + let storage = config.storage; + let root = RootedDirectory::open_readonly_root( + &storage, + locks::path_identity(&storage).map_err(|_| invalid())?, + )?; + let current = + super::maintenance_generation_gate::read_current(&root).map_err(|_| invalid())?; + // Unsettled run journals can still protect their base/candidate. + // Start only at an actually empty registry; terminal runs get their + // own cursor turn before this source is closed. + if active_source.is_none() && !managed_runs_empty(&root)? { + return Ok(None); + } + let generations = root.open_directory_readonly(b"generations")?; + if len == 0 { + select_generation_name( + &generations, + root.identity(), + ¤t.0[..usize::from(current.1)], + active_source, + &mut name, + &mut len, + cancel, + )?; + } + if len == 0 || name[4..len] == current.0[..usize::from(current.1)] { + return Ok(None); + } + let member = storage + .join("generations") + .join(std::str::from_utf8(&name[..len]).map_err(|_| invalid())?); + let artifact = storage.join("artifacts"); + (storage, member, artifact) + } + LegacyNativeScope::DefaultDirect => { + // The configured Direct graph is current; this role only handles an + // old default pair after a genuine Managed administrative cohort. + if config.direct.is_some() { + return Ok(None); + } + let state_id = locks::path_identity(&target.state_root).map_err(|_| invalid())?; + if active_source.is_some_and(|s| s.root_identity() != state_id) { + return Ok(None); + } + let pair = super::inventory::default_direct(target); + if active_source.is_none() && (!pair.database.is_file() || !pair.manifest.is_file()) { + return Ok(None); + } + let conventional = target.state_root.join("artifacts"); + let keyed = super::layout::DirectLayout::new(&pair.database, &pair.manifest) + .artifact_root_path(); + let artifact = if cohort.is_some() && conventional.is_dir() { + conventional + } else { + keyed + }; + ( + target.state_root.clone(), + target.state_root.clone(), + artifact, + ) + } + }; + let mut scope = scope_box(target, binding, mode, &storage, &member, &artifact, memory)?; + let mut recovering = scope.refresh_publication_recovery(memory, None)?; + let mut version = if recovering { + None + } else { + source::observe_legacy_source_version(&mut scope, memory)? + }; + if mode == LegacyNativeScope::DefaultDirect { + if let Some(version) = version.or(active_source) { + if locks::path_identity(&artifact).ok() != Some(version.cache_identity()) { + let pair = super::inventory::default_direct(target); + let keyed = super::layout::DirectLayout::new(&pair.database, &pair.manifest) + .artifact_root_path(); + let conventional = target.state_root.join("artifacts"); + if let Some(actual) = [keyed, conventional] + .into_iter() + .find(|p| locks::path_identity(p).ok() == Some(version.cache_identity())) + { + artifact = actual; + scope = scope_box(target, binding, mode, &storage, &member, &artifact, memory)?; + recovering = scope.refresh_publication_recovery(memory, None)?; + } + } + } + } + + if mode == LegacyNativeScope::ManagedGeneration + && active_source.is_none() + && version.is_some_and(|v| v.phase() != LegacyPhase::Removed) + { + if let Some((actual, actual_len)) = source::observe_legacy_member_name(&mut scope, memory)? + { + if actual != name || usize::from(actual_len) != len { + name = actual; + len = usize::from(actual_len); + member = storage + .join("generations") + .join(std::str::from_utf8(&name[..len]).map_err(|_| invalid())?); + scope = scope_box(target, binding, mode, &storage, &member, &artifact, memory)?; + version = source::observe_legacy_source_version(&mut scope, memory)?; + } + } + } + if active_source.is_none() + && mode == LegacyNativeScope::ManagedGeneration + && version.is_some_and(|v| v.phase() == LegacyPhase::Removed) + { + version = None; + } + if mode == LegacyNativeScope::DefaultDirect + && version.is_none() + && !recovering + && (!scope.database_path().is_file() || !scope.manifest_path().is_file()) + { + return Ok(None); + } + if !recovering && active_source.is_some_and(|s| version.is_none_or(|v| !v.same_job_source(&s))) + { + return Ok(None); + } + let owner = version + .map(|v| source::reopen_legacy_source_owner(&mut scope, &v, memory)) + .transpose()?; + let automatic = + if mode == LegacyNativeScope::DefaultDirect && cohort.is_none() && owner.is_none() { + let pair = super::inventory::default_direct(target); + let layout = super::layout::DirectLayout::new(&pair.database, &pair.manifest); + if artifact != layout.artifact_root_path() { + return Ok(None); + } + let Some(cap) = + super::maintenance_ownership::prepare_automatic_direct(&layout, memory)? + else { + return Ok(None); + }; + Some(cap) + } else { + None + }; + + if owner.is_none() && !recovering { + if let Some(cap) = cohort.as_ref() { + scope.prepare_enrollment_in_cohort(cap, memory)?; + } else if automatic.is_some() { + scope.prepare_enrollment_automatic(memory)?; + } + } + boxed( + Job { + mode, + scope, + owner, + cohort, + automatic, + member, + storage, + member_name: name, + member_len: len as u8, + proof: None, + current: None, + }, + memory, + ) + .map(Some) +} +/// Preparation and all retained payload disposal run on the joined worker, +/// outside the execution permit. Each returned work owns at most one payload +/// validation, or the association job's actual native pair. +#[allow(clippy::too_many_arguments)] +#[inline(never)] +pub(crate) fn prepare_work( + target: &MaintenanceTarget, + binding: &PreparedTargetBinding, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + mode: LegacyNativeScope, + state: &mut LegacyNativePreparationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + if cancel() { + return Err(invalid()); + } + let origin = + super::maintenance_legacy_engine::staging_origin(target, domain, expected, memory)?; + let metadata = super::maintenance_legacy_engine::staging_metadata(target, &origin, memory)?; + let job = if let Some(job) = state.job.take() { + if job.mode != mode { + state.job = Some(job); + return Ok(None); + } + job + } else { + let cohort = if root_cohort_control_exists(domain)? { + prepare_root_cohort_boxed(target, binding, memory)? + } else { + None + }; + let Some(job) = select_job(target, binding, mode, &metadata, cohort, memory, cancel)? + else { + return Ok(None); + }; + job + }; + prepare_job( + target, domain, expected, state, job, metadata, memory, cancel, + ) +} +#[inline(never)] +// Keep prepared scope, metadata and immutable source proofs separate across the bounded off-permit stage. +#[allow(clippy::too_many_arguments)] +fn prepare_job( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut LegacyNativePreparationState, + mut job: MemoryBox, + metadata: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let recovering = { + let job = &mut *job; + job.scope + .refresh_publication_recovery(memory, job.owner.as_ref())? + }; + if recovering { + return work(job, Action::Recover, memory); + } + if job.owner.is_none() { + if job.mode == LegacyNativeScope::ManagedGeneration { + let root = RootedDirectory::open_readonly_root( + &job.storage, + locks::path_identity(&job.storage).map_err(|_| invalid())?, + )?; + let current = + super::maintenance_generation_gate::read_current(&root).map_err(|_| invalid())?; + if !managed_runs_empty(&root)? { + state.job = Some(job); + return Ok(None); + } + if job.member_name[4..usize::from(job.member_len)] + == current.0[..usize::from(current.1)] + { + return Err(invalid()); + } + job.current = Some(current.2); + } + job.scope.refresh_enrollment_preparation(memory)?; + return work(job, Action::Enroll, memory); + } + refresh_current(&mut job)?; + let version = job.owner.as_ref().unwrap().closed_version(); + if let Some(recovery) = admission::prepare_metadata_recovery(&metadata, memory)? { + if !metadata.payload().is_some_and(is_native_payload) { + state.job = Some(job); + return Ok(None); + } + return work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update: boxed(recovery, memory)?, + reset: None, + }, + memory, + )?), + memory, + ); + } + if metadata.payload().is_some_and(is_native_payload) { + return prepare_control(target, domain, job, metadata, memory, cancel); + } + if version.phase() == LegacyPhase::Removed && job.mode == LegacyNativeScope::DefaultDirect { + return work(job, Action::Complete, memory); + } + if version.phase() == LegacyPhase::Preparing { + drop(metadata); + return prepare_association_job(target, domain, expected, state, job, memory, cancel); + } + prepare_retiring_job(target, job, metadata, version, memory) +} + +#[allow(clippy::too_many_arguments)] +#[inline(never)] +fn prepare_association_job( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + state: &mut LegacyNativePreparationState, + mut job: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let version = job.owner.as_ref().ok_or_else(invalid)?.closed_version(); + if job.proof.is_some() { + prepare_transition(&mut job, LegacyPhase::Retiring, memory)?; + return work(job, Action::Retire, memory); + } + let progress = if state.association.needs_source_handles() { + let root = RootedDirectory::open_readonly_root( + job.scope.artifact_root(), + version.cache_identity(), + )?; + stream::prepare_association_source( + target, + domain, + expected, + File::open(job.scope.database_path())?, + File::open(job.scope.manifest_path())?, + root, + job.scope.artifact_root(), + &target.repository_root, + job.owner.as_ref(), + None, + &mut state.association, + memory, + cancel, + )? + } else { + stream::prepare_parked_association_source( + target, + domain, + expected, + job.scope.artifact_root(), + &target.repository_root, + job.owner.as_ref(), + None, + &mut state.association, + memory, + cancel, + )? + }; + match progress { + AssociationSourceProgress::Pending => { + state.job = Some(job); + Ok(None) + } + AssociationSourceProgress::Step(step) => work(job, Action::Association(step), memory), + AssociationSourceProgress::Complete { + association, + database, + manifest, + } => { + job.proof = Some((association, database, manifest)); + prepare_transition(&mut job, LegacyPhase::Retiring, memory)?; + work(job, Action::Retire, memory) + } + } +} + +#[inline(never)] +fn prepare_retiring_job( + target: &MaintenanceTarget, + job: MemoryBox, + metadata: MemoryBox, + version: LegacyClosedVersion, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let header = metadata + .payload() + .and_then(|p| LegacyStreamHeader::decode(p).ok()); + let header = header.ok_or_else(invalid)?; + let original = if header.done() || header.pending() { + header.before_native_cohort()? + } else { + let replay = LegacyStreamReplay::new( + &metadata, + &header.source_version().ok_or_else(invalid)?, + header.context_digest(), + memory, + )?; + let cp = replay.checkpoint()?; + if cp.as_slice().len() != 3968 || &cp.as_slice()[..8] != b"CGLGNCP1" { + return Err(invalid()); + } + let original = LegacyStreamHeader::decode(&cp.as_slice()[8..2056])?; + validate_native_payload(cp.as_slice(), &job, version, &original)?; + return install_control(job, metadata, original, header.chain_digest(), memory); + }; + if !version.same_job_source(&original.source_version().ok_or_else(invalid)?) { + return Err(invalid()); + } + let _replay = LegacyStreamReplay::from_header(&metadata, original, memory)?; + let payload = native_payload(&job, version, original, memory)?; + let frame = LegacyStreamFrame::new( + LegacyFrameKind::NativeCohort, + original.context_digest(), + original.sequence(), + original.chain_digest(), + payload.as_slice(), + memory, + )?; + let action = if header.pending() { + LegacyStreamAction::FinishAppend(frame) + } else { + LegacyStreamAction::BeginAppend(frame) + }; + let step = + stream::prepare_legacy_stream_step(target, &metadata.origin_scope(), action, memory)?; + work(job, Action::Custody(step), memory) +} + +#[inline(never)] +fn native_payload( + job: &Job, + version: LegacyClosedVersion, + original: LegacyStreamHeader, + memory: &CleanupMemory, +) -> Result { + let mut payload = memory + .reserve(3968) + .map_err(FixedControlError::Resource)? + .bytes(3968) + .map_err(FixedControlError::Resource)?; + let b = payload.as_mut_slice(); + b.fill(0); + b[..8].copy_from_slice(b"CGLGNCP1"); + original.encode((&mut b[8..2056]).try_into().unwrap()); + version.encode_checkpoint((&mut b[2056..2568]).try_into().unwrap()); + b[2568] = job.mode as u8; + b[2569] = job.member_len; + b[2576..2704].copy_from_slice(&job.member_name); + let digest = Sha256::digest(&b[..3936]); + b[3936..].copy_from_slice(&digest); + Ok(payload) +} +fn validate_native_payload( + b: &[u8], + job: &Job, + version: LegacyClosedVersion, + original: &LegacyStreamHeader, +) -> Result<(), FixedControlError> { + if b.len() != 3968 + || &b[..8] != b"CGLGNCP1" + || b[2568] != job.mode as u8 + || b[2569] != job.member_len + || b[2576..2704] != job.member_name + || Sha256::digest(&b[..3936])[..] != b[3936..] + || !version.same_job_source(&LegacyClosedVersion::decode_checkpoint( + b[2056..2568].try_into().unwrap(), + )?) + || !version.same_job_source(&original.source_version().ok_or_else(invalid)?) + { + return Err(invalid()); + } + Ok(()) +} +fn control_update( + metadata: &PreparedPrivateMetadata, + control: &Control, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + boxed( + admission::prepare_metadata_control( + &metadata.origin_scope(), + metadata.committed().ok_or_else(invalid)?, + &control.encode(), + memory, + )?, + memory, + ) +} +fn completed_cursor( + job: &Job, + original: &LegacyStreamHeader, + installation: [u8; 32], +) -> Result<[u8; stream::CURSOR_BYTES], FixedControlError> { + let mut cursor = super::maintenance::MaintenanceCursor::decode(original.cursor(), installation) + .map_err(|_| invalid())?; + let mut address = job.member_name; + let category = if job.mode == LegacyNativeScope::ManagedGeneration { + super::maintenance::WorkCategory::LegacyNative + } else { + let name = job + .scope + .database_path() + .file_name() + .ok_or_else(invalid)? + .as_encoded_bytes(); + if name.len() > 128 { + return Err(invalid()); + } + address.fill(0); + address[..name.len()].copy_from_slice(name); + super::maintenance::WorkCategory::DefaultDirect + }; + cursor.inspected(category, super::maintenance::CursorAddress(address)); + Ok(cursor.encode()) +} +fn install_control( + job: MemoryBox, + metadata: MemoryBox, + original: LegacyStreamHeader, + frame: [u8; 32], + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + let control = Control { + mode: job.mode, + stage: INSPECT, + cache_started: false, + index: 0, + source: job.owner.as_ref().unwrap().closed_version(), + member: job.member_name, + member_len: job.member_len, + offset: original.committed_end(), + frame, + key: [0; 32], + stamp: None, + digest: [0; 32], + parent: None, + directory: None, + cursor: completed_cursor(&job, &original, metadata.origin_scope().incarnation)?, + }; + let update = control_update(&metadata, &control, memory)?; + work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: None, + }, + memory, + )?), + memory, + ) +} +/// The only accepted tail is the exact single committed NativeCohort frame. +/// Extra bytes are never truncated, including on apparently completed jobs. +#[inline(never)] +fn read_custody( + metadata: &PreparedPrivateMetadata, + control: &Control, + job: &Job, + memory: &CleanupMemory, +) -> Result { + if metadata.stream_len() != control.offset.checked_add(4096).ok_or_else(invalid)? { + return Err(invalid()); + } + let mut bytes = memory + .reserve(4096) + .map_err(FixedControlError::Resource)? + .bytes(4096) + .map_err(FixedControlError::Resource)?; + metadata.read_stream_exact(control.offset, bytes.as_mut_slice())?; + let b = bytes.as_slice(); + if &b[..8] != b"CGLGFR01" + || b[8] != 7 + || b[9..16].iter().any(|x| *x != 0) + || u64::from_le_bytes(b[56..64].try_into().unwrap()) != 3968 + || Sha256::digest(&b[..4064])[..] != b[4064..] + || b[4064..] != control.frame + { + return Err(invalid()); + } + let original = LegacyStreamHeader::decode(&b[104..2152])?; + validate_native_payload( + &b[96..4064], + job, + job.owner.as_ref().unwrap().closed_version(), + &original, + )?; + if original.committed_end() != control.offset + || b[16..48] != original.context_digest() + || b[64..96] != original.chain_digest() + || u64::from_le_bytes(b[48..56].try_into().unwrap()) != original.sequence() + { + return Err(invalid()); + } + LegacyStreamReplay::from_header(metadata, original, memory)?; + Ok(original) +} +fn leaf_name(job: &Job, index: u16) -> Result, FixedControlError> { + let index = usize::from(index); + if index >= NAMES.len() { + return Err(invalid()); + } + if job.mode == LegacyNativeScope::ManagedGeneration || index == 1 || index >= 12 { + return Ok(NAMES[index].to_vec()); + } + let base = job + .scope + .database_path() + .file_name() + .ok_or_else(invalid)? + .as_encoded_bytes(); + let mut name = base.to_vec(); + if index != 0 { + name.extend_from_slice(&NAMES[index][b"graph.ldb".len()..]); + } + Ok(name) +} +fn optional_file(root: &RootedDirectory, name: &[u8]) -> Result, FixedControlError> { + match root.open_readonly_file(name) { + Ok(file) => Ok(Some(file)), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(e) => Err(e.into()), + } +} +fn managed_runs_empty(root: &RootedDirectory) -> Result { + let runs = match root.open_directory_readonly(b"runs") { + Ok(runs) => runs, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false), + Err(error) => return Err(error.into()), + }; + let mut empty = true; + let mut scratch = [0; 4096]; + match runs.visit_entries(&mut scratch, &mut |_| { + empty = false; + Err(std::io::ErrorKind::WouldBlock.into()) + }) { + Ok(()) => Ok(empty), + Err(error) if !empty && error.kind() == std::io::ErrorKind::WouldBlock => Ok(false), + Err(error) => Err(error.into()), + } +} +fn refresh_current(job: &mut Job) -> Result<(), FixedControlError> { + if job.mode == LegacyNativeScope::DefaultDirect { + return Ok(()); + } + let root = RootedDirectory::open_readonly_root( + &job.storage, + job.owner.as_ref().unwrap().closed_version().root_identity(), + )?; + let current = super::maintenance_generation_gate::read_current(&root).map_err(|_| invalid())?; + if job.member_name[4..usize::from(job.member_len)] == current.0[..usize::from(current.1)] { + return Err(invalid()); + } + job.current = Some(current.2); + Ok(()) +} + +fn lease( + member: &RootedDirectory, + mode: LegacyNativeScope, +) -> Result, FixedControlError> { + if mode == LegacyNativeScope::DefaultDirect { + return Ok(None); + } + FixedLockedFile::try_lock(member.open_existing(b"lease.lock")?, LockMode::Exclusive)? + .map(Some) + .ok_or(FixedControlError::Invalid(FixedControlProblem::Busy)) +} +#[inline(never)] +fn prepare_control( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + mut job: MemoryBox, + metadata: MemoryBox, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + let mut control = Control::decode(metadata.payload().ok_or_else(invalid)?)?; + let version = job.owner.as_ref().unwrap().closed_version(); + if control.mode != job.mode + || !version.same_job_source(&control.source) + || control.member != job.member_name + || control.member_len != job.member_len + { + return Err(invalid()); + } + let already_reset = control.stage == RESET + && version.phase() == LegacyPhase::Removed + && metadata.stream_len() == 0; + let original = if already_reset { + None + } else { + Some(read_custody(&metadata, &control, &job, memory)?) + }; + if let Some(original) = original.as_ref() { + if completed_cursor(&job, original, metadata.origin_scope().incarnation)? != control.cursor + { + return Err(invalid()); + } + } + super::maintenance::MaintenanceCursor::decode( + &control.cursor, + metadata.origin_scope().incarnation, + ) + .map_err(|_| invalid())?; + refresh_current(&mut job)?; + if control.stage == RESET { + if version.phase() != LegacyPhase::Removed { + return Err(invalid()); + } + let cursor = &control.cursor; + let update = boxed( + admission::prepare_metadata_control( + &metadata.origin_scope(), + metadata.committed().ok_or_else(invalid)?, + cursor, + memory, + )?, + memory, + )?; + return work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: Some(if already_reset { + 0 + } else { + control.offset + 4096 + }), + }, + memory, + )?), + memory, + ); + } + if version.phase() == LegacyPhase::Removed { + control.stage = RESET; + let update = control_update(&metadata, &control, memory)?; + return work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: None, + }, + memory, + )?), + memory, + ); + } + if version.phase() != LegacyPhase::Retiring { + return Err(invalid()); + } + if control.stage == FINISH { + prepare_transition(&mut job, LegacyPhase::Removed, memory)?; + return work(job, Action::Finish, memory); + } + if control.stage == ROOT { + return prepare_root(job, metadata, control, memory); + } + if control.index == CACHE { + return prepare_cache( + target, + domain, + job, + metadata, + control, + original.ok_or_else(invalid)?, + memory, + cancel, + ); + } + if usize::from(control.index) >= NAMES.len() { + control.stage = if job.mode == LegacyNativeScope::ManagedGeneration { + ROOT + } else { + INSPECT + }; + if job.mode == LegacyNativeScope::DefaultDirect { + control.index = CACHE; + control.key = [0; 32]; + control.cache_started = false; + } + let update = control_update(&metadata, &control, memory)?; + return work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: None, + }, + memory, + )?), + memory, + ); + } + if job.mode == LegacyNativeScope::DefaultDirect && matches!(control.index, 2 | 3 | 12 | 13) { + control.index += 1; + let update = control_update(&metadata, &control, memory)?; + return work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: None, + }, + memory, + )?), + memory, + ); + } + let member = RootedDirectory::open_readonly_root(&job.member, version.member_identity())?; + let name = leaf_name(&job, control.index)?; + let mut file = optional_file(&member, &name)?; + if control.stage == INSPECT { + if let Some(file) = file.as_mut() { + let stamp = FixedFileStamp::capture(file, FixedControlKind::File)?; + recognize_leaf(&job, control.index, file, stamp, memory)?; + control.digest = hash(file, stamp, memory, cancel)?; + control.stamp = Some(stamp); + control.stage = UNLINK; + } else { + control.index += 1; + } + let update = control_update(&metadata, &control, memory)?; + return work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: None, + }, + memory, + )?), + memory, + ); + } + if let Some(file) = file.as_mut() { + let stamp = control.stamp.ok_or_else(invalid)?; + stamp.recheck(file)?; + if hash(file, stamp, memory, cancel)? != control.digest { + return Err(invalid()); + } + } + let mut next = Control::decode(&control.encode())?; + next.stage = INSPECT; + next.index += 1; + next.stamp = None; + next.digest = [0; 32]; + let update = control_update(&metadata, &next, memory)?; + let invalidation = if job.mode == LegacyNativeScope::ManagedGeneration { + Some(boxed( + admission::prepare_scoped_invalidation(domain, memory)?, + memory, + )?) + } else { + None + }; + work( + job, + Action::Leaf(boxed( + LeafAction { + metadata, + update, + control, + name, + file, + invalidation, + }, + memory, + )?), + memory, + ) +} +#[inline(never)] +fn hash( + file: &mut File, + stamp: FixedFileStamp, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result<[u8; 32], FixedControlError> { + let mut bytes = memory + .reserve(4096) + .map_err(FixedControlError::Resource)? + .bytes(4096) + .map_err(FixedControlError::Resource)?; + file.seek(SeekFrom::Start(0))?; + let mut digest = Sha256::new(); + let mut remaining = stamp.len(); + while remaining != 0 { + if cancel() { + return Err(invalid()); + } + let size = remaining.min(4096) as usize; + file.read_exact(&mut bytes.as_mut_slice()[..size])?; + digest.update(&bytes.as_slice()[..size]); + remaining -= size as u64; + } + stamp.recheck(file)?; + Ok(digest.finalize().into()) +} +/// Native DB and manifest membership comes from the full opaque association. +/// Ancillary files additionally require the actual producer's bounded format. +#[inline(never)] +fn recognize_leaf( + job: &Job, + index: u16, + file: &mut File, + stamp: FixedFileStamp, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + match index { + 0 => { + if Some(stamp) + != job + .owner + .as_ref() + .unwrap() + .closed_version() + .database_stamp() + { + return Err(invalid()); + } + } + 1 => { + if Some(stamp) + != job + .owner + .as_ref() + .unwrap() + .closed_version() + .manifest_stamp() + { + return Err(invalid()); + } + } + 2 | 6 | 12 => { + if stamp.len() > 16384 { + return Err(invalid()); + } + let mut bytes = memory + .reserve(stamp.len() as usize + 32768) + .map_err(FixedControlError::Resource)? + .bytes(stamp.len() as usize) + .map_err(FixedControlError::Resource)?; + file.seek(SeekFrom::Start(0))?; + file.read_exact(bytes.as_mut_slice())?; + if index == 2 { + let meta: super::managed::GenerationMetadata = + serde_json::from_slice(bytes.as_slice()).map_err(|_| invalid())?; + if !matches!(meta.schema_version, 1 | 2) + || meta.generation_id.as_bytes() + != &job.member_name[4..usize::from(job.member_len)] + { + return Err(invalid()); + } + } + if index == 6 { + #[derive(serde::Deserialize)] + struct Meta { + backend: String, + format_version: u64, + } + let meta: Meta = serde_json::from_slice(bytes.as_slice()).map_err(|_| invalid())?; + if meta.backend != "disk_bm25_v1" || meta.format_version != 1 { + return Err(invalid()); + } + } + if index == 12 { + let _: super::managed::GenerationRetirement = + serde_json::from_slice(bytes.as_slice()).map_err(|_| invalid())?; + } + } + 3 => { + let mut bytes = [0; 64]; + if stamp.len() == 0 || stamp.len() > 64 { + return Err(invalid()); + } + file.seek(SeekFrom::Start(0))?; + file.read_exact(&mut bytes[..stamp.len() as usize])?; + let b = &bytes[..stamp.len() as usize]; + if b != b"prepared\n" + && (!b.ends_with(b"\n") || !b[..b.len() - 1].iter().all(u8::is_ascii_digit)) + { + return Err(invalid()); + } + } + // A non-empty WAL/tmp needs native recovery and is intentionally retained. + 4 | 5 => { + if stamp.len() != 0 { + return Err(invalid()); + } + } + 7..=11 => recognize_search(index, file, stamp, memory)?, + 13 => { + let directory = RootedDirectory::open_readonly_root( + &job.member, + job.owner + .as_ref() + .unwrap() + .closed_version() + .member_identity(), + )?; + let (_, _, actual) = super::maintenance_generation_gate::read_legacy_final( + &directory, + job.owner.as_ref().unwrap().closed_version().root_identity(), + ) + .map_err(|_| invalid())?; + if actual != stamp { + return Err(invalid()); + } + } + _ => return Err(invalid()), + } + stamp.recheck(file)?; + Ok(()) +} +#[inline(never)] +fn recognize_search( + index: u16, + file: &mut File, + stamp: FixedFileStamp, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + let magic = match index { + 7 => b"CBGLX001", + 8 => b"CBGPS001", + 9 => b"CBGDL001", + 10 => b"CBGDC001", + 11 => b"CBGDO001", + _ => return Err(invalid()), + }; + file.seek(SeekFrom::Start(0))?; + let mut actual = [0; 8]; + file.read_exact(&mut actual)?; + if &actual != magic { + return Err(invalid()); + } + let mut remaining = stamp.len().checked_sub(8).ok_or_else(invalid)?; + if index == 8 || index == 9 { + if remaining % 12 != 0 { + return Err(invalid()); + } + let mut record = [0; 12]; + while remaining != 0 { + file.read_exact(&mut record)?; + remaining -= 12; + if index == 8 { + if u32::from_le_bytes(record[8..12].try_into().unwrap()) == 0 { + return Err(invalid()); + } + } else if record[8] > 1 || record[9..12].iter().any(|v| *v != 0) { + return Err(invalid()); + } + } + return Ok(()); + } + if index == 11 { + if remaining % 8 != 0 { + return Err(invalid()); + } + let mut prior = None; + let mut record = [0; 8]; + while remaining != 0 { + file.read_exact(&mut record)?; + remaining -= 8; + let offset = u64::from_le_bytes(record); + if prior.is_none() && offset != 8 || prior.is_some_and(|old| offset <= old) { + return Err(invalid()); + } + prior = Some(offset); + } + return Ok(()); + } + let mut previous = memory + .reserve(65536) + .map_err(FixedControlError::Resource)? + .bytes(65536) + .map_err(FixedControlError::Resource)?; + let mut previous_len = 0usize; + let mut next_postings = 8u64; + let mut scratch = memory + .reserve(65536) + .map_err(FixedControlError::Resource)? + .bytes(65536) + .map_err(FixedControlError::Resource)?; + while remaining != 0 { + if remaining < 4 { + return Err(invalid()); + } + let mut size = [0; 4]; + file.read_exact(&mut size)?; + remaining -= 4; + let len = u32::from_le_bytes(size) as usize; + if len == 0 || len > 65536 || len as u64 > remaining { + return Err(invalid()); + } + file.read_exact(&mut scratch.as_mut_slice()[..len])?; + remaining -= len as u64; + if index == 7 { + std::str::from_utf8(&scratch.as_slice()[..len]).map_err(|_| invalid())?; + if previous_len != 0 && previous.as_slice()[..previous_len] >= scratch.as_slice()[..len] + { + return Err(invalid()); + } + previous.as_mut_slice()[..len].copy_from_slice(&scratch.as_slice()[..len]); + previous_len = len; + if remaining < 12 { + return Err(invalid()); + } + let mut tail = [0; 12]; + file.read_exact(&mut tail)?; + remaining -= 12; + let offset = u64::from_le_bytes(tail[..8].try_into().unwrap()); + let frequency = u32::from_le_bytes(tail[8..].try_into().unwrap()); + if offset != next_postings || frequency == 0 { + return Err(invalid()); + } + next_postings = next_postings + .checked_add(u64::from(frequency) * 12) + .ok_or_else(invalid)?; + } else { + #[derive(serde::Deserialize)] + struct Document { + id: String, + node_type: String, + index_order: u64, + layer: String, + } + let _floor = memory + .reserve(131072) + .map_err(FixedControlError::Resource)?; + let doc: Document = + serde_json::from_slice(&scratch.as_slice()[..len]).map_err(|_| invalid())?; + let _ = (&doc.id, &doc.node_type, doc.index_order); + if !matches!(doc.layer.as_str(), "semantic" | "syntax") { + return Err(invalid()); + } + } + } + Ok(()) +} +fn key_name(key: [u8; 32]) -> [u8; 64] { + let mut out = [0; 64]; + for (i, b) in key.iter().enumerate() { + out[i * 2] = b"0123456789abcdef"[(b >> 4) as usize]; + out[i * 2 + 1] = b"0123456789abcdef"[(b & 15) as usize]; + } + out +} +fn cache_directory( + job: &Job, + control: &Control, +) -> Result<(RootedDirectory, RootedDirectory), FixedControlError> { + let root = RootedDirectory::open_readonly_root( + job.scope.artifact_root(), + job.owner + .as_ref() + .unwrap() + .closed_version() + .cache_identity(), + )?; + let name = key_name(control.key); + let shard = root.open_directory_readonly(&name[..2])?; + let directory = shard.open_directory_readonly(&name)?; + if control.parent.is_some_and(|id| id != shard.identity()) + || control + .directory + .is_some_and(|id| id != directory.identity()) + { + return Err(invalid()); + } + Ok((shard, directory)) +} +#[inline(never)] +// Keep prepared scope, metadata and immutable source proofs separate across the bounded off-permit stage. +#[allow(clippy::too_many_arguments)] +fn prepare_cache( + _target: &MaintenanceTarget, + domain: &ReferenceDomain, + job: MemoryBox, + metadata: MemoryBox, + mut control: Control, + original: LegacyStreamHeader, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result>, FixedControlError> { + if job.mode != LegacyNativeScope::DefaultDirect { + return Err(invalid()); + } + // A shared modern cache needs the modern reference retirement protocol. + // This role never converts old source membership into current reachability. + if locks::path_identity(domain.artifact_root()).map_err(|_| invalid())? + == job + .owner + .as_ref() + .unwrap() + .closed_version() + .cache_identity() + { + control.stage = FINISH; + let update = control_update(&metadata, &control, memory)?; + return work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: None, + }, + memory, + )?), + memory, + ); + } + if control.stage == UNLINK { + let file = match cache_directory(&job, &control) { + Ok((_, directory)) => optional_file(&directory, b"partition.json")?, + Err(FixedControlError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e), + }; + let mut file = file; + if let Some(f) = file.as_mut() { + let stamp = control.stamp.ok_or_else(invalid)?; + stamp.recheck(f)?; + if hash(f, stamp, memory, cancel)? != control.digest { + return Err(invalid()); + } + } + let mut next = Control::decode(&control.encode())?; + next.stage = INSPECT; + next.stamp = None; + next.parent = None; + next.directory = None; + let update = control_update(&metadata, &next, memory)?; + return work( + job, + Action::Leaf(boxed( + LeafAction { + metadata, + update, + control, + name: b"partition.json".to_vec(), + file, + invalidation: None, + }, + memory, + )?), + memory, + ); + } + let replay = LegacyStreamReplay::from_header(&metadata, original, memory)?; + let mut selected: Option = None; + replay + .replay(memory, &mut |cert| { + if cert.loader_usable() + && (!control.cache_started || cert.key() > control.key) + && selected.is_none_or(|old| cert.key() < old.key()) + { + selected = Some(cert) + } + Ok(()) + }) + .map_err(|_| invalid())?; + let Some(cert) = selected else { + control.stage = FINISH; + let update = control_update(&metadata, &control, memory)?; + return work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: None, + }, + memory, + )?), + memory, + ); + }; + control.key = cert.key(); + control.cache_started = true; + let actual = match cache_directory(&job, &control) { + Ok(pair) => Some(pair), + Err(FixedControlError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => None, + Err(e) => return Err(e), + }; + if let Some((shard, directory)) = actual { + if let Some(mut file) = optional_file(&directory, b"partition.json")? { + cert.stamp().recheck(&file)?; + if hash(&mut file, cert.stamp(), memory, cancel)? != cert.payload_digest() { + return Err(invalid()); + } + control.stamp = Some(cert.stamp()); + control.digest = cert.payload_digest(); + control.parent = Some(shard.identity()); + control.directory = Some(directory.identity()); + control.stage = UNLINK; + } + } + let update = control_update(&metadata, &control, memory)?; + work( + job, + Action::Metadata(boxed( + MetadataAction { + metadata, + update, + reset: None, + }, + memory, + )?), + memory, + ) +} +#[inline(never)] +fn prepare_root( + mut job: MemoryBox, + metadata: MemoryBox, + mut control: Control, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + if job.mode != LegacyNativeScope::ManagedGeneration { + return Err(invalid()); + } + let directory = + match RootedDirectory::open_readonly_root(&job.member, control.source.member_identity()) { + Ok(d) => d, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + prepare_transition(&mut job, LegacyPhase::Removed, memory)?; + return work(job, Action::Finish, memory); + } + Err(e) => return Err(e.into()), + }; + let resource = super::maintenance_generation_gate::resource_name_for(b"graph.ldb"); + let manifest_resource = super::maintenance_generation_gate::resource_name_for(b"manifest.json"); + let names: [&[u8]; 5] = [ + b"lease.lock", + b".database-use-family.lock", + &resource, + &manifest_resource, + b"graph.ldb.lock", + ]; + let mut controls: [Option; 5] = std::array::from_fn(|_| None); + for (index, name) in names.iter().enumerate() { + if let Some(file) = optional_file(&directory, name)? { + controls[index] = Some(ControlLeaf { + name: name.to_vec(), + stamp: FixedFileStamp::capture(&file, FixedControlKind::File)?, + file: Some(file), + }); + } + } + let mut scratch = [0; 4096]; + let mut count = 0; + directory.visit_entries(&mut scratch, &mut |entry| { + count += 1; + if count > 100 || !names.contains(&entry) { + return Err(std::io::ErrorKind::InvalidData.into()); + } + Ok(()) + })?; + control.stage = FINISH; + let update = control_update(&metadata, &control, memory)?; + work( + job, + Action::Root(boxed( + RootAction { + metadata, + update, + controls, + held: std::array::from_fn(|_| None), + }, + memory, + )?), + memory, + ) +} +struct ControlLeaf { + name: Vec, + stamp: FixedFileStamp, + file: Option, +} +fn check_current_facts( + mode: LegacyNativeScope, + current: Option, + root: &RootedDirectory, +) -> Result<(), FixedControlError> { + if mode == LegacyNativeScope::ManagedGeneration { + current + .ok_or_else(invalid)? + .recheck(&root.open_readonly_file(b"active.json")?)?; + } + Ok(()) +} +fn publish_owned( + action: &mut MetadataAction, + root_use: &FixedLockedFile, + grant: &mut MutationGrant, +) -> Result<(), FixedControlError> { + admission::with_reserved_private_metadata_owned_root( + &mut action.metadata, + root_use, + grant, + action.update.effects(), + &mut |mutation, grant| { + if let Some(len) = action.reset { + mutation.reset_stream(len, grant)?; + } + mutation.publish_control(&action.update, grant) + }, + )?; + Ok(()) +} +impl LegacyNativeWork { + #[inline(never)] + pub(crate) fn advance( + &mut self, + grant: &mut MutationGrant, + _confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + let job: &mut Job = self.job.as_mut().ok_or_else(invalid)?; + let memory = &self.memory; + let mode = job.mode; + let current = job.current; + match &mut self.action { + Action::Recover => { + if !job + .scope + .recover_publication(job.owner.as_mut(), grant, memory)? + { + return Err(invalid()); + } + self.report.mutated_entries = 1; + } + Action::Complete => { + grant.check()?; + if !job.owner.as_ref().unwrap().is_current(memory)? { + return Err(invalid()); + } + self.done = true; + self.report.pending = false; + } + Action::Enroll => { + let mut apply = |authority: &source::LegacyEnrollmentAuthority<'_>, + _grant: &mut MutationGrant| { + check_current_facts(mode, current, authority.directory())?; + let _lease = lease(authority.member_directory(), mode)?; + Ok(()) + }; + let outcome = if let Some(cap) = job.cohort.as_ref() { + source::with_legacy_enrollment_in_cohort( + &mut job.scope, + cap, + grant, + memory, + &mut apply, + ) + } else { + source::with_legacy_enrollment_automatic( + &mut job.scope, + job.automatic.as_mut().ok_or_else(invalid)?, + grant, + memory, + &mut apply, + ) + }; + match outcome { + Ok((_, owner)) => job.owner = Some(owner), + Err(FixedControlError::Invalid(FixedControlProblem::Busy)) + if job.scope.enrollment_pending() => {} + Err(error) => return Err(error), + } + self.report.mutated_entries = 1; + } + Action::Association(step) => { + self.committed = Some(step.advance(grant)?); + self.report.mutated_entries = 1; + } + Action::Retire => { + let (association, _, _) = job.proof.as_ref().ok_or_else(invalid)?; + // The owner and full association pin the actual pair. Transition + // is durable before any native-cohort intent can be selected. + source::begin_legacy_retirement( + &mut job.scope, + job.owner.as_mut().ok_or_else(invalid)?, + association, + grant, + memory, + )?; + self.report.mutated_entries = 1; + } + Action::Custody(step) => { + let owned = mode == LegacyNativeScope::ManagedGeneration; + if owned { + grant.take_entries(step.effects())?; + } + self.committed = Some(source::with_closed_source( + &mut job.scope, + job.owner.as_ref().ok_or_else(invalid)?, + grant, + memory, + &mut |authority, grant| { + check_current_facts(mode, current, authority.directory())?; + let _lease = lease(authority.member_directory(), mode)?; + if owned { + step.advance_with_owned_root(authority.artifact_use_guard(), grant) + } else { + step.advance(grant) + } + }, + )?); + self.report.mutated_entries = 1; + } + Action::Metadata(action) => { + let action: &mut MetadataAction = action; + if job.owner.as_ref().unwrap().closed_version().phase() == LegacyPhase::Removed { + if !job.owner.as_ref().unwrap().is_current(memory)? { + return Err(invalid()); + } + let effects = action.update.effects() + usize::from(action.reset.is_some()) * 2; + admission::with_private_metadata( + &mut action.metadata, + grant, + effects, + &mut |mutation, grant| { + if let Some(len) = action.reset { + mutation.reset_stream(len, grant)?; + } + mutation.publish_control(&action.update, grant) + }, + )?; + } else { + let same_root = action.metadata.origin_scope().root + == job + .owner + .as_ref() + .unwrap() + .closed_version() + .cache_identity(); + if same_root { + grant.take_entries(action.update.effects())?; + } + source::with_closed_source( + &mut job.scope, + job.owner.as_ref().unwrap(), + grant, + memory, + &mut |authority, grant| { + check_current_facts(mode, current, authority.directory())?; + let _lease = lease(authority.member_directory(), mode)?; + if same_root { + publish_owned(action, authority.artifact_use_guard(), grant) + } else { + admission::with_private_metadata( + &mut action.metadata, + grant, + action.update.effects(), + &mut |mutation, grant| { + mutation.publish_control(&action.update, grant) + }, + )?; + Ok(()) + } + }, + )?; + } + self.report.mutated_entries = 1; + if action.reset.is_some() { + self.done = true; + self.report.pending = false; + } + } + Action::Leaf(action) => { + advance_leaf(job, action, grant, memory, &mut self.report)?; + } + Action::Root(action) => { + advance_root(job, action, grant, memory, &mut self.report)?; + } + Action::Finish => { + source::finish_legacy_retirement( + &mut job.scope, + job.owner.as_mut().unwrap(), + grant, + memory, + )?; + self.report.mutated_entries = 1; + } + } + Ok(self.report) + } +} +fn open_cache_under_guard( + root: &RootedDirectory, + key: &[u8; 64], + control: &Control, +) -> Result, FixedControlError> { + let shard = match root.open_directory(&key[..2]) { + Ok(d) => d, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + if Some(shard.identity()) != control.parent { + return Err(invalid()); + } + let directory = match shard.open_directory(key) { + Ok(d) => d, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + if Some(directory.identity()) != control.directory { + return Err(invalid()); + } + Ok(Some((shard, directory))) +} +#[inline(never)] +fn advance_leaf( + job: &mut Job, + action: &mut LeafAction, + grant: &mut MutationGrant, + memory: &CleanupMemory, + report: &mut CandidateReport, +) -> Result<(), FixedControlError> { + let mode = job.mode; + let current = job.current; + let control = &action.control; + let name = &action.name; + let key = key_name(control.key); + let same_root = action.metadata.origin_scope().root + == job + .owner + .as_ref() + .unwrap() + .closed_version() + .cache_identity(); + // Reserve the atomic effect unit before admitting the actual source kernel. + let effects = action.update.effects() + 5; + if same_root { + grant.take_entries(effects)?; + } + source::with_closed_source( + &mut job.scope, + job.owner.as_ref().unwrap(), + grant, + memory, + &mut |authority, grant| { + check_current_facts(mode, current, authority.directory())?; + let _lease = lease(authority.member_directory(), mode)?; + let cache = if control.index == CACHE { + open_cache_under_guard(authority.artifact_directory(), &key, control)? + } else { + None + }; + let parent = cache + .as_ref() + .map(|(_, d)| d) + .unwrap_or(authority.member_directory()); + let mutate = |grant: &mut MutationGrant| -> Result<(), FixedControlError> { + if let Some(file) = action.file.as_ref() { + let stamp = control.stamp.ok_or_else(invalid)?; + stamp.recheck(file)?; + stamp.recheck(&parent.open_readonly_file(name)?)?; + grant.check_time()?; + parent.unlink_file(name, stamp.identity())?; + parent.sync_namespace()?; + } else if (control.index != CACHE || cache.is_some()) + && optional_file(parent, name)?.is_some() + { + return Err(invalid()); + } + if let Some((shard, directory)) = &cache { + // Empty-only disposal preserves every unrecognized neighbor. + grant.check_time()?; + match shard.remove_empty_directory(&key, directory.identity()) { + Ok(()) => shard.sync_namespace()?, + Err(e) if locks::is_directory_not_empty(&e) => {} + Err(e) => return Err(e.into()), + } + } + Ok(()) + }; + if let Some(invalidation) = action.invalidation.as_mut() { + admission::with_scoped_reference_invalidation_owned_root( + invalidation, + authority.artifact_use_guard(), + grant, + 0, + &mut |_, _| Ok(()), + &mut |_, grant| { + mutate(grant)?; + admission::with_reserved_private_metadata_owned_root( + &mut action.metadata, + authority.artifact_use_guard(), + grant, + action.update.effects(), + &mut |metadata, grant| metadata.publish_control(&action.update, grant), + )?; + Ok(()) + }, + )?; + } else if same_root { + mutate(grant)?; + admission::with_reserved_private_metadata_owned_root( + &mut action.metadata, + authority.artifact_use_guard(), + grant, + action.update.effects(), + &mut |metadata, grant| metadata.publish_control(&action.update, grant), + )?; + } else { + grant.take_entries(5)?; + mutate(grant)?; + admission::with_private_metadata( + &mut action.metadata, + grant, + action.update.effects(), + &mut |metadata, grant| metadata.publish_control(&action.update, grant), + )?; + } + Ok(()) + }, + )?; + report.mutated_entries = 1; + if action.file.is_some() { + report.reclaimed_entries = 1; + report.reclaimed_bytes = control.stamp.ok_or_else(invalid)?.len(); + } + Ok(()) +} +#[inline(never)] +fn advance_root( + job: &mut Job, + action: &mut RootAction, + grant: &mut MutationGrant, + memory: &CleanupMemory, + report: &mut CandidateReport, +) -> Result<(), FixedControlError> { + let mode = job.mode; + let current = job.current; + let member = job + .owner + .as_ref() + .unwrap() + .closed_version() + .member_identity(); + grant.take_entries(action.update.effects() + 12)?; + source::with_legacy_root_completion( + &mut job.scope, + job.owner.as_ref().unwrap(), + grant, + memory, + &mut |authority, grant| { + check_current_facts(mode, current, authority.directory())?; + let directory = authority + .member_parent() + .open_directory(authority.member_name())?; + if directory.identity() != member { + return Err(invalid()); + } + for (index, leaf) in action.controls.iter_mut().enumerate() { + if let Some(leaf) = leaf { + leaf.stamp + .recheck(&directory.open_readonly_file(&leaf.name)?)?; + let guard = FixedLockedFile::try_lock( + leaf.file.take().ok_or_else(invalid)?, + LockMode::Exclusive, + )? + .ok_or(FixedControlError::Invalid(FixedControlProblem::Busy))?; + action.held[index] = Some(guard); + } + } + let mut scratch = [0; 4096]; + let mut count = 0; + directory.visit_entries(&mut scratch, &mut |entry| { + count += 1; + if count > 5 || !action.controls.iter().flatten().any(|c| c.name == entry) { + return Err(std::io::ErrorKind::InvalidData.into()); + } + Ok(()) + })?; + for leaf in action.controls.iter().flatten() { + grant.check_time()?; + directory.unlink_file(&leaf.name, leaf.stamp.identity())?; + } + directory.sync_namespace()?; + grant.check_time()?; + authority + .member_parent() + .remove_empty_directory(authority.member_name(), member)?; + authority.member_parent().sync_namespace()?; + admission::with_reserved_private_metadata_owned_root( + &mut action.metadata, + authority.artifact_use_guard().ok_or_else(invalid)?, + grant, + action.update.effects(), + &mut |metadata, grant| metadata.publish_control(&action.update, grant), + )?; + Ok(()) + }, + )?; + report.mutated_entries = 1; + report.reclaimed_entries = 1; + Ok(()) +} +/// ACK and disposal are deliberately outside both execution and role permits. +pub(crate) fn finish_work( + work: &mut LegacyNativeWork, + state: &mut LegacyNativePreparationState, +) -> Result<(), FixedControlError> { + if matches!(work.action, Action::Association(_)) { + if let Some(committed) = work.committed.take() { + stream::finish_association_source_step(&mut state.association, committed)?; + } + } else { + let _ = work.committed.take(); + } + let mut job = work.job.take().ok_or_else(invalid)?; + if matches!(work.action, Action::Recover) && job.owner.is_none() { + let version = source::observe_legacy_source_version(&mut job.scope, &work.memory)? + .ok_or_else(invalid)?; + job.owner = Some(source::reopen_legacy_source_owner( + &mut job.scope, + &version, + &work.memory, + )?); + } + if matches!(work.action, Action::Retire) + || (matches!(work.action, Action::Recover) + && job.owner.as_ref().is_some_and(|owner| { + matches!( + owner.closed_version().phase(), + LegacyPhase::Retiring | LegacyPhase::Removed + ) + })) + { + drop(job.proof.take()); + state.association.clear(); + } + if work.done { + state.association.clear(); + drop(job); + } else { + state.job = Some(job); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn legacy_generation_advisory_scan_exceeds_one_hundred_entries() { + let mut nonce = [0; 16]; + getrandom::fill(&mut nonce).unwrap(); + let path = std::env::temp_dir().canonicalize().unwrap().join(format!( + "legacy-selection-{:032x}", + u128::from_le_bytes(nonce) + )); + std::fs::create_dir(&path).unwrap(); + let active = path.join("gen-000-active"); + std::fs::create_dir(&active).unwrap(); + std::fs::write(active.join("graph.ldb"), b"active native graph").unwrap(); + let modern = path.join("gen-001-modern"); + std::fs::create_dir(&modern).unwrap(); + std::fs::write(modern.join(".generation-origin"), b"unknown origin bytes").unwrap(); + let unknown = path.join("unknown-state"); + std::fs::create_dir(&unknown).unwrap(); + std::fs::write(unknown.join("proof"), b"retain unknown state").unwrap(); + for index in 0..150 { + std::fs::create_dir(path.join(format!("gen-legacy-{index:03}"))).unwrap(); + } + let root = RootedDirectory::open_readonly_root(&path, locks::path_identity(&path).unwrap()) + .unwrap(); + for _ in 0..2 { + let mut name = [0; 128]; + let mut len = 0; + select_generation_name( + &root, + root.identity(), + b"000-active", + None, + &mut name, + &mut len, + &|| false, + ) + .unwrap(); + assert_eq!(&name[..len], b"gen-legacy-000"); + } + assert_eq!( + std::fs::read(active.join("graph.ldb")).unwrap(), + b"active native graph" + ); + assert_eq!( + std::fs::read(modern.join(".generation-origin")).unwrap(), + b"unknown origin bytes" + ); + assert_eq!( + std::fs::read(unknown.join("proof")).unwrap(), + b"retain unknown state" + ); + assert_eq!(std::fs::read_dir(&path).unwrap().count(), 153); + let visited = std::cell::Cell::new(0); + let mut name = [0; 128]; + let mut len = 0; + let error = select_generation_name( + &root, + root.identity(), + b"000-active", + None, + &mut name, + &mut len, + &|| { + visited.set(visited.get() + 1); + visited.get() == 4 + }, + ) + .unwrap_err(); + assert_eq!(error.kind(), std::io::ErrorKind::Interrupted); + assert_eq!(visited.get(), 4); + assert_eq!(std::fs::read_dir(&path).unwrap().count(), 153); + drop(root); + std::fs::remove_dir_all(path).unwrap(); + } +} diff --git a/src/storage/maintenance_legacy_guard.rs b/src/storage/maintenance_legacy_guard.rs new file mode 100644 index 00000000..1b02c272 --- /dev/null +++ b/src/storage/maintenance_legacy_guard.rs @@ -0,0 +1,1507 @@ +//! Administrative legacy quiescence. Control coverage is independent of payload +//! origin. Only actual current OS exclusion plus a bound operator confirmation +//! can enroll an old scope; observations and serialized versions grant nothing. +use super::layout::DirectLayout; +use super::locks::{ + self, FileIdentity, FixedControlError, FixedControlKind as Kind, + FixedControlProblem as Problem, FixedControlScratch, FixedFileStamp, FixedLockedFile, LockMode, + LockedFile, RootedDirectory, WriterLease, +}; +use super::maintenance_budget::{CleanupMemory, MutationGrant}; +use super::maintenance_controls as codec; +use crate::error::NativeError; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::path::{Path, PathBuf}; + +pub(super) const ADMIN_GATE: &[u8] = b".legacy-root-cohort.use.lock"; +pub(super) const ADMIN_STATE: &[u8] = b".legacy-root-cohort.state"; +pub(super) const ADMIN_OWNER: &[u8] = b".legacy-root-cohort.owner.lock"; +pub(super) const GATE: &[u8] = b".legacy-source.use.lock"; +pub(super) const STATE: &[u8] = b".legacy-source.state"; +pub(super) const OWNER: &[u8] = b".legacy-source.owner.lock"; +pub(super) const BINDING_BYTES: usize = 1024; +pub(super) const RECORD_BYTES: usize = 1024; +pub(super) const SLOT0: u64 = BINDING_BYTES as u64; +pub(super) const SLOT1: u64 = SLOT0 + codec::SLOT_BYTES as u64; +pub(super) const SELECTOR: u64 = SLOT1 + codec::SLOT_BYTES as u64; +pub(super) const STATE_BYTES: u64 = SELECTOR + codec::SELECTOR_BYTES as u64; +pub(super) fn invalid(p: Problem) -> FixedControlError { + FixedControlError::Invalid(p) +} +pub(super) fn primary(reason: &str) -> NativeError { + NativeError::InvalidInput(reason.to_owned()) +} +pub(super) fn id(out: &mut [u8], v: FileIdentity) { + let (a, b) = v.fixed_words(); + out[..8].copy_from_slice(&a.to_le_bytes()); + out[8..16].copy_from_slice(&b.to_le_bytes()); +} +pub(super) fn read_id(bytes: &[u8]) -> FileIdentity { + FileIdentity::from_fixed_words( + u64::from_le_bytes(bytes[..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + ) +} +pub(super) fn checksum(bytes: &mut [u8]) { + let end = bytes.len() - 32; + let hash = Sha256::digest(&bytes[..end]); + bytes[end..].copy_from_slice(&hash); +} +pub(super) fn checked(bytes: &[u8]) -> bool { + let end = bytes.len() - 32; + Sha256::digest(&bytes[..end])[..] == bytes[end..] +} +pub(super) fn component(bytes: &[u8]) -> bool { + !bytes.is_empty() + && bytes.len() <= 255 + && bytes != b"." + && bytes != b".." + && !bytes.iter().any(|b| matches!(b, 0 | b'/' | b'\\')) +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum LegacyScopeKind { + DefaultDirect, + ManagedGeneration, + NativeStaging, + ManagedAdministration, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum LegacyPhase { + Active, + Preparing, + Retiring, + Removed, +} +#[derive(Clone, Copy, PartialEq, Eq)] +pub(super) struct Binding { + pub(super) kind: LegacyScopeKind, + pub(super) root: FileIdentity, + pub(super) gate: FileIdentity, + pub(super) state: FileIdentity, + pub(super) owner: FileIdentity, + pub(super) nonce: [u8; 32], + pub(super) owner_nonce: [u8; 32], + pub(super) key: [u8; 64], + pub(super) database: [u8; 256], + pub(super) database_len: usize, + pub(super) cache: [u8; 256], + pub(super) cache_len: usize, + pub(super) repository: FileIdentity, +} +impl Binding { + pub(super) fn encode(self) -> [u8; BINDING_BYTES] { + let mut out = [0; BINDING_BYTES]; + out[..8].copy_from_slice(b"CGLGB001"); + out[8] = match self.kind { + LegacyScopeKind::DefaultDirect => 1, + LegacyScopeKind::ManagedGeneration => 2, + LegacyScopeKind::NativeStaging => 3, + LegacyScopeKind::ManagedAdministration => 4, + }; + id(&mut out[16..32], self.root); + id(&mut out[32..48], self.gate); + id(&mut out[48..64], self.state); + id(&mut out[64..80], self.owner); + out[80..112].copy_from_slice(&self.nonce); + out[112..144].copy_from_slice(&self.owner_nonce); + out[144..208].copy_from_slice(&self.key); + out[208..210].copy_from_slice(&(self.database_len as u16).to_le_bytes()); + out[210..212].copy_from_slice(&(self.cache_len as u16).to_le_bytes()); + out[216..472].copy_from_slice(&self.database); + out[472..728].copy_from_slice(&self.cache); + id(&mut out[728..744], self.repository); + checksum(&mut out); + out + } + pub(super) fn decode(bytes: &[u8; BINDING_BYTES]) -> Result { + if &bytes[..8] != b"CGLGB001" + || !checked(bytes) + || bytes[9..16] + .iter() + .chain(bytes[212..216].iter()) + .chain(bytes[744..992].iter()) + .any(|b| *b != 0) + { + return Err(invalid(Problem::InvalidRecord)); + } + let kind = match bytes[8] { + 1 => LegacyScopeKind::DefaultDirect, + 2 => LegacyScopeKind::ManagedGeneration, + 3 => LegacyScopeKind::NativeStaging, + 4 => LegacyScopeKind::ManagedAdministration, + _ => return Err(invalid(Problem::InvalidRecord)), + }; + let database_len = u16::from_le_bytes(bytes[208..210].try_into().unwrap()) as usize; + let cache_len = u16::from_le_bytes(bytes[210..212].try_into().unwrap()) as usize; + let database: [u8; 256] = bytes[216..472].try_into().unwrap(); + let cache: [u8; 256] = bytes[472..728].try_into().unwrap(); + if database_len > 255 + || cache_len > 255 + || (kind != LegacyScopeKind::NativeStaging && !component(&database[..database_len])) + || !component(&cache[..cache_len]) + || database[database_len..] + .iter() + .chain(cache[cache_len..].iter()) + .any(|b| *b != 0) + { + return Err(invalid(Problem::InvalidRecord)); + } + Ok(Self { + kind, + root: read_id(&bytes[16..32]), + gate: read_id(&bytes[32..48]), + state: read_id(&bytes[48..64]), + owner: read_id(&bytes[64..80]), + nonce: bytes[80..112].try_into().unwrap(), + owner_nonce: bytes[112..144].try_into().unwrap(), + key: bytes[144..208].try_into().unwrap(), + database, + database_len, + cache, + cache_len, + repository: read_id(&bytes[728..744]), + }) + } +} +#[derive(Clone, Copy, PartialEq, Eq)] +pub(super) struct Record { + pub(super) phase: LegacyPhase, + pub(super) version: u64, + pub(super) member: FileIdentity, + pub(super) database: Option, + pub(super) manifest: Option, + pub(super) cache: FileIdentity, + pub(super) root_use: FileIdentity, + pub(super) confirmation: [u8; 32], + pub(super) job: [u8; 32], + pub(super) member_name: [u8; 128], + pub(super) member_len: usize, + pub(super) freeze_cache: bool, + pub(super) operator_generation: u64, + pub(super) operator_pending: bool, + pub(super) pending_source_stamp: Option, + pub(super) root_default_source: Option, +} +impl Record { + pub(super) fn encode(self) -> [u8; RECORD_BYTES] { + let mut out = [0; RECORD_BYTES]; + out[..8].copy_from_slice(b"CGLGR002"); + out[8] = match self.phase { + LegacyPhase::Active => 0, + LegacyPhase::Preparing => 1, + LegacyPhase::Retiring => 2, + LegacyPhase::Removed => 3, + }; + out[9] = u8::from(self.freeze_cache) + | if self.database.is_some() { 2 } else { 0 } + | if self.manifest.is_some() { 4 } else { 0 } + | if self.operator_pending { 8 } else { 0 } + | if self.pending_source_stamp.is_some() { + 16 + } else { + 0 + } + | if self.root_default_source.is_some() { + 32 + } else { + 0 + }; + out[10..12].copy_from_slice(&(self.member_len as u16).to_le_bytes()); + out[16..24].copy_from_slice(&self.version.to_le_bytes()); + id(&mut out[24..40], self.member); + if let Some(stamp) = self.database { + stamp.encode_checkpoint((&mut out[40..120]).try_into().unwrap()); + } + if let Some(stamp) = self.manifest { + stamp.encode_checkpoint((&mut out[120..200]).try_into().unwrap()); + } + id(&mut out[200..216], self.cache); + id(&mut out[216..232], self.root_use); + out[232..264].copy_from_slice(&self.confirmation); + out[264..296].copy_from_slice(&self.job); + out[296..424].copy_from_slice(&self.member_name); + out[424..432].copy_from_slice(&self.operator_generation.to_le_bytes()); + if let Some(stamp) = self.pending_source_stamp { + stamp.encode_checkpoint((&mut out[432..512]).try_into().unwrap()); + } + if let Some(source) = self.root_default_source { + source.encode_checkpoint((&mut out[512..1024]).try_into().unwrap()); + } + out + } + pub(super) fn decode(bytes: &[u8], version: u64) -> Result { + if bytes.len() != RECORD_BYTES + || &bytes[..8] != b"CGLGR002" + || bytes[9] > 63 + || bytes[12..16].iter().any(|b| *b != 0) + { + return Err(invalid(Problem::InvalidRecord)); + } + let phase = match bytes[8] { + 0 => LegacyPhase::Active, + 1 => LegacyPhase::Preparing, + 2 => LegacyPhase::Retiring, + 3 => LegacyPhase::Removed, + _ => return Err(invalid(Problem::InvalidRecord)), + }; + let member_len = u16::from_le_bytes(bytes[10..12].try_into().unwrap()) as usize; + let member_name: [u8; 128] = bytes[296..424].try_into().unwrap(); + if member_len > 128 + || member_name[member_len..].iter().any(|b| *b != 0) + || member_len > 0 && !component(&member_name[..member_len]) + || u64::from_le_bytes(bytes[16..24].try_into().unwrap()) != version + { + return Err(invalid(Problem::InvalidRecord)); + } + Ok(Self { + phase, + version, + member: read_id(&bytes[24..40]), + database: if bytes[9] & 2 != 0 { + Some(FixedFileStamp::decode_checkpoint( + (&bytes[40..120]).try_into().unwrap(), + )?) + } else { + if bytes[40..120].iter().any(|b| *b != 0) { + return Err(invalid(Problem::InvalidRecord)); + } + None + }, + manifest: if bytes[9] & 4 != 0 { + Some(FixedFileStamp::decode_checkpoint( + (&bytes[120..200]).try_into().unwrap(), + )?) + } else { + if bytes[120..200].iter().any(|b| *b != 0) { + return Err(invalid(Problem::InvalidRecord)); + } + None + }, + cache: read_id(&bytes[200..216]), + root_use: read_id(&bytes[216..232]), + confirmation: bytes[232..264].try_into().unwrap(), + job: bytes[264..296].try_into().unwrap(), + member_name, + member_len, + freeze_cache: bytes[9] & 1 == 1, + operator_generation: u64::from_le_bytes(bytes[424..432].try_into().unwrap()), + operator_pending: bytes[9] & 8 != 0, + pending_source_stamp: if bytes[9] & 16 != 0 { + Some(FixedFileStamp::decode_checkpoint( + (&bytes[432..512]).try_into().unwrap(), + )?) + } else { + if bytes[432..512].iter().any(|v| *v != 0) { + return Err(invalid(Problem::InvalidRecord)); + } + None + }, + root_default_source: if bytes[9] & 32 != 0 { + Some( + super::maintenance_admission::LegacyConfirmationSource::decode_checkpoint( + (&bytes[512..1024]).try_into().unwrap(), + )?, + ) + } else { + if bytes[512..1024].iter().any(|v| *v != 0) { + return Err(invalid(Problem::InvalidRecord)); + } + None + }, + }) + } +} +#[derive(Clone, Copy)] +pub(super) struct Observation { + pub(super) binding: Binding, + pub(super) record: Record, + pub(super) commit: codec::CommitFact, + pub(super) state_stamp: FixedFileStamp, + pub(super) pending: bool, +} +pub(super) fn observe_files( + root: &RootedDirectory, + gate: &File, + state: &File, +) -> Result { + observe_files_named(root, gate, state, OWNER) +} +pub(super) fn observe_files_named( + root: &RootedDirectory, + gate: &File, + state: &File, + owner_name: &[u8], +) -> Result { + let owner = root.open_readonly_file(owner_name)?; + observe_source_snapshot(root, gate, state, owner_name, &owner, false) +} +fn observe_source_snapshot( + root: &RootedDirectory, + gate: &File, + state: &File, + owner_name: &[u8], + owner: &File, + allow_pending: bool, +) -> Result { + let gate_stamp = FixedFileStamp::capture(gate, Kind::File)?; + let state_stamp = FixedFileStamp::capture(state, Kind::File)?; + if state_stamp.len() != STATE_BYTES && state_stamp.len() != SOURCE_CAPSULE_STATE_BYTES { + return Err(invalid(Problem::InvalidRecord)); + } + let mut binding_bytes = [0; BINDING_BYTES]; + locks::read_fixed_range(state, 0, &mut binding_bytes)?; + let binding = Binding::decode(&binding_bytes)?; + if binding.root != root.identity() + || binding.gate != gate_stamp.identity() + || binding.state != state_stamp.identity() + || locks::read_coordination_incarnation(gate) + .map_err(|_| invalid(Problem::InvalidRecord))? + != Some(binding.nonce) + { + return Err(invalid(Problem::Changed)); + } + let named_owner = root.open_metadata_file(owner_name)?; + let owner_stamp = FixedFileStamp::capture(owner, Kind::File)?; + if owner_stamp.identity() != binding.owner + || FixedFileStamp::capture(&named_owner, Kind::File)?.identity() != binding.owner + || locks::read_coordination_incarnation(owner) + .map_err(|_| invalid(Problem::InvalidRecord))? + != Some(binding.owner_nonce) + { + return Err(invalid(Problem::Changed)); + } + let mut slots = [[0; codec::SLOT_BYTES]; 2]; + let mut selector = [0; codec::SELECTOR_BYTES]; + locks::read_fixed_range(state, SLOT0, &mut slots[0])?; + locks::read_fixed_range(state, SLOT1, &mut slots[1])?; + locks::read_fixed_range(state, SELECTOR, &mut selector)?; + let sf = slots + .each_ref() + .map(|bytes| codec::decode_slot(bytes, &binding.nonce).ok()); + let commit = codec::decode_commit(&selector[..codec::COMMIT_BYTES], &binding.nonce).ok(); + let intent = codec::decode_intent(&selector[codec::COMMIT_BYTES..], &binding.nonce).ok(); + if selector[codec::COMMIT_BYTES..] + .iter() + .any(|byte| *byte != 0) + && intent.is_none() + { + return Err(invalid(Problem::InvalidRecord)); + } + let (commit, pending) = match codec::recovery_step(commit, intent, sf) { + codec::RecoveryStep::Committed(c) if !allow_pending || intent.is_none() => (c, false), + codec::RecoveryStep::Committed(_) if allow_pending => { + let i = intent.ok_or_else(|| invalid(Problem::InvalidRecord))?; + let before = sf[1 - i.target].ok_or_else(|| invalid(Problem::InvalidRecord))?; + if before.generation != i.before { + return Err(invalid(Problem::InvalidRecord)); + } + ( + codec::CommitFact { + generation: i.before, + active: 1 - i.target, + slot_digest: before.digest, + }, + true, + ) + } + codec::RecoveryStep::RewriteIntendedSlot(i) + | codec::RecoveryStep::CommitIntendedSlot(i) + if allow_pending => + { + let intended = + &selector[codec::COMMIT_BYTES + 96..codec::COMMIT_BYTES + 96 + codec::SLOT_BYTES]; + let intended_fact = codec::decode_slot(intended, &binding.nonce) + .map_err(|_| invalid(Problem::InvalidRecord))?; + if intended_fact.payload_len != RECORD_BYTES || intended_fact.digest != i.slot_digest { + return Err(invalid(Problem::InvalidRecord)); + } + Record::decode(&intended[56..56 + RECORD_BYTES], i.after)?; + let before = sf[1 - i.target].ok_or_else(|| invalid(Problem::InvalidRecord))?; + if before.generation != i.before { + return Err(invalid(Problem::InvalidRecord)); + } + ( + codec::CommitFact { + generation: i.before, + active: 1 - i.target, + slot_digest: before.digest, + }, + true, + ) + } + _ => return Err(invalid(Problem::Busy)), + }; + let fact = sf[commit.active].ok_or_else(|| invalid(Problem::InvalidRecord))?; + if fact.payload_len != RECORD_BYTES { + return Err(invalid(Problem::InvalidRecord)); + } + let record_bytes: [u8; RECORD_BYTES] = slots[commit.active][56..56 + RECORD_BYTES] + .try_into() + .unwrap(); + let record = Record::decode(&record_bytes, commit.generation)?; + if let Some(capsule) = read_creation_capsule(root, state)? { + if capsule.template.kind != binding.kind + || capsule.template.repository != binding.repository + || capsule.template.key != binding.key + || capsule.template.database != binding.database + || capsule.template.database_len != binding.database_len + || capsule.template.cache != binding.cache + || capsule.template.cache_len != binding.cache_len + { + return Err(invalid(Problem::Changed)); + } + let mut hash = Sha256::new(); + hash.update(b"CGLegacySourceBirthCommitted01"); + hash.update(capsule.permission.job); + hash.update(binding_bytes); + let final_nonce: [u8; 32] = hash.finalize().into(); + if locks::created_file_nonce(state)? != Some(final_nonce) { + return Err(invalid(Problem::Busy)); + } + } + state_stamp.recheck(state)?; + gate_stamp.recheck(gate)?; + Ok(Observation { + binding, + record, + commit, + state_stamp, + pending, + }) +} +/// A current OS Shared use lease; it is retained through producer/native use. +#[derive(Debug)] +pub(crate) struct LegacySourceUseGuard { + gate: LockedFile, + state: File, + _root_pin: File, + state_stamp: FixedFileStamp, + root: PathBuf, + nonce: [u8; 32], +} +#[derive(Debug)] +pub(crate) struct LegacyNativeUseGuard { + use_guard: LegacySourceUseGuard, + database: PathBuf, +} +fn primary_observe( + parent: &Path, +) -> Result, NativeError> { + let path = parent.join(std::str::from_utf8(GATE).unwrap()); + let gate = match locks::try_open_existing_locked(&path, LockMode::Shared) { + Ok(Some(g)) => g, + Ok(None) => return Err(primary("legacy source transition is busy")), + Err(NativeError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e), + }; + // Unrecognized names never become administrative authority or a new primary + // requirement. Once a genuine bound cohort is recognized, torn state defers. + let Some(nonce) = gate.coordination_incarnation()? else { + return Ok(None); + }; + let root = RootedDirectory::open_readonly_root(parent, locks::path_identity(parent)?)?; + let state = match root.open_readonly_file(STATE) { + Ok(f) => f, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + let gate_file = root.open_readonly_file(GATE)?; + let mut prefix = [0; BINDING_BYTES]; + if locks::read_fixed_range(&state, 0, &mut prefix).is_err() { + return Ok(None); + } + let binding = match Binding::decode(&prefix) { + Ok(b) if b.nonce == nonce => b, + _ => return Ok(None), + }; + let observed = match observe_files(&root, &gate_file, &state) { + Ok(o) => o, + Err(_) if recognized_source_binding(&root, &gate_file, &state, OWNER) => { + return Err(primary("legacy source publication requires recovery")) + } + Err(_) => return Ok(None), + }; + if binding.gate != gate.identity()? || !gate.is_current()? { + return Err(primary("legacy source gate changed")); + } + let guard = LegacySourceUseGuard { + gate, + state, + _root_pin: root.pin_handle()?, + state_stamp: observed.state_stamp, + root: parent.to_owned(), + nonce, + }; + Ok(Some((guard, observed.binding, observed.record))) +} +pub(crate) fn before_artifact_producer( + root: &Path, +) -> Result, NativeError> { + let Some(parent) = root.parent() else { + return Ok(None); + }; + let Some((guard, binding, record)) = primary_observe(parent)? else { + return Ok(None); + }; + if root.file_name().map(|n| n.as_encoded_bytes()) != Some(&binding.cache[..binding.cache_len]) { + return Ok(None); + } + if (record.phase != LegacyPhase::Active || record.operator_pending) + && (binding.kind == LegacyScopeKind::DefaultDirect || record.freeze_cache) + { + return Err(primary("legacy source is closed for maintenance")); + } + if binding.kind == LegacyScopeKind::ManagedGeneration { + return Ok(None); + } + Ok(Some(guard)) +} +pub(crate) fn before_native_open( + database: &Path, +) -> Result, NativeError> { + let Some(parent) = database.parent() else { + return Ok(None); + }; + if let Some((guard, binding, record)) = primary_observe(parent)? { + if binding.kind == LegacyScopeKind::DefaultDirect + && database.file_name().map(|n| n.as_encoded_bytes()) + == Some(&binding.database[..binding.database_len]) + { + if record.phase != LegacyPhase::Active || record.operator_pending { + return Err(primary( + "legacy source is closed for maintenance; rebuild or recovery is required", + )); + } + return Ok(Some(LegacyNativeUseGuard { + use_guard: guard, + database: database.to_owned(), + })); + } + } + if database.file_name().is_some_and(|n| n == "graph.ldb") + && parent + .parent() + .and_then(|p| p.file_name()) + .is_some_and(|n| n == "generations") + { + if let Some(storage) = parent.parent().and_then(Path::parent) { + if let Some((guard, binding, record)) = primary_observe(storage)? { + if binding.kind == LegacyScopeKind::ManagedGeneration { + let matches = record.member_len > 0 + && parent.file_name().map(|n| n.as_encoded_bytes()) + == Some(&record.member_name[..record.member_len]); + if (record.phase != LegacyPhase::Active || record.operator_pending) && matches { + return Err(primary("legacy generation is closed for maintenance")); + } + if !matches { + return Ok(None); + } + return Ok(Some(LegacyNativeUseGuard { + use_guard: guard, + database: database.to_owned(), + })); + } + } + } + } + Ok(None) +} +pub(crate) fn recheck_before_native( + guard: Option<&LegacyNativeUseGuard>, + database: &Path, +) -> Result<(), NativeError> { + if let Some(guard) = guard { + if guard.database != database + || !guard.use_guard.gate.is_current()? + || guard + .use_guard + .state_stamp + .recheck(&guard.use_guard.state) + .is_err() + || locks::read_coordination_incarnation(&guard.use_guard.gate_file()?)? + != Some(guard.use_guard.nonce) + { + return Err(primary("legacy native source changed before open")); + } + Ok(()) + } else { + let _temporary = before_native_open(database)?; + Ok(()) + } +} +impl LegacySourceUseGuard { + fn gate_file(&self) -> Result { + Ok(File::open( + self.root.join(std::str::from_utf8(GATE).unwrap()), + )?) + } +} + +pub(super) const STAGING_GATE: &[u8] = b".legacy-staging.use.lock"; +pub(super) const STAGING_STATE: &[u8] = b".legacy-staging.state"; +pub(super) const STAGING_OWNER: &[u8] = b".legacy-staging.owner.lock"; +/// The ordinary staging producer retains this Shared kernel lease through the +/// last native COPY reader and disposal of its staged result. +#[derive(Debug)] +pub(crate) struct LegacyStagingProducerGuard { + _gate: LockedFile, + _root: File, +} +pub(crate) fn before_staging_producer( + path: &Path, +) -> Result, NativeError> { + let staging = if path.file_name().is_some_and(|n| n == "native-staging") { + path + } else if path + .parent() + .is_some_and(|p| p.file_name().is_some_and(|n| n == "native-staging")) + { + path.parent().unwrap() + } else { + return Ok(None); + }; + let Some(state_path) = staging + .parent() + .filter(|p| p.file_name().is_some_and(|n| n == ".codebaseGraph")) + else { + return Ok(None); + }; + // The conventional address alone grants no cleanup authority. Publishing a + // producer coordination lease is safe even before administrative enrollment; + // it makes already-running upgraded staging producers visible to enrollment. + let root = RootedDirectory::open_readonly_root(state_path, locks::path_identity(state_path)?)?; + let gate_path = state_path.join(std::str::from_utf8(STAGING_GATE).unwrap()); + let gate = match locks::open_existing_shared_coordination(&gate_path) { + Ok(gate) => gate, + Err(NativeError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => { + match locks::create_new_coordination_in(&root, STAGING_GATE, &gate_path) { + Ok(gate) => { + let mut nonce = [0; 32]; + getrandom::fill(&mut nonce) + .map_err(|_| primary("legacy staging nonce unavailable"))?; + gate.initialize_creation_incarnation(nonce)?; + gate.share_after_creation()? + } + Err(NativeError::Io(e)) if e.kind() == std::io::ErrorKind::AlreadyExists => { + locks::open_existing_shared_coordination(&gate_path)? + } + Err(e) => return Err(e), + } + } + Err(e) => return Err(e), + }; + if !gate.is_current()? { + return Err(primary("legacy staging gate changed")); + } + if let (Ok(state), Ok(gate_file)) = ( + root.open_readonly_file(STAGING_STATE), + root.open_readonly_file(STAGING_GATE), + ) { + if let Ok(observed) = observe_files_named(&root, &gate_file, &state, STAGING_OWNER) { + if observed.binding.kind == LegacyScopeKind::NativeStaging + && observed.binding.gate == gate.identity()? + && (observed.record.phase != LegacyPhase::Active + || observed.record.operator_pending) + { + return Err(primary("legacy staging source is closed for maintenance")); + } + } else if recognized_source_binding(&root, &gate_file, &state, STAGING_OWNER) { + return Err(primary("legacy staging publication requires recovery")); + } + } + Ok(Some(LegacyStagingProducerGuard { + _gate: gate, + _root: root.pin_handle()?, + })) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Arc; + fn fixture() -> PathBuf { + let mut nonce = [0; 16]; + getrandom::fill(&mut nonce).unwrap(); + let path = + std::env::temp_dir().join(format!("dc-legacy-use-{:032x}", u128::from_le_bytes(nonce))); + std::fs::create_dir_all(path.join(".codebaseGraph/native-staging")).unwrap(); + path + } + #[test] + fn dc_legacy_staging_retains_actual_shared_gate_through_arc_result_lifetime() { + fn send_sync() {} + send_sync::(); + let repo = fixture(); + let staging = repo.join(".codebaseGraph/native-staging"); + let guard = Arc::new(before_staging_producer(&staging).unwrap().unwrap()); + let result = guard.clone(); + drop(guard); + let gate = staging + .parent() + .unwrap() + .join(std::str::from_utf8(STAGING_GATE).unwrap()); + assert!(locks::try_open_existing_locked(&gate, LockMode::Exclusive) + .unwrap() + .is_none()); + drop(result); + assert!(locks::try_open_existing_locked(&gate, LockMode::Exclusive) + .unwrap() + .is_some()); + std::fs::remove_dir_all(repo).unwrap(); + } + #[test] + fn dc_legacy_staging_custom_root_is_not_enrolled_or_created() { + let repo = fixture(); + let custom = repo.join("custom"); + assert!(before_staging_producer(&custom).unwrap().is_none()); + assert!(!custom.exists()); + std::fs::remove_dir_all(repo).unwrap(); + } + #[test] + fn dc_legacy_staging_closed_record_refuses_new_actual_producer() { + let repo = fixture(); + let state_path = repo.join(".codebaseGraph"); + let staging = state_path.join("native-staging"); + drop(before_staging_producer(&staging).unwrap()); + let gate_path = state_path.join(std::str::from_utf8(STAGING_GATE).unwrap()); + let gate = locks::open_locked(&gate_path, LockMode::Exclusive).unwrap(); + let owner = locks::open_locked( + state_path.join(std::str::from_utf8(STAGING_OWNER).unwrap()), + LockMode::Exclusive, + ) + .unwrap(); + owner.initialize_creation_incarnation([9; 32]).unwrap(); + let state_file = std::fs::OpenOptions::new() + .create_new(true) + .read(true) + .write(true) + .open(state_path.join(std::str::from_utf8(STAGING_STATE).unwrap())) + .unwrap(); + let mut cache = [0; 256]; + cache[..14].copy_from_slice(b"native-staging"); + let binding = Binding { + kind: LegacyScopeKind::NativeStaging, + root: locks::path_identity(&state_path).unwrap(), + gate: gate.identity().unwrap(), + state: locks::file_identity(&state_file).unwrap(), + owner: owner.identity().unwrap(), + nonce: gate.coordination_incarnation().unwrap().unwrap(), + owner_nonce: owner.coordination_incarnation().unwrap().unwrap(), + key: [0; 64], + database: [0; 256], + database_len: 0, + cache, + cache_len: 14, + repository: locks::path_identity(&repo).unwrap(), + }; + let member = locks::path_identity(&staging).unwrap(); + let record = Record { + phase: LegacyPhase::Preparing, + version: 2, + member, + database: None, + manifest: None, + cache: member, + root_use: binding.gate, + confirmation: [1; 32], + job: [2; 32], + member_name: [0; 128], + member_len: 0, + freeze_cache: false, + operator_generation: 3, + operator_pending: false, + pending_source_stamp: None, + root_default_source: None, + }; + let mut slot = [0; codec::SLOT_BYTES]; + let fact = codec::encode_slot(&mut slot, &binding.nonce, 2, &record.encode()).unwrap(); + let mut selector = [0; codec::SELECTOR_BYTES]; + codec::encode_commit( + &mut selector[..codec::COMMIT_BYTES], + &binding.nonce, + codec::CommitFact { + generation: 2, + active: 0, + slot_digest: fact.digest, + }, + ) + .unwrap(); + use std::io::{Seek, SeekFrom, Write}; + let mut file = &state_file; + file.write_all(&binding.encode()).unwrap(); + file.write_all(&slot).unwrap(); + file.seek(SeekFrom::Start(SELECTOR)).unwrap(); + file.write_all(&selector).unwrap(); + file.sync_all().unwrap(); + drop((gate, owner)); + assert!(before_staging_producer(&staging).is_err()); + std::fs::remove_dir_all(repo).unwrap(); + } +} + +pub(super) fn write_range( + file: &File, + offset: u64, + bytes: &[u8], + grant: &mut MutationGrant, +) -> Result<(), FixedControlError> { + grant.check()?; + locks::write_fixed_range(file, offset, bytes, grant)?; + grant.take_entry()?; + file.sync_all()?; + grant.check()?; + Ok(()) +} + +pub(super) fn publish_record( + state: &File, + binding: Binding, + current: codec::CommitFact, + record: Record, + grant: &mut MutationGrant, +) -> Result { + if record.version + != current + .generation + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))? + { + return Err(invalid(Problem::InvalidRecord)); + } + let mut slot = [0; codec::SLOT_BYTES]; + let fact = codec::encode_slot(&mut slot, &binding.nonce, record.version, &record.encode()) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let target = 1 - current.active; + let mut intent = [0; codec::INTENT_BYTES]; + codec::encode_intent(&mut intent, &binding.nonce, current, &slot) + .map_err(|_| invalid(Problem::InvalidRecord))?; + // Fixed own-control codecs only. No source payload/config/reference decoding + // or hashing occurs in this short admitted transition. + write_range(state, SELECTOR + codec::COMMIT_BYTES as u64, &intent, grant)?; + write_range(state, if target == 0 { SLOT0 } else { SLOT1 }, &slot, grant)?; + let commit = codec::CommitFact { + generation: record.version, + active: target, + slot_digest: fact.digest, + }; + let mut bytes = [0; codec::COMMIT_BYTES]; + codec::encode_commit(&mut bytes, &binding.nonce, commit) + .map_err(|_| invalid(Problem::InvalidRecord))?; + write_range(state, SELECTOR, &bytes, grant)?; + write_range( + state, + SELECTOR + codec::COMMIT_BYTES as u64, + &[0; codec::INTENT_BYTES], + grant, + )?; + Ok(commit) +} + +pub(super) fn recover_source_state( + root: &RootedDirectory, + gate: &File, + state: &File, + owner_name: &[u8], + grant: &mut MutationGrant, +) -> Result<(), FixedControlError> { + let mut wire = [0; BINDING_BYTES]; + locks::read_fixed_range(state, 0, &mut wire)?; + let binding = Binding::decode(&wire)?; + let owner = root.open_readonly_file(owner_name)?; + if root.identity() != binding.root + || FixedFileStamp::capture(gate, Kind::File)?.identity() != binding.gate + || FixedFileStamp::capture(state, Kind::File)?.identity() != binding.state + || FixedFileStamp::capture(&owner, Kind::File)?.identity() != binding.owner + || locks::read_coordination_incarnation(gate) + .map_err(|_| invalid(Problem::InvalidRecord))? + != Some(binding.nonce) + || locks::read_coordination_incarnation(&owner) + .map_err(|_| invalid(Problem::InvalidRecord))? + != Some(binding.owner_nonce) + { + return Err(invalid(Problem::Changed)); + } + let mut slots = [[0; codec::SLOT_BYTES]; 2]; + let mut selector = [0; codec::SELECTOR_BYTES]; + locks::read_fixed_range(state, SLOT0, &mut slots[0])?; + locks::read_fixed_range(state, SLOT1, &mut slots[1])?; + locks::read_fixed_range(state, SELECTOR, &mut selector)?; + let sf = slots + .each_ref() + .map(|b| codec::decode_slot(b, &binding.nonce).ok()); + let commit = codec::decode_commit(&selector[..codec::COMMIT_BYTES], &binding.nonce).ok(); + let intent = codec::decode_intent(&selector[codec::COMMIT_BYTES..], &binding.nonce).ok(); + let step = codec::recovery_step(commit, intent, sf); + let intended = match step { + codec::RecoveryStep::RewriteIntendedSlot(i) + | codec::RecoveryStep::CommitIntendedSlot(i) => Some(i), + codec::RecoveryStep::Committed(_) => None, + codec::RecoveryStep::Unknown => return Err(invalid(Problem::InvalidRecord)), + }; + if let Some(i) = intended { + let slot = + &selector[codec::COMMIT_BYTES + 96..codec::COMMIT_BYTES + 96 + codec::SLOT_BYTES]; + let fact = codec::decode_slot(slot, &binding.nonce) + .map_err(|_| invalid(Problem::InvalidRecord))?; + if fact.payload_len != RECORD_BYTES { + return Err(invalid(Problem::InvalidRecord)); + } + Record::decode(&slot[56..56 + RECORD_BYTES], i.after)?; + if matches!(step, codec::RecoveryStep::RewriteIntendedSlot(_)) { + write_range( + state, + if i.target == 0 { SLOT0 } else { SLOT1 }, + slot, + grant, + )?; + } + let mut commit_wire = [0; codec::COMMIT_BYTES]; + codec::encode_commit( + &mut commit_wire, + &binding.nonce, + codec::CommitFact { + generation: i.after, + active: i.target, + slot_digest: i.slot_digest, + }, + ) + .map_err(|_| invalid(Problem::InvalidRecord))?; + write_range(state, SELECTOR, &commit_wire, grant)?; + } + if intent.is_some() { + write_range( + state, + SELECTOR + codec::COMMIT_BYTES as u64, + &[0; codec::INTENT_BYTES], + grant, + )?; + } + Ok(()) +} + +pub(crate) fn reactivate_legacy_source( + database: &Path, + manifest: &Path, + writer: &WriterLease, +) -> Result<(), crate::error::NativeError> { + fn run( + database: &Path, + manifest: &Path, + writer: &WriterLease, + ) -> Result<(), FixedControlError> { + let Some(parent) = database + .parent() + .filter(|p| p.file_name().is_some_and(|n| n == ".codebaseGraph")) + else { + return Ok(()); + }; + if manifest != parent.join("manifest.json") { + return Ok(()); + } + let memory = CleanupMemory::new(1024 * 1024).map_err(FixedControlError::Resource)?; + let path_charge = database + .as_os_str() + .as_encoded_bytes() + .len() + .checked_add(manifest.as_os_str().as_encoded_bytes().len()) + .and_then(|n| n.checked_mul(16)) + .and_then(|n| n.checked_add(32768)) + .ok_or(FixedControlError::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?; + let _floor = memory + .reserve(path_charge) + .map_err(FixedControlError::Resource)?; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(FixedControlError::Resource)?, + )?; + let root = RootedDirectory::open_fixed( + parent, + locks::path_identity(parent).map_err(|_| invalid(Problem::Changed))?, + true, + &mut scratch, + )?; + let state = match root.open_existing(STATE) { + Ok(f) => f, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()), + Err(e) => return Err(e.into()), + }; + let mut wire = [0; BINDING_BYTES]; + if locks::read_fixed_range(&state, 0, &mut wire).is_err() { + return Ok(()); + } + let binding = match Binding::decode(&wire) { + Ok(b) if b.kind == LegacyScopeKind::DefaultDirect => b, + _ => return Ok(()), + }; + let mut hash = Sha256::new(); + hash.update(database.as_os_str().as_encoded_bytes()); + hash.update([0]); + hash.update(manifest.as_os_str().as_encoded_bytes()); + let digest = hash.finalize(); + let mut key = [0; 64]; + for (i, b) in digest.iter().enumerate() { + key[2 * i] = b"0123456789abcdef"[(b >> 4) as usize]; + key[2 * i + 1] = b"0123456789abcdef"[(b & 15) as usize]; + } + if binding.key != key + || database.file_name().map(|n| n.as_encoded_bytes()) + != Some(&binding.database[..binding.database_len]) + { + return Ok(()); + } + let layout = DirectLayout::new(database, manifest); + let writer_file = + locks::open_fixed_control(&layout.writer_lock_path(), Kind::File, false, &mut scratch)?; + let writer_stamp = FixedFileStamp::capture(&writer_file, Kind::File)?; + if !writer + .matches_fixed_current(&writer_stamp, LockMode::Exclusive) + .map_err(|_| invalid(Problem::Changed))? + { + return Err(invalid(Problem::Changed)); + } + let gate_file = root.open_existing(GATE)?; + let gate = FixedLockedFile::try_lock(gate_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + let mut grant = MutationGrant::start(); + recover_source_state(&root, gate.file(), &state, OWNER, &mut grant)?; + let observed = observe_files(&root, gate.file(), &state)?; + if observed.binding.encode() != binding.encode() { + return Err(invalid(Problem::Changed)); + } + if observed.record.phase == LegacyPhase::Active { + return Ok(()); + } + if observed.record.phase == LegacyPhase::Retiring { + return Err(invalid(Problem::Busy)); + } + let family_path = parent.join(".database-use-family.lock"); + let family_file = locks::open_fixed_control(&family_path, Kind::File, true, &mut scratch)?; + let _family = FixedLockedFile::try_lock(family_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + let (_, native_path) = super::maintenance_ownership::database_lock_path(database, false) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let native_file = locks::open_fixed_control(&native_path, Kind::File, true, &mut scratch)?; + let _native = FixedLockedFile::try_lock(native_file, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + let artifact = root.open_directory(&binding.cache[..binding.cache_len])?; + if artifact.identity() != observed.record.cache { + return Err(invalid(Problem::Changed)); + } + let artifact_use = artifact.open_existing(b".use.lock")?; + let artifact_use = FixedLockedFile::try_lock(artifact_use, LockMode::Exclusive)? + .ok_or_else(|| invalid(Problem::Busy))?; + if FixedFileStamp::capture(artifact_use.file(), Kind::File)?.identity() + != observed.record.root_use + { + return Err(invalid(Problem::Changed)); + } + let mut next = observed.record; + next.phase = LegacyPhase::Active; + next.operator_pending = false; + next.pending_source_stamp = None; + next.version = next + .version + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))?; + // Keeping the same job is harmless for Active, but a fresh nonce makes + // stale preparation certificates unambiguously unrelated on reclose. + getrandom::fill(&mut next.job).map_err(|_| invalid(Problem::InvalidRecord))?; + publish_record(&state, binding, observed.commit, next, &mut grant)?; + Ok(()) + } + run(database, manifest, writer).map_err(|e| match e { + FixedControlError::Io(e) => crate::error::NativeError::Io(e), + _ => crate::error::NativeError::InvalidInput( + "legacy Direct source is busy, changed, or requires retirement recovery".to_owned(), + ), + }) +} + +/// All source-state codecs are prepared outside the execution permit. This +/// owner is only an image; the retained source kernel verifies actual custody. +pub(super) struct PreparedSourcePublication { + before: codec::CommitFact, + after: Record, + binding: Binding, + slot: [u8; codec::SLOT_BYTES], + intent: [u8; codec::INTENT_BYTES], + commit_image: [u8; codec::COMMIT_BYTES], + commit: codec::CommitFact, + _floor: super::maintenance_budget::MemoryReservation, +} +impl PreparedSourcePublication { + pub(super) fn prepare( + binding: Binding, + before: codec::CommitFact, + after: Record, + memory: &CleanupMemory, + ) -> Result, FixedControlError> { + let floor = memory + .reserve(std::mem::size_of::() * 2 + 4096) + .map_err(FixedControlError::Resource)?; + if after.version + != before + .generation + .checked_add(1) + .ok_or_else(|| invalid(Problem::InvalidRecord))? + { + return Err(invalid(Problem::InvalidRecord)); + } + let mut slot = [0; codec::SLOT_BYTES]; + let fact = codec::encode_slot(&mut slot, &binding.nonce, after.version, &after.encode()) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let mut intent = [0; codec::INTENT_BYTES]; + codec::encode_intent(&mut intent, &binding.nonce, before, &slot) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let commit = codec::CommitFact { + generation: after.version, + active: 1 - before.active, + slot_digest: fact.digest, + }; + let mut commit_image = [0; codec::COMMIT_BYTES]; + codec::encode_commit(&mut commit_image, &binding.nonce, commit) + .map_err(|_| invalid(Problem::InvalidRecord))?; + super::maintenance_budget::MemoryBox::new( + Self { + before, + after, + binding, + slot, + intent, + commit_image, + commit, + _floor: floor, + }, + memory, + ) + .map_err(FixedControlError::Resource) + } + pub(super) fn publish( + &self, + state: &File, + binding: Binding, + before: codec::CommitFact, + after: Record, + grant: &mut MutationGrant, + ) -> Result { + if self.before != before || self.binding != binding || self.after != after { + return Err(invalid(Problem::Changed)); + } + write_range( + state, + SELECTOR + codec::COMMIT_BYTES as u64, + &self.intent, + grant, + )?; + write_range( + state, + if self.commit.active == 0 { + SLOT0 + } else { + SLOT1 + }, + &self.slot, + grant, + )?; + write_range(state, SELECTOR, &self.commit_image, grant)?; + write_range( + state, + SELECTOR + codec::COMMIT_BYTES as u64, + &[0; codec::INTENT_BYTES], + grant, + )?; + Ok(self.commit) + } +} + +pub(super) const SOURCE_CAPSULE_BYTES: usize = 2112; +pub(super) const SOURCE_CAPSULE_OFFSET: u64 = STATE_BYTES; +pub(super) const SOURCE_CAPSULE_STATE_BYTES: u64 = STATE_BYTES + SOURCE_CAPSULE_BYTES as u64; +#[derive(Clone, Copy)] +pub(super) struct SourceCreationCapsule { + pub(super) mode: u8, + pub(super) template: Binding, + pub(super) permission: Record, +} +impl SourceCreationCapsule { + pub(super) fn encode(&self) -> [u8; SOURCE_CAPSULE_BYTES] { + let mut out = [0; SOURCE_CAPSULE_BYTES]; + out[..8].copy_from_slice(b"CGLGCR01"); + out[8] = self.mode; + out[16..1040].copy_from_slice(&self.template.encode()); + out[1040..2064].copy_from_slice(&self.permission.encode()); + checksum(&mut out); + out + } + pub(super) fn decode(bytes: &[u8; SOURCE_CAPSULE_BYTES]) -> Result { + if &bytes[..8] != b"CGLGCR01" + || !checked(bytes) + || !(1..=3).contains(&bytes[8]) + || bytes[9..16] + .iter() + .chain(bytes[2064..2080].iter()) + .any(|v| *v != 0) + { + return Err(invalid(Problem::InvalidRecord)); + } + let template = Binding::decode(bytes[16..1040].try_into().unwrap())?; + let permission = Record::decode(&bytes[1040..2064], 1)?; + let zero = FileIdentity::from_fixed_words(0, 0); + if template.gate != zero + || template.state != zero + || template.owner != zero + || template.nonce != permission.job + || permission.phase != LegacyPhase::Active + || !permission.operator_pending + || permission.version != 1 + { + return Err(invalid(Problem::InvalidRecord)); + } + Ok(Self { + mode: bytes[8], + template, + permission, + }) + } +} +pub(super) fn read_creation_capsule( + root: &RootedDirectory, + state: &File, +) -> Result, FixedControlError> { + let before = FixedFileStamp::capture(state, Kind::File)?; + if before.len() != SOURCE_CAPSULE_STATE_BYTES { + return Ok(None); + } + let mut bytes = [0; SOURCE_CAPSULE_BYTES]; + locks::read_fixed_range(state, SOURCE_CAPSULE_OFFSET, &mut bytes)?; + let capsule = SourceCreationCapsule::decode(&bytes)?; + if capsule.template.root != root.identity() { + return Err(invalid(Problem::Changed)); + } + before.recheck(state)?; + Ok(Some(capsule)) +} + +/// Advisory decoding of an exact owned intent. No record becomes current until +/// the later exclusive source callback publishes these already encoded bytes. +pub(super) struct PreparedSourceRecovery { + pub(super) before: Observation, + pub(super) after: Record, + pub(super) commit: codec::CommitFact, + slot: [u8; codec::SLOT_BYTES], + commit_image: [u8; codec::COMMIT_BYTES], + gate_header: [u8; locks::COORDINATION_HEADER_BYTES], + owner_header: [u8; locks::COORDINATION_HEADER_BYTES], + _floor: super::maintenance_budget::MemoryReservation, +} +pub(super) fn prepare_source_observation( + root: &RootedDirectory, + gate: &File, + state: &File, + owner_name: &[u8], + owner: &File, + memory: &CleanupMemory, +) -> Result< + ( + Observation, + Option>, + ), + FixedControlError, +> { + let data = + prepare_source_observation_with_owner_boxed(root, gate, state, owner_name, owner, memory)? + .into_inner(); + Ok((data.observed.into_inner(), data.recovery)) +} + +/// Off-permit source facts and the existing opaque recovery image. The hot +/// observe_files/snapshot/codec functions remain allocation-free and unchanged. +pub(super) struct PreparedSourceObservationData { + pub(super) observed: super::maintenance_budget::MemoryBox, + pub(super) recovery: Option>, +} +#[inline(never)] +fn source_snapshot_boxed( + root: &RootedDirectory, + gate: &File, + state: &File, + owner_name: &[u8], + owner: &File, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let observed = observe_source_snapshot(root, gate, state, owner_name, owner, true)?; + super::maintenance_budget::MemoryBox::new(observed, memory).map_err(FixedControlError::Resource) +} +#[inline(never)] +pub(super) fn prepare_source_observation_boxed( + root: &RootedDirectory, + gate: &File, + state: &File, + owner_name: &[u8], + memory: &CleanupMemory, +) -> Result, FixedControlError> +{ + let owner = root.open_readonly_file(owner_name)?; + prepare_source_observation_with_owner_boxed(root, gate, state, owner_name, &owner, memory) +} +pub(super) fn prepare_source_observation_with_owner_boxed( + root: &RootedDirectory, + gate: &File, + state: &File, + owner_name: &[u8], + owner: &File, + memory: &CleanupMemory, +) -> Result, FixedControlError> +{ + let floor = memory + .reserve(std::mem::size_of::() * 2 + 16384) + .map_err(FixedControlError::Resource)?; + let observed = source_snapshot_boxed(root, gate, state, owner_name, owner, memory)?; + let recovery = if observed.pending { + Some(prepare_pending_source_recovery( + gate, state, owner, &observed, floor, memory, + )?) + } else { + None + }; + super::maintenance_budget::MemoryBox::new( + PreparedSourceObservationData { observed, recovery }, + memory, + ) + .map_err(FixedControlError::Resource) +} + +// Pending slot/commit images exist only after the snapshot stage returned; +// they cannot multiply the stack above immutable source decoding. +#[inline(never)] +fn prepare_pending_source_recovery( + gate: &File, + state: &File, + owner: &File, + before: &Observation, + floor: super::maintenance_budget::MemoryReservation, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let mut intent = [0; codec::INTENT_BYTES]; + locks::read_fixed_range(state, SELECTOR + codec::COMMIT_BYTES as u64, &mut intent)?; + let fact = codec::decode_intent(&intent, &before.binding.nonce) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let slot: [u8; codec::SLOT_BYTES] = intent[96..96 + codec::SLOT_BYTES].try_into().unwrap(); + let after = Record::decode(&slot[56..56 + RECORD_BYTES], fact.after)?; + let commit = codec::CommitFact { + generation: fact.after, + active: fact.target, + slot_digest: fact.slot_digest, + }; + let mut commit_image = [0; codec::COMMIT_BYTES]; + codec::encode_commit(&mut commit_image, &before.binding.nonce, commit) + .map_err(|_| invalid(Problem::InvalidRecord))?; + let mut gate_header = [0; locks::COORDINATION_HEADER_BYTES]; + let mut owner_header = [0; locks::COORDINATION_HEADER_BYTES]; + locks::read_fixed_range(gate, 0, &mut gate_header)?; + locks::read_fixed_range(owner, 0, &mut owner_header)?; + before.state_stamp.recheck(state)?; + let prepared = super::maintenance_budget::MemoryBox::new( + PreparedSourceRecovery { + before: *before, + after, + commit, + slot, + commit_image, + gate_header, + owner_header, + _floor: floor, + }, + memory, + ) + .map_err(FixedControlError::Resource)?; + Ok(prepared) +} + +impl PreparedSourceRecovery { + pub(super) fn apply( + &self, + root: &RootedDirectory, + gate: &FixedLockedFile, + owner: &FixedLockedFile, + state: &File, + grant: &mut MutationGrant, + ) -> Result<(), FixedControlError> { + self.before.state_stamp.recheck(state)?; + let binding = self.before.binding; + let mut gate_header = [0; locks::COORDINATION_HEADER_BYTES]; + let mut owner_header = [0; locks::COORDINATION_HEADER_BYTES]; + locks::read_fixed_range(gate.file(), 0, &mut gate_header)?; + locks::read_fixed_range(owner.file(), 0, &mut owner_header)?; + if root.identity() != binding.root + || gate.mode() != LockMode::Exclusive + || owner.mode() != LockMode::Exclusive + || FixedFileStamp::capture(gate.file(), Kind::File)?.identity() != binding.gate + || FixedFileStamp::capture(owner.file(), Kind::File)?.identity() != binding.owner + || gate_header != self.gate_header + || owner_header != self.owner_header + { + return Err(invalid(Problem::Changed)); + } + write_range( + state, + if self.commit.active == 0 { + SLOT0 + } else { + SLOT1 + }, + &self.slot, + grant, + )?; + write_range(state, SELECTOR, &self.commit_image, grant)?; + write_range( + state, + SELECTOR + codec::COMMIT_BYTES as u64, + &[0; codec::INTENT_BYTES], + grant, + )?; + Ok(()) + } +} + +fn recognized_source_binding( + root: &RootedDirectory, + gate: &File, + state: &File, + owner_name: &[u8], +) -> bool { + let check = || -> Result { + let mut bytes = [0; BINDING_BYTES]; + locks::read_fixed_range(state, 0, &mut bytes)?; + let binding = Binding::decode(&bytes)?; + // Recognition only vetoes primary access on uncertain publication; + // source facts and effects still require the validated owner header. + let owner = root.open_metadata_file(owner_name)?; + Ok(binding.root == root.identity() + && binding.gate == FixedFileStamp::capture(gate, Kind::File)?.identity() + && binding.state == FixedFileStamp::capture(state, Kind::File)?.identity() + && binding.owner == FixedFileStamp::capture(&owner, Kind::File)?.identity() + && locks::read_coordination_incarnation(gate).ok().flatten() == Some(binding.nonce)) + }; + check().unwrap_or(false) +} + +#[cfg(test)] +mod source_observation_layout_tests { + use super::*; + #[test] + fn prepared_source_observation_keeps_snapshot_and_recovery_images_boxed() { + // Measured fixed owner is48B on the qualified local compiler/target; + // larger protocol images must not be copied above hot snapshot reads. + assert!(std::mem::size_of::() <= 128); + let _: fn(PreparedSourceObservationData) = |data| { + let _: super::super::maintenance_budget::MemoryBox = data.observed; + let _: Option> = + data.recovery; + }; + } +} diff --git a/src/storage/maintenance_legacy_source_recovery_tests.rs b/src/storage/maintenance_legacy_source_recovery_tests.rs new file mode 100644 index 00000000..99f91041 --- /dev/null +++ b/src/storage/maintenance_legacy_source_recovery_tests.rs @@ -0,0 +1,208 @@ +//! Real mailbox/dispatcher regression for a durable source intent cut. +use super::super::maintenance_budget::MemoryBox; +use super::super::maintenance_scheduler::{AttemptOutcome, TargetBinding}; +use super::*; +use std::cell::RefCell; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +#[test] +fn dc_legacy_staging_owned_intent_recovers_without_another_confirmation() { + recover_after_cut(2); +} +#[test] +fn dc_legacy_staging_owned_slot_recovers_without_another_confirmation() { + recover_after_cut(3); +} +#[test] +fn dc_legacy_staging_owned_commit_recovers_without_another_confirmation() { + recover_after_cut(4); +} +fn recover_after_cut(allowance: usize) { + let root = std::env::temp_dir().join(format!( + "legacy-source-intent-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + let state = root.join(".codebaseGraph"); + std::fs::create_dir_all(state.join("storage/artifacts")).unwrap(); + std::fs::create_dir(state.join("native-staging")).unwrap(); + std::fs::create_dir(state.join("artifacts")).unwrap(); + std::fs::write(state.join("config.json"), br#"{"schema_version":3}"#).unwrap(); + let root = std::fs::canonicalize(root).unwrap(); + let target = MaintenanceTarget { + repository_root: root.clone(), + state_root: root.join(".codebaseGraph"), + storage_root: None, + direct_pair: None, + config_path: None, + }; + let request_target = target.clone(); + struct Work { + scope: MemoryBox, + owner: Option, + memory: CleanupMemory, + cut: bool, + recovering: bool, + done: bool, + } + let verified = Arc::new(AtomicBool::new(false)); + let observed = verified.clone(); + let mut retained_binding = None; + let snapshot = super::super::maintenance_scheduler::with_claimed_legacy_confirmation( + Some(&request_target), + Arc::new(crate::coordinator::ExecutionDispatcher::default()), + move |memory, request, parked: Option>>| { + let resumed = parked.is_some(); + let work = if let Some(work) = parked { + work + } else { + let binding = retained_binding.get_or_insert_with(|| { + MemoryBox::new( + super::super::maintenance_admission::prepare_target_binding( + &target, memory, + ) + .unwrap(), + memory, + ) + .unwrap() + }); + let scope = prepare_legacy_staging_scope( + &target, + binding, + &target.state_root.join("native-staging"), + memory, + ) + .unwrap(); + MemoryBox::new( + RefCell::new(Work { + scope: MemoryBox::new(scope, memory).unwrap(), + owner: None, + memory: memory.clone(), + cut: false, + recovering: false, + done: false, + }), + memory, + ) + .unwrap() + }; + let binding; + { + let mut work = work.borrow_mut(); + let Work { + scope, + owner, + memory, + cut, + recovering, + done, + } = &mut *work; + binding = TargetBinding(scope.binding.digest()); + if *cut { + if owner.as_ref().unwrap().closed_version().phase() == LegacyPhase::Active { + assert!( + super::super::maintenance_legacy_guard::before_staging_producer( + &scope.staging + ) + .unwrap() + .is_some() + ); + observed.store(true, Ordering::Release); + *done = true; + } else { + let state = owner.as_ref().unwrap().state_pin.try_clone().unwrap(); + let mut wire = [0; codec::INTENT_BYTES]; + locks::read_fixed_range( + &state, + SELECTOR + codec::COMMIT_BYTES as u64, + &mut wire, + ) + .unwrap(); + assert!(codec::decode_intent( + &wire, + &owner.as_ref().unwrap().binding.nonce + ) + .is_ok()); + + *recovering = scope + .refresh_publication_recovery(memory, owner.as_ref()) + .unwrap(); + assert!(*recovering, "durable own intent must be recognized"); + } + } else if let Some(owner) = owner.as_ref() { + scope + .prepare_transition(owner, LegacyPhase::Active, memory) + .unwrap(); + } else if resumed { + scope.refresh_enrollment_preparation(memory).unwrap(); + } else if request.is_some() { + scope.prepare_enrollment_metadata(request, memory).unwrap(); + } + } + Ok((binding, work)) + }, + move |work, confirmation, grant| { + let mut work = work.borrow_mut(); + let Work { + scope, + owner, + memory, + cut, + recovering, + done, + } = &mut *work; + if *done { + return Ok(AttemptOutcome::Complete); + } + if *recovering { + assert!( + confirmation.is_none(), + "retry must use accepted job, not a new TRUE" + ); + scope + .recover_publication(owner.as_mut(), grant, memory) + .unwrap(); + return Ok(AttemptOutcome::Pending); + } + if let Some(owner) = owner.as_mut() { + assert!(confirmation.is_none()); + // One entry opens the actual exclusive staging gate; one fsync + // makes each real publication write durable. Exhaustion cuts + // after the selected intent, slot, or commit sync. + grant + .take_entries(grant.remaining_entries() - allowance) + .unwrap(); + let result = abort_legacy_staging_preparation(scope, owner, grant, memory); + assert!(matches!( + result, + Err(FixedControlError::Grant( + super::super::maintenance_budget::GrantExhausted::Entries + )) + )); + *cut = true; + return Ok(AttemptOutcome::Pending); + } + match with_legacy_staging_enrollment(scope, confirmation, grant, memory, &mut |_, _| { + Ok(()) + }) { + Ok((_, actual)) => *owner = Some(actual), + Err(FixedControlError::Invalid(Problem::Busy) | FixedControlError::Grant(_)) + if scope.enrollment_pending() => {} + Err(error) => panic!("enrollment: {error:?}"), + } + Ok(AttemptOutcome::Pending) + }, + ); + assert!(verified.load(Ordering::Acquire), "{snapshot:?}"); + assert_eq!( + snapshot.last_attempt.unwrap().outcome, + AttemptOutcome::Complete + ); + std::fs::remove_dir_all(root).unwrap(); +} diff --git a/src/storage/maintenance_legacy_staging.rs b/src/storage/maintenance_legacy_staging.rs new file mode 100644 index 00000000..be4a8397 --- /dev/null +++ b/src/storage/maintenance_legacy_staging.rs @@ -0,0 +1,3074 @@ +//! Bounded recognition of historical COPY/JSON staging without a DB/manifest. +//! Row/schema/source facts never authorize mutation or legacy enrollment. +use super::locks::{self, FileIdentity, FixedControlKind, FixedFileStamp, RootedDirectory}; +use super::maintenance_budget::{CleanupMemory, ResourceLimit}; +use super::maintenance_json::{JsonFailure, JsonToken, MaintenanceJsonReader, TokenKind}; +use super::maintenance_validation::{ + self as validation, ByteSource, KeyEncoding, NumberKind, Span, ValidationIssue, +}; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::io::{self, Read}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum StagingIssue { + Resource(ResourceLimit), + Io(io::ErrorKind), + Unknown, + Changed, + Cancelled, +} +impl From for StagingIssue { + fn from(e: ResourceLimit) -> Self { + Self::Resource(e) + } +} +impl From for StagingIssue { + fn from(e: io::Error) -> Self { + Self::Io(e.kind()) + } +} +impl From for StagingIssue { + fn from(e: JsonFailure) -> Self { + match e { + JsonFailure::Resource(e) => Self::Resource(e), + JsonFailure::Io(e) => Self::Io(e), + _ => Self::Unknown, + } + } +} +impl From for StagingIssue { + fn from(e: ValidationIssue) -> Self { + match e { + ValidationIssue::Resource(e) => Self::Resource(e), + ValidationIssue::Io(e) => Self::Io(e), + _ => Self::Unknown, + } + } +} +fn file_stamp(file: &File) -> Result { + FixedFileStamp::capture(file, FixedControlKind::File).map_err(|_| StagingIssue::Changed) +} +fn recheck(stamp: FixedFileStamp, file: &File) -> Result<(), StagingIssue> { + stamp.recheck(file).map_err(|_| StagingIssue::Changed) +} +fn extend_chain(chain: &mut [u8; 32], bytes: &[u8]) { + let mut hash = Sha256::new(); + hash.update(b"CGLegacyStagingRowChain01"); + hash.update(*chain); + hash.update(bytes); + *chain = hash.finalize().into(); +} +fn hash(bytes: &[u8]) -> [u8; 32] { + Sha256::digest(bytes).into() +} +fn put64(out: &mut [u8], value: u64) { + out[..8].copy_from_slice(&value.to_le_bytes()); +} +fn get64(bytes: &[u8]) -> u64 { + u64::from_le_bytes(bytes[..8].try_into().unwrap()) +} +fn put_id(out: &mut [u8], id: FileIdentity) { + let (a, b) = id.fixed_words(); + put64(out, a); + put64(&mut out[8..], b); +} +fn get_id(bytes: &[u8]) -> FileIdentity { + FileIdentity::from_fixed_words(get64(bytes), get64(&bytes[8..])) +} +fn finish(out: &mut [u8]) { + let at = out.len() - 32; + let digest = hash(&out[..at]); + out[at..].copy_from_slice(&digest); +} +fn checked(bytes: &[u8]) -> bool { + bytes.len() >= 32 && hash(&bytes[..bytes.len() - 32]) == bytes[bytes.len() - 32..] +} + +/// Pinned producer table spellings. Classification selects a parser only; +/// recognized origin additionally requires full bundle/source association. +const NODES: [&str; 37] = [ + "APIEndpoint", + "Assignment", + "CallExpression", + "Class", + "ClassAttribute", + "Component", + "Constant", + "ControlFlowBlock", + "Decorator", + "Dependency", + "DocumentationChunk", + "DocumentationSource", + "ExceptionFlow", + "ExportDeclaration", + "Expression", + "File", + "Function", + "ImportDeclaration", + "InstanceAttribute", + "Literal", + "Method", + "Module", + "Parameter", + "Property", + "Query", + "Reference", + "Repository", + "ReturnType", + "Route", + "Scope", + "SecretRef", + "SourceRoot", + "Symbol", + "SyntaxCapture", + "TypeAlias", + "TypeAnnotation", + "Variable", +]; +const EDGES: [&str; 25] = [ + "Assigns", + "Calls", + "Contains", + "Declares", + "DecoratedBy", + "Defines", + "DependsOn", + "DerivedFrom", + "Documents", + "EvidencedBy", + "ExecutesQuery", + "Exports", + "Exposes", + "Handles", + "HasParameter", + "HasReturnType", + "HasScope", + "HasTypeAnnotation", + "Imports", + "Raises", + "References", + "ResolvesTo", + "RoutesTo", + "SyntaxChild", + "UsesSecret", +]; +fn table_name(index: u8) -> Option<&'static str> { + if index < 37 { + NODES.get(index as usize).copied() + } else { + EDGES.get(usize::from(index) - 37).copied() + } +} +fn stem_equal(raw: &[u8], table: &str) -> bool { + raw.len() == table.len() + && raw + .iter() + .zip(table.bytes()) + .all(|(a, b)| *a == b.to_ascii_lowercase()) +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum StagingPayloadKind { + Node, + Edge, + Connector, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct StagingProfile { + pub(crate) kind: StagingPayloadKind, + pub(crate) table: u8, + pub(crate) from: u8, + pub(crate) to: u8, + pub(crate) chunk: u64, +} +/// Exact flat producer filenames/chunking. Unknown names remain protected. +pub(crate) fn staging_profile(name: &[u8]) -> Option { + if name.is_empty() + || name.len() > 255 + || name + .iter() + .any(|b| !b.is_ascii_alphanumeric() && !matches!(b, b'_' | b'.')) + { + return None; + } + let (mut stem, csv) = if let Some(s) = name.strip_suffix(b".json") { + (s, false) + } else { + let stem = name.strip_suffix(b".csv")?; + (stem, true) + }; + let mut chunk = 0u64; + if let Some(at) = stem.windows(2).rposition(|s| s == b"__") { + let tail = &stem[at + 2..]; + if tail.len() >= 6 && tail.iter().all(u8::is_ascii_digit) { + if tail.len() > 6 && tail[0] == b'0' { + return None; + } + for b in tail { + chunk = chunk + .checked_mul(10u64)? + .checked_add(u64::from(*b - b'0'))?; + } + if chunk == 0 { + return None; + } + stem = &stem[..at]; + } + } + if !csv { + for index in 0..62 { + let table = table_name(index)?; + if stem_equal(stem, table) { + return Some(StagingProfile { + kind: if index < 37 { + StagingPayloadKind::Node + } else { + StagingPayloadKind::Edge + }, + table: index, + from: 0, + to: 0, + chunk, + }); + } + } + return None; + } + for (edge, relation) in EDGES.iter().enumerate() { + for (side, prefix) in [(0, b"from_".as_slice()), (1, b"to_".as_slice())] { + let Some(rest) = stem.strip_prefix(prefix) else { + continue; + }; + let relation_len = relation.len(); + if rest.len() <= relation_len + 2 + || !stem_equal(&rest[..relation_len], relation) + || &rest[relation_len..relation_len + 2] != b"__" + { + continue; + } + let endpoints = &rest[relation_len + 2..]; + let at = endpoints.windows(2).position(|v| v == b"__")?; + let (a, b) = (&endpoints[..at], &endpoints[at + 2..]); + for from in 0..62 { + let from_name = table_name(from)?; + if !stem_equal(a, from_name) { + continue; + } + for to in 0..62 { + if stem_equal(b, table_name(to)?) { + return Some(StagingProfile { + kind: StagingPayloadKind::Connector, + table: (62 + side * 25 + edge) as u8, + from, + to, + chunk, + }); + } + } + } + } + } + None +} + +struct Source<'a> { + file: &'a File, + len: u64, +} +impl ByteSource for Source<'_> { + fn len(&self) -> u64 { + self.len + } + fn read_at(&self, offset: u64, out: &mut [u8]) -> Result { + if offset > self.len { + return Err(ValidationIssue::InvalidSpan); + } + let n = usize::try_from((self.len - offset).min(out.len() as u64)) + .map_err(|_| ValidationIssue::Overflow)?; + #[cfg(unix)] + { + use std::os::unix::fs::FileExt; + self.file + .read_at(&mut out[..n], offset) + .map_err(|e| ValidationIssue::Io(e.kind())) + } + #[cfg(windows)] + { + use std::os::windows::fs::FileExt; + self.file + .seek_read(&mut out[..n], offset) + .map_err(|e| ValidationIssue::Io(e.kind())) + } + #[cfg(not(any(unix, windows)))] + { + let _ = (offset, out, n); + Err(ValidationIssue::Io(io::ErrorKind::Unsupported)) + } + } +} +struct Sequential<'a> { + source: &'a Source<'a>, + offset: u64, + prefix_array: bool, + cancel: &'a dyn Fn() -> bool, +} +impl Read for Sequential<'_> { + fn read(&mut self, out: &mut [u8]) -> io::Result { + if (self.cancel)() { + return Err(io::ErrorKind::Interrupted.into()); + } + if self.prefix_array && !out.is_empty() { + out[0] = b'['; + self.prefix_array = false; + return Ok(1); + } + let n = self + .source + .read_at(self.offset, out) + .map_err(|_| io::Error::from(io::ErrorKind::InvalidData))?; + self.offset = self + .offset + .checked_add(n as u64) + .ok_or(io::ErrorKind::InvalidData)?; + Ok(n) + } +} + +pub(crate) const STAGING_ROW_BYTES: usize = 768; +pub(crate) const STAGING_PAYLOAD_BYTES: usize = 768; +/// Self-contained locator: no borrowed file cursor or unbounded FD registry. +/// These rows are not replayable until their complete payload certificate and +/// matching witness-chain digest have been durably committed by the caller. +#[derive(Clone, Copy)] +pub(crate) struct StagingRowWitness { + pub(crate) profile: StagingProfile, + pub(crate) parent: FileIdentity, + pub(crate) name: [u8; 255], + pub(crate) name_len: u8, + pub(crate) stamp: FixedFileStamp, + pub(crate) ordinal: u64, + /// JSON: id,label,kind,path,canonical,source,target,content_hash. + /// CSV: from_id,to_id,role, then empty spans. CSV spans are raw ASCII. + pub(crate) fields: [Span; 8], +} +impl StagingRowWitness { + pub(crate) fn encode(&self, out: &mut [u8; STAGING_ROW_BYTES]) { + out.fill(0); + out[..8].copy_from_slice(b"CGLGSR01"); + out[8] = match self.profile.kind { + StagingPayloadKind::Node => 0, + StagingPayloadKind::Edge => 1, + StagingPayloadKind::Connector => 2, + }; + out[9] = self.profile.table; + out[10] = self.profile.from; + out[11] = self.profile.to; + out[12] = self.name_len; + put64(&mut out[16..], self.profile.chunk); + out[32..287].copy_from_slice(&self.name); + put_id(&mut out[288..304], self.parent); + self.stamp + .encode_checkpoint((&mut out[304..384]).try_into().unwrap()); + put64(&mut out[384..], self.ordinal); + for (i, s) in self.fields.iter().enumerate() { + put64(&mut out[400 + i * 16..], s.start); + put64(&mut out[408 + i * 16..], s.end); + } + finish(out); + } + pub(crate) fn decode(bytes: &[u8; STAGING_ROW_BYTES]) -> Result { + if &bytes[..8] != b"CGLGSR01" + || !checked(bytes) + || bytes[13..16] + .iter() + .chain(bytes[24..32].iter()) + .chain(bytes[287..288].iter()) + .chain(bytes[392..400].iter()) + .chain(bytes[528..736].iter()) + .any(|b| *b != 0) + { + return Err(StagingIssue::Unknown); + } + let len = usize::from(bytes[12]); + if len == 0 || bytes[32 + len..287].iter().any(|b| *b != 0) { + return Err(StagingIssue::Unknown); + } + let profile = staging_profile(&bytes[32..32 + len]).ok_or(StagingIssue::Unknown)?; + if bytes[8] + != match profile.kind { + StagingPayloadKind::Node => 0, + StagingPayloadKind::Edge => 1, + StagingPayloadKind::Connector => 2, + } + || bytes[9] != profile.table + || bytes[10] != profile.from + || bytes[11] != profile.to + || get64(&bytes[16..]) != profile.chunk + { + return Err(StagingIssue::Unknown); + } + let stamp = FixedFileStamp::decode_checkpoint((&bytes[304..384]).try_into().unwrap()) + .map_err(|_| StagingIssue::Changed)?; + let mut fields = [Span::default(); 8]; + for (i, s) in fields.iter_mut().enumerate() { + s.start = get64(&bytes[400 + i * 16..]); + s.end = get64(&bytes[408 + i * 16..]); + if s.start > s.end || s.end > stamp.len() { + return Err(StagingIssue::Unknown); + } + } + Ok(Self { + profile, + parent: get_id(&bytes[288..304]), + name: bytes[32..287].try_into().unwrap(), + name_len: bytes[12], + stamp, + ordinal: get64(&bytes[384..]), + fields, + }) + } +} +#[derive(Clone, Copy)] +pub(crate) struct StagingPayloadCertificate { + row: StagingRowWitness, + pub(crate) digest: [u8; 32], + pub(crate) rows: u64, + pub(crate) witness_chain: [u8; 32], + pub(crate) source_context: [u8; 32], +} +impl StagingPayloadCertificate { + pub(crate) fn witness_address(&self) -> &StagingRowWitness { + &self.row + } + pub(crate) fn encode(&self, out: &mut [u8; STAGING_PAYLOAD_BYTES]) { + self.row.encode(out); + out[..8].copy_from_slice(b"CGLGSP01"); + out[400..528].fill(0); + out[400..432].copy_from_slice(&self.digest); + put64(&mut out[432..], self.rows); + out[440..472].copy_from_slice(&self.witness_chain); + out[472..504].copy_from_slice(&self.source_context); + finish(out); + } + pub(crate) fn decode(bytes: &[u8; STAGING_PAYLOAD_BYTES]) -> Result { + if &bytes[..8] != b"CGLGSP01" || !checked(bytes) || bytes[504..736].iter().any(|b| *b != 0) + { + return Err(StagingIssue::Unknown); + } + let mut row = *bytes; + row[..8].copy_from_slice(b"CGLGSR01"); + row[400..528].fill(0); + finish(&mut row); + let row = StagingRowWitness::decode(&row)?; + if row.ordinal != 0 { + return Err(StagingIssue::Unknown); + } + Ok(Self { + row, + digest: bytes[400..432].try_into().unwrap(), + rows: get64(&bytes[432..]), + witness_chain: bytes[440..472].try_into().unwrap(), + source_context: bytes[472..504].try_into().unwrap(), + }) + } + pub(crate) fn certificate_digest(&self) -> [u8; 32] { + let mut bytes = [0; STAGING_PAYLOAD_BYTES]; + self.encode(&mut bytes); + hash(&bytes) + } +} + +struct Scanner<'a> { + source: &'a Source<'a>, + reader: MaintenanceJsonReader>, + memory: &'a CleanupMemory, + cancel: &'a dyn Fn() -> bool, + adjustment: Option, +} +impl Scanner<'_> { + fn token(&mut self) -> Result { + if (self.cancel)() { + return Err(StagingIssue::Cancelled); + } + let mut token = self.reader.next_token()?.ok_or(StagingIssue::Unknown)?; + if let Some(base) = self.adjustment { + token.start = token.start.checked_add(base).ok_or(StagingIssue::Unknown)?; + token.end = token.end.checked_add(base).ok_or(StagingIssue::Unknown)?; + } + Ok(token) + } + fn string(&self, t: JsonToken) -> Result { + if matches!(t.kind, TokenKind::String { key: false, .. }) { + Ok(Span { + start: t.start, + end: t.end, + }) + } else { + Err(StagingIssue::Unknown) + } + } + fn field(&self, t: JsonToken, names: &[&[u8]]) -> Result { + if !matches!(t.kind, TokenKind::String { key: true, .. }) { + return Err(StagingIssue::Unknown); + } + let mut key = [0; 32]; + let n = validation::copy_decoded_key( + self.source, + Span { + start: t.start, + end: t.end, + }, + KeyEncoding::JsonDecoded, + &mut key, + self.memory, + )?; + names + .iter() + .position(|name| *name == &key[..n]) + .ok_or(StagingIssue::Unknown) + } + fn literal(&self, span: Span, expected: &[u8]) -> Result { + Ok(validation::compare_native_json( + expected, + Span { + start: 0, + end: expected.len() as u64, + }, + self.source, + span, + self.memory, + )? == std::cmp::Ordering::Equal) + } + fn number(&self, t: JsonToken, kind: NumberKind) -> Result<(), StagingIssue> { + if t.kind != TokenKind::Number || t.end < t.start || t.end - t.start > 64 { + return Err(StagingIssue::Unknown); + } + let mut bytes = [0; 64]; + let n = (t.end - t.start) as usize; + if self.source.read_at(t.start, &mut bytes[..n])? != n { + return Err(StagingIssue::Changed); + } + let facts = validation::number_facts(&bytes[..n], kind, self.memory)?; + if facts.typed && facts.stable { + Ok(()) + } else { + Err(StagingIssue::Unknown) + } + } + fn metadata(&mut self, first: JsonToken) -> Result { + if first.kind != TokenKind::ObjectStart { + return Err(StagingIssue::Unknown); + } + let mut prior = [None; 127]; + let mut depth = 1usize; + let mut canonical = None; + let mut canonical_next = false; + while depth > 0 { + let t = self.token()?; + match t.kind { + TokenKind::String { key: true, .. } => { + let span = Span { + start: t.start, + end: t.end, + }; + if let Some(p) = prior[depth - 1] { + if validation::compare_decoded_spans( + self.source, + p, + self.source, + span, + self.memory, + )? != std::cmp::Ordering::Less + { + return Err(StagingIssue::Unknown); + } + } + prior[depth - 1] = Some(span); + canonical_next = depth == 1 && self.literal(span, b"canonical_key")?; + } + TokenKind::ObjectStart | TokenKind::ArrayStart => { + if canonical_next || depth == prior.len() { + return Err(StagingIssue::Unknown); + } + prior[depth] = None; + depth += 1; + } + TokenKind::ObjectEnd | TokenKind::ArrayEnd => { + if canonical_next { + return Err(StagingIssue::Unknown); + } + depth -= 1; + } + TokenKind::Number => { + if canonical_next { + return Err(StagingIssue::Unknown); + } + self.number(t, NumberKind::Value)?; + } + TokenKind::String { key: false, .. } => { + if canonical_next { + if canonical.is_some() { + return Err(StagingIssue::Unknown); + } + canonical = Some(self.string(t)?); + canonical_next = false; + } + } + _ => { + if canonical_next { + return Err(StagingIssue::Unknown); + } + } + } + } + canonical.ok_or(StagingIssue::Unknown) + } + fn validate_id(&self, row: &StagingRowWitness) -> Result<(), StagingIssue> { + let canonical = row.fields[4]; + let sha = validation::key_sha1_prefix20( + self.source, + canonical, + KeyEncoding::JsonDecoded, + self.memory, + )?; + let prefix = if row.profile.kind == StagingPayloadKind::Edge { + b"edge".as_slice() + } else { + table_name(row.profile.table) + .ok_or(StagingIssue::Unknown)? + .as_bytes() + }; + let mut expected = [0; 64]; + if prefix.len() + 21 > expected.len() { + return Err(StagingIssue::Unknown); + } + expected[..prefix.len()].copy_from_slice(prefix); + expected[prefix.len()] = b':'; + for (i, b) in sha[..10].iter().enumerate() { + expected[prefix.len() + 1 + i * 2] = b"0123456789abcdef"[(b >> 4) as usize]; + expected[prefix.len() + 2 + i * 2] = b"0123456789abcdef"[(b & 15) as usize]; + } + if !self.literal(row.fields[0], &expected[..prefix.len() + 21])? { + return Err(StagingIssue::Unknown); + } + Ok(()) + } + fn row(&mut self, template: StagingRowWitness) -> Result { + const NODE_FIELDS: [&[u8]; 18] = [ + b"id", + b"label", + b"kind", + b"language", + b"grammar_version", + b"path", + b"qualified_name", + b"scope_id", + b"line_start", + b"line_end", + b"byte_start", + b"byte_end", + b"tree_sitter_node_type", + b"capture_name", + b"summary", + b"text", + b"metadata", + b"content_hash", + ]; + const EDGE_FIELDS: [&[u8]; 12] = [ + b"id", + b"kind", + b"source_id", + b"target_id", + b"confidence", + b"field_name", + b"child_index", + b"line_start", + b"line_end", + b"byte_start", + b"byte_end", + b"metadata", + ]; + let node = template.profile.kind == StagingPayloadKind::Node; + let names = if node { + NODE_FIELDS.as_slice() + } else { + EDGE_FIELDS.as_slice() + }; + let mut row = template; + let mut seen = 0u32; + loop { + let t = self.token()?; + if t.kind == TokenKind::ObjectEnd { + break; + } + let index = self.field(t, names)?; + let bit = 1u32 << index; + if seen & bit != 0 { + return Err(StagingIssue::Unknown); + } + seen |= bit; + let value = self.token()?; + if (node && index == 16) || (!node && index == 11) { + row.fields[4] = self.metadata(value)?; + continue; + } + let numeric = if node { + (8..=11).contains(&index) + } else { + index == 4 || (6..=10).contains(&index) + }; + if numeric { + if (node || index != 4) && value.kind == TokenKind::Null { + continue; + } + self.number( + value, + if !node && index == 4 { + NumberKind::F64 + } else { + NumberKind::I64 + }, + )?; + continue; + } + let span = self.string(value)?; + let projected = if node { + match index { + 0 => Some(0), + 1 => Some(1), + 2 => Some(2), + 5 => Some(3), + 17 => Some(7), + _ => None, + } + } else { + match index { + 0 => Some(0), + 1 => Some(2), + 2 => Some(5), + 3 => Some(6), + _ => None, + } + }; + if let Some(i) = projected { + row.fields[i] = span; + } + } + let required = if node { + ((1u32 << 18) - 1) & !(1 << 4) & !(1 << 17) + } else { + ((1u32 << 12) - 1) & !(1 << 5) & !(1 << 6) + }; + if seen & required != required { + return Err(StagingIssue::Unknown); + } + self.validate_id(&row)?; + if !node { + let mut from = [0; 64]; + let mut to = [0; 64]; + let mut kind = [0; 256]; + let from_len = validation::copy_decoded_key( + self.source, + row.fields[5], + KeyEncoding::JsonDecoded, + &mut from, + self.memory, + )?; + let to_len = validation::copy_decoded_key( + self.source, + row.fields[6], + KeyEncoding::JsonDecoded, + &mut to, + self.memory, + )?; + let kind_len = validation::copy_decoded_key( + self.source, + row.fields[2], + KeyEncoding::JsonDecoded, + &mut kind, + self.memory, + )?; + let table = table_name(row.profile.table) + .ok_or(StagingIssue::Unknown)? + .as_bytes(); + let mut canonical = [0; 512]; + let mut length = 0; + for part in [ + table, + b"|", + &from[..from_len], + b"|", + &to[..to_len], + b"|", + &kind[..kind_len], + ] { + canonical[length..length + part.len()].copy_from_slice(part); + length += part.len(); + } + if !self.literal(row.fields[4], &canonical[..length])? { + return Err(StagingIssue::Unknown); + } + } + if node && row.profile.table == 15 { + #[cfg(windows)] + const PATH_LIMIT: usize = 131072; + #[cfg(not(windows))] + const PATH_LIMIT: usize = 4096; + let mut path_bytes = self.memory.reserve(PATH_LIMIT)?.bytes(PATH_LIMIT)?; + let path_len = validation::copy_decoded_key( + self.source, + row.fields[3], + KeyEncoding::JsonDecoded, + path_bytes.as_mut_slice(), + self.memory, + )?; + let path = std::str::from_utf8(&path_bytes.as_slice()[..path_len]) + .map_err(|_| StagingIssue::Unknown)?; + if path.is_empty() + || path.as_bytes().iter().any(|b| matches!(b, 0 | b'\\')) + || path.split('/').any(|part| part.is_empty()) + || !std::path::Path::new(path) + .components() + .all(|part| matches!(part, std::path::Component::Normal(_))) + { + return Err(StagingIssue::Unknown); + } + if row.fields[7] == Span::default() + || validation::compare_decoded_spans( + self.source, + row.fields[3], + self.source, + row.fields[4], + self.memory, + )? != std::cmp::Ordering::Equal + { + return Err(StagingIssue::Unknown); + } + let mut content_hash = [0; 64]; + let n = validation::copy_decoded_key( + self.source, + row.fields[7], + KeyEncoding::JsonDecoded, + &mut content_hash, + self.memory, + )?; + if n != 64 + || content_hash + .iter() + .any(|b| !b.is_ascii_hexdigit() || b.is_ascii_uppercase()) + { + return Err(StagingIssue::Unknown); + } + } + Ok(row) + } +} + +/// The caller stages each fixed row record through its admitted private-stream +/// protocol. A callback success does not make rows committed: only the matching +/// final certificate permits replay. Cancellation/error leaves uncommitted data. +#[cfg(test)] +pub(crate) fn validate_staging_payload( + parent: &RootedDirectory, + name: &[u8], + source_context: [u8; 32], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + emit: &mut dyn FnMut(&StagingRowWitness) -> Result<(), StagingIssue>, +) -> Result { + if cancel() { + return Err(StagingIssue::Cancelled); + } + let profile = staging_profile(name).ok_or(StagingIssue::Unknown)?; + let _floor = memory.reserve( + std::mem::size_of::() * 5 + + std::mem::size_of::() * 3 + + std::mem::size_of::>() + + std::mem::size_of::<[Option; 127]>() + + STAGING_ROW_BYTES * 2 + + 16384, + )?; + let file = parent.open_readonly_file(name)?; + let before = file_stamp(&file)?; + let source = Source { + file: &file, + len: before.len(), + }; + let mut address = [0; 255]; + address[..name.len()].copy_from_slice(name); + let template = StagingRowWitness { + profile, + parent: parent.identity(), + name: address, + name_len: name.len() as u8, + stamp: before, + ordinal: 0, + fields: [Span::default(); 8], + }; + if profile.kind == StagingPayloadKind::Connector { + return validate_connector(&source, template, source_context, memory, cancel, emit); + } + let reader = MaintenanceJsonReader::new( + Sequential { + source: &source, + offset: 0, + prefix_array: false, + cancel, + }, + memory, + )?; + let mut scan = Scanner { + source: &source, + reader, + memory, + cancel, + adjustment: None, + }; + if scan.token()?.kind != TokenKind::ArrayStart { + return Err(StagingIssue::Unknown); + } + let mut count = 0u64; + let mut chain = [0u8; 32]; + let mut previous = None; + loop { + let t = scan.token()?; + if t.kind == TokenKind::ArrayEnd { + break; + } + if t.kind != TokenKind::ObjectStart { + return Err(StagingIssue::Unknown); + } + let mut row = template; + row.ordinal = count; + let row = scan.row(row)?; + if let Some(prior) = previous { + if validation::compare_decoded_spans(&source, prior, &source, row.fields[0], memory)? + != std::cmp::Ordering::Less + { + return Err(StagingIssue::Unknown); + } + } + previous = Some(row.fields[0]); + let mut bytes = [0; STAGING_ROW_BYTES]; + row.encode(&mut bytes); + extend_chain(&mut chain, &bytes); + emit(&row)?; + count = count.checked_add(1).ok_or(StagingIssue::Unknown)?; + } + if scan.reader.next_token()?.is_some() { + return Err(StagingIssue::Unknown); + } + let (digest, _) = scan.reader.finish()?; + if count == 0 { + return Err(StagingIssue::Unknown); + } + recheck(before, &file)?; + if cancel() { + return Err(StagingIssue::Cancelled); + } + Ok(StagingPayloadCertificate { + row: template, + digest, + rows: count, + witness_chain: chain, + source_context, + }) +} + +struct Csv<'a> { + source: &'a Source<'a>, + bytes: [u8; 4096], + cursor: usize, + filled: usize, + loaded: u64, + offset: u64, + hash: Sha256, + cancel: &'a dyn Fn() -> bool, +} +impl Csv<'_> { + fn byte(&mut self) -> Result, StagingIssue> { + if self.cursor == self.filled { + if (self.cancel)() { + return Err(StagingIssue::Cancelled); + } + if self.loaded == self.source.len { + return Ok(None); + } + self.filled = self.source.read_at(self.loaded, &mut self.bytes)?; + if self.filled == 0 { + return Err(StagingIssue::Changed); + } + self.loaded = self + .loaded + .checked_add(self.filled as u64) + .ok_or(StagingIssue::Unknown)?; + self.hash.update(&self.bytes[..self.filled]); + self.cursor = 0; + } + let b = self.bytes[self.cursor]; + self.cursor += 1; + self.offset = self.offset.checked_add(1).ok_or(StagingIssue::Unknown)?; + Ok(Some(b)) + } +} +fn id_literal(bytes: &[u8], table: u8) -> bool { + let Some(table) = table_name(table) else { + return false; + }; + let prefix = if EDGES.contains(&table) { + b"edge".as_slice() + } else { + table.as_bytes() + }; + bytes.len() == prefix.len() + 21 + && bytes.starts_with(prefix) + && bytes[prefix.len()] == b':' + && bytes[prefix.len() + 1..] + .iter() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(b)) +} +#[cfg(test)] +fn validate_connector( + source: &Source<'_>, + template: StagingRowWitness, + source_context: [u8; 32], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + emit: &mut dyn FnMut(&StagingRowWitness) -> Result<(), StagingIssue>, +) -> Result { + let _floor = memory.reserve( + std::mem::size_of::>() + + 4096 + + 192 * 2 + + STAGING_ROW_BYTES + + std::mem::size_of::() * 2, + )?; + let p = template.profile; + if !super::maintenance_legacy_association::staging_endpoint_allowed(p.table, p.from, p.to) { + return Err(StagingIssue::Unknown); + } + let target = p.table >= 87; + let relation = if target { + p.table.checked_sub(87) + } else { + p.table.checked_sub(62) + } + .filter(|index| *index < 25) + .ok_or(StagingIssue::Unknown)?; + if (!target && (p.from >= 37 || p.to != 37 + relation)) + || (target && (p.from != 37 + relation || p.to >= 37)) + { + return Err(StagingIssue::Unknown); + } + let mut csv = Csv { + source, + bytes: [0; 4096], + cursor: 0, + filled: 0, + loaded: 0, + offset: 0, + hash: Sha256::new(), + cancel, + }; + for expected in b"from_id,to_id,role\r\n" { + if csv.byte()? != Some(*expected) { + return Err(StagingIssue::Unknown); + } + } + let mut count = 0u64; + let mut chain = [0u8; 32]; + let mut previous = [0; 192]; + let mut previous_len = 0usize; + loop { + let start = csv.offset; + let Some(first) = csv.byte()? else { break }; + let mut line = [0; 192]; + line[0] = first; + let mut n = 1; + loop { + let b = csv.byte()?.ok_or(StagingIssue::Unknown)?; + if n == line.len() { + return Err(StagingIssue::Unknown); + } + line[n] = b; + n += 1; + if b == b'\n' { + break; + } + } + if n < 2 + || line[n - 2] != b'\r' + || (previous_len != 0 && previous[..previous_len] >= line[..n]) + { + return Err(StagingIssue::Unknown); + } + let body = &line[..n - 2]; + let a = body + .iter() + .position(|b| *b == b',') + .ok_or(StagingIssue::Unknown)?; + let b = a + + 1 + + body[a + 1..] + .iter() + .position(|b| *b == b',') + .ok_or(StagingIssue::Unknown)?; + if !id_literal(&body[..a], p.from) + || !id_literal(&body[a + 1..b], p.to) + || &body[b + 1..] + != if target { + b"target".as_slice() + } else { + b"source" + } + { + return Err(StagingIssue::Unknown); + } + let mut row = template; + row.ordinal = count; + row.fields[0] = Span { + start, + end: start + a as u64, + }; + row.fields[1] = Span { + start: start + a as u64 + 1, + end: start + b as u64, + }; + row.fields[2] = Span { + start: start + b as u64 + 1, + end: start + (n - 2) as u64, + }; + let mut bytes = [0; STAGING_ROW_BYTES]; + row.encode(&mut bytes); + extend_chain(&mut chain, &bytes); + emit(&row)?; + previous[..n].copy_from_slice(&line[..n]); + previous_len = n; + count = count.checked_add(1).ok_or(StagingIssue::Unknown)?; + } + if count == 0 { + return Err(StagingIssue::Unknown); + } + recheck(template.stamp, source.file)?; + if cancel() { + return Err(StagingIssue::Cancelled); + } + Ok(StagingPayloadCertificate { + row: template, + digest: csv.hash.finalize().into(), + rows: count, + witness_chain: chain, + source_context, + }) +} + +/// Real program payload fixture only. This copies actual successful public +/// producer COPY/JSON outputs; no receipt, authority, DB or manifest is made up. +#[cfg(test)] +pub(crate) struct CopiedOrphanStagingFixture { + pub(crate) directory: std::path::PathBuf, + pub(crate) payloads: Vec, +} +#[cfg(test)] +pub(crate) fn copy_public_orphan_staging_fixture( + repository: &std::path::Path, + storage: &std::path::Path, +) -> Result { + let runs = std::fs::read_dir(storage.join("runs")).map_err(|e| e.to_string())?; + let staging = runs + .filter_map(Result::ok) + .map(|entry| entry.path().join("staging")) + .find(|path| { + path.join("repository.json").is_file() + && path.join("sourceroot.json").is_file() + && path.join("file.json").is_file() + }) + .ok_or("actual public producer COPY fixture was not retained")?; + let directory = repository.join(".codebaseGraph/native-staging"); + std::fs::create_dir(&directory).map_err(|e| e.to_string())?; + let mut payloads = Vec::new(); + for entry in std::fs::read_dir(staging).map_err(|e| e.to_string())? { + let entry = entry.map_err(|e| e.to_string())?; + let name = entry.file_name(); + let Some(name) = name.to_str() else { continue }; + if staging_profile(name.as_bytes()).is_some() { + let target = directory.join(name); + std::fs::copy(entry.path(), &target).map_err(|e| e.to_string())?; + payloads.push(target); + } + } + payloads.sort(); + if payloads.is_empty() { + return Err("actual public COPY bundle was empty".into()); + } + if directory.join("graph.ldb").exists() + || directory.join("manifest.json").exists() + || directory.join(".origin-registry").exists() + { + return Err("fixture unexpectedly contains DB/manifest/modern registry".into()); + } + Ok(CopiedOrphanStagingFixture { + directory, + payloads, + }) +} + +/// Tiny real producer payload for transport-only tests. Invoke before capturing +/// target/source bindings: the fallback public producer legitimately advances E. +/// This single row file is intentionally NOT a complete association/GC proof. +#[cfg(test)] +/// Noncreating fixture observation of the actual administrative source record; +/// it grants no authority and does not acquire an owner or consume confirmation. +#[cfg(test)] +pub(crate) struct LegacyStagingJobObservation { + pub(crate) member: FileIdentity, + pub(crate) version: u64, + pub(crate) job: [u8; 32], +} +#[cfg(test)] +pub(crate) fn observe_accepted_staging_job( + repository: &std::path::Path, +) -> Result, String> { + use super::maintenance_legacy_guard as guard; + let path = repository.join(".codebaseGraph"); + let root = RootedDirectory::open_readonly_root( + &path, + locks::path_identity(&path).map_err(|e| e.to_string())?, + ) + .map_err(|e| e.to_string())?; + let open = |name: &[u8]| -> Result, String> { + match root.open_readonly_file(name) { + Ok(file) => Ok(Some(file)), + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None), + Err(e) => Err(e.to_string()), + } + }; + let (Some(gate), Some(state)) = (open(guard::STAGING_GATE)?, open(guard::STAGING_STATE)?) + else { + return Ok(None); + }; + let observed = guard::observe_files_named(&root, &gate, &state, guard::STAGING_OWNER) + .map_err(|e| format!("actual staging source observation: {e:?}"))?; + if observed.binding.kind != guard::LegacyScopeKind::NativeStaging + || observed.binding.repository + != locks::path_identity(repository).map_err(|e| e.to_string())? + || observed.record.database.is_some() + || observed.record.manifest.is_some() + { + return Err("staging source binding differs from actual fixture".into()); + } + if !matches!( + observed.record.phase, + guard::LegacyPhase::Preparing | guard::LegacyPhase::Retiring | guard::LegacyPhase::Removed + ) || observed.record.job == [0; 32] + { + return Ok(None); + } + Ok(Some(LegacyStagingJobObservation { + member: observed.record.member, + version: observed.record.version, + job: observed.record.job, + })) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::api::{execute_materialization, MaterializeOptions}; + struct FixtureReplay(Vec); + impl ReplayStagingCertificates for FixtureReplay { + fn replay( + &self, + emit: &mut dyn FnMut(StagingReplayEvent) -> Result<(), StagingIssue>, + ) -> Result<(), StagingIssue> { + for event in &self.0 { + emit(*event)?; + } + Ok(()) + } + } + #[test] + fn dc_staging_actual_public_copy_json_csv_profiles_and_streamed_certificates() { + let base = std::env::temp_dir().join(format!( + "dc-staging-actual-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + std::fs::create_dir_all(base.join("src")).unwrap(); + let base = std::fs::canonicalize(base).unwrap(); + std::fs::write(base.join("src/lib.rs"), "pub fn alpha(x:i64)->i64{x+1}\n").unwrap(); + let storage = base.join(".codebaseGraph/storage"); + let options = MaterializeOptions { + source_root: Some(base.clone()), + storage_root: Some(storage.clone()), + mode: "full".into(), + parallel: false, + use_git: false, + ..MaterializeOptions::default() + }; + execute_materialization(&options).unwrap(); + let copied = copy_public_orphan_staging_fixture(&base, &storage).unwrap(); + let legacy = copied.directory; + let count = copied.payloads.len() as u64; + assert!(count > 10); + assert!(!legacy.join("graph.ldb").exists()); + assert!(!legacy.join("manifest.json").exists()); + let parent = + RootedDirectory::open_readonly_root(&legacy, locks::path_identity(&legacy).unwrap()) + .unwrap(); + let memory = CleanupMemory::new(16 * 1024 * 1024).unwrap(); + let baseline = memory.used(); + let mut validated = 0; + let mut rows = 0; + let mut entries: Vec<_> = std::fs::read_dir(&legacy) + .unwrap() + .map(|e| e.unwrap().file_name().to_str().unwrap().to_owned()) + .collect(); + entries.sort(); + // Fixture-only independent oracle storage, never a production witness registry. + let mut replay = FixtureReplay(Vec::new()); + for name in &entries { + let mut chain = [0u8; 32]; + let mut emitted = 0; + let cert = validate_staging_payload( + &parent, + name.as_bytes(), + [7; 32], + &memory, + &|| false, + &mut |row| { + let mut bytes = [0; STAGING_ROW_BYTES]; + row.encode(&mut bytes); + assert!(StagingRowWitness::decode(&bytes).is_ok()); + extend_chain(&mut chain, &bytes); + emitted += 1; + Ok(()) + }, + ) + .unwrap_or_else(|e| panic!("actual producer profile {name}: {e:?}")); + assert_eq!(cert.rows, emitted); + assert_eq!(cert.witness_chain, chain); + let mut bytes = [0; STAGING_PAYLOAD_BYTES]; + cert.encode(&mut bytes); + assert_eq!( + StagingPayloadCertificate::decode(&bytes) + .unwrap() + .certificate_digest(), + cert.certificate_digest() + ); + let mut projection = + StagingProjection::new(&parent, name.as_bytes(), [7; 32], &memory).unwrap(); + let mut projected_rows = Vec::new(); + let mut final_cert = None; + loop { + let Some(batch) = projection.advance(&parent, &memory, &|| false).unwrap() else { + break; + }; + assert!( + projection.advance(&parent, &memory, &|| false).is_err(), + "unacknowledged segment cannot advance" + ); + for row in batch.rows.iter().flatten() { + projected_rows.push(*row); + } + if let Some(cert) = batch.certificate { + final_cert = Some(cert); + } + let mut checkpoint = [0; STAGING_CHECKPOINT_BYTES]; + batch.checkpoint.encode(&mut checkpoint); + let restored = StagingProjectionCheckpoint::decode(&checkpoint).unwrap(); + assert_eq!(restored.digest(), batch.checkpoint.digest()); + std::fs::write(legacy.join(".fixture-progress"), checkpoint).unwrap(); + projection.acknowledge(restored.digest()).unwrap(); + drop(batch); + // Every acknowledged boundary survives a fresh job object; no + // prefix parse, payload-sized list, or sha2 layout is persisted. + drop(projection); + projection = + StagingProjection::restore(&parent, restored, [7; 32], &memory).unwrap(); + } + let projected = final_cert.unwrap(); + assert_eq!(projected.certificate_digest(), cert.certificate_digest()); + assert_eq!(projected_rows.len() as u64, cert.rows); + drop(projection); + replay.0.push(StagingReplayEvent::Payload(projected)); + replay + .0 + .extend(projected_rows.into_iter().map(StagingReplayEvent::Row)); + replay.0.push(StagingReplayEvent::EndPayload); + validated += 1; + rows += cert.rows; + } + assert_eq!(validated, count); + assert!(rows > 30); + assert_eq!(memory.used(), baseline); + let association = associate_staging_bundle( + &parent, + &base, + [7; 32], + &replay, + 512, + &memory, + &|| false, + &|| Ok(()), + ) + .expect("actual orphan COPY bundle full source/endpoint/support association"); + assert_eq!(association.payload_count(), count); + assert_eq!(association.row_count(), rows); + let mut proof = [0; 512]; + association.encode(&mut proof); + assert_eq!( + StagingAssociation::decode(&proof) + .unwrap() + .certificate_set_digest(), + association.certificate_set_digest() + ); + assert!( + associate_staging_bundle( + &parent, + base.parent().unwrap(), + [7; 32], + &replay, + 512, + &memory, + &|| false, + &|| Ok(()) + ) + .is_err(), + "same schemas with substituted repository source are not associated" + ); + let mut incomplete = FixtureReplay(replay.0.clone()); + let end = incomplete + .0 + .iter() + .position(|e| matches!(e, StagingReplayEvent::EndPayload)) + .unwrap(); + incomplete.0.drain(..=end); + assert!( + associate_staging_bundle( + &parent, + &base, + [7; 32], + &incomplete, + 512, + &memory, + &|| false, + &|| Ok(()) + ) + .is_err(), + "missing cross-file certificate cannot cover the real directory" + ); + assert_eq!(memory.used(), baseline); + let file = legacy.join("file.json"); + let original = std::fs::read(&file).unwrap(); + let mut row: serde_json::Value = serde_json::from_slice(&original).unwrap(); + row[0]["id"] = serde_json::Value::String("File:00000000000000000000".into()); + std::fs::write(&file, serde_json::to_vec(&row).unwrap()).unwrap(); + assert!(matches!( + validate_staging_payload( + &parent, + b"file.json", + [7; 32], + &memory, + &|| false, + &mut |_| Ok(()) + ), + Err(StagingIssue::Unknown) + )); + std::fs::write(&file, &original).unwrap(); + assert_eq!( + validate_staging_payload( + &parent, + b"file.json", + [7; 32], + &memory, + &|| true, + &mut |_| panic!("cancel must not emit") + ) + .err(), + Some(StagingIssue::Cancelled) + ); + let tiny = CleanupMemory::new(128).unwrap(); + assert!(matches!( + validate_staging_payload( + &parent, + b"file.json", + [7; 32], + &tiny, + &|| false, + &mut |_| panic!("cap before IO/emit") + ), + Err(StagingIssue::Resource(_)) + )); + std::fs::remove_dir_all(base).unwrap(); + } + #[test] + fn dc_staging_filenames_select_only_exact_known_profiles() { + assert!(staging_profile(b"file.json").is_some()); + assert_eq!(staging_profile(b"file__000001.json").unwrap().chunk, 1); + assert!(staging_profile(b"file__000000.json").is_none()); + assert!(staging_profile(b"../file.json").is_none()); + assert!(staging_profile(b"foreign.json").is_none()); + assert!(staging_profile(b"from_contains__repository__contains.csv").is_some()); + assert!(staging_profile(b"to_contains__contains__file__000001.csv").is_some()); + } +} + +#[derive(Clone, Copy)] +pub(crate) enum StagingReplayEvent { + Payload(StagingPayloadCertificate), + Row(StagingRowWitness), + EndPayload, +} +/// Replay ONLY complete durably committed payload segments. A segment starts +/// with its final fixed certificate, followed by its exact row records. A +/// partially appended segment is never a Payload event. +pub(crate) trait ReplayStagingCertificates { + fn replay( + &self, + emit: &mut dyn FnMut(StagingReplayEvent) -> Result<(), StagingIssue>, + ) -> Result<(), StagingIssue>; +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct ReplaySummary { + payloads: u64, + rows: u64, + digest: [u8; 32], +} +fn same_address(a: &StagingRowWitness, b: &StagingRowWitness) -> bool { + a.parent == b.parent + && a.name_len == b.name_len + && a.name == b.name + && a.profile == b.profile + && a.stamp == b.stamp +} +fn replay_checked( + root: &RootedDirectory, + context: [u8; 32], + replay: &R, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + emit: &mut dyn FnMut(&StagingRowWitness) -> Result<(), StagingIssue>, +) -> Result { + let _floor = memory.reserve( + std::mem::size_of::() * 5 + + std::mem::size_of::() * 3 + + STAGING_ROW_BYTES * 2 + + 16384, + )?; + let mut current = None::; + let mut opened = None::; + let mut ordinal = 0u64; + let mut chain = [0u8; 32]; + let mut complete = Sha256::new(); + let mut summary = ReplaySummary { + payloads: 0, + rows: 0, + digest: [0; 32], + }; + let mut prior_name = [0; 255]; + let mut prior_len = 0usize; + let mut previous_row = None::; + let mut previous_profile = None::; + replay.replay(&mut |event| { + if cancel() { + return Err(StagingIssue::Cancelled); + } + match event { + StagingReplayEvent::Payload(cert) => { + if current.is_some() + || cert.rows == 0 + || cert.source_context != context + || cert.row.parent != root.identity() + || cert.row.ordinal != 0 + { + return Err(StagingIssue::Unknown); + } + let len = usize::from(cert.row.name_len); + let name = &cert.row.name[..len]; + if prior_len > 0 && prior_name[..prior_len] >= *name { + return Err(StagingIssue::Unknown); + } + let profile = staging_profile(name).ok_or(StagingIssue::Unknown)?; + if profile != cert.row.profile { + return Err(StagingIssue::Unknown); + } + if let Some(prior) = previous_profile { + let same = prior.kind == profile.kind + && prior.table == profile.table + && prior.from == profile.from + && prior.to == profile.to; + if (same && prior.chunk.checked_add(1) != Some(profile.chunk)) + || (!same && profile.chunk != 0) + { + return Err(StagingIssue::Unknown); + } + if !same { + previous_row = None; + } + } else if profile.chunk != 0 { + return Err(StagingIssue::Unknown); + } + let file = root.open_readonly_file(name)?; + recheck(cert.row.stamp, &file)?; + current = Some(cert); + opened = Some(file); + ordinal = 0; + chain = [0; 32]; + prior_name = cert.row.name; + prior_len = len; + previous_profile = Some(profile); + } + StagingReplayEvent::Row(row) => { + let cert = current.as_ref().ok_or(StagingIssue::Unknown)?; + if !same_address(&cert.row, &row) || row.ordinal != ordinal { + return Err(StagingIssue::Unknown); + } + let mut bytes = [0; STAGING_ROW_BYTES]; + row.encode(&mut bytes); + StagingRowWitness::decode(&bytes)?; + if let Some(prior) = previous_row { + if row.ordinal == 0 { + let left = + root.open_readonly_file(&prior.name[..usize::from(prior.name_len)])?; + recheck(prior.stamp, &left)?; + let left = Source { + file: &left, + len: prior.stamp.len(), + }; + let right = Source { + file: opened.as_ref().unwrap(), + len: row.stamp.len(), + }; + let cmp = if row.profile.kind == StagingPayloadKind::Connector { + compare_ascii(&left, prior.fields[0], &right, row.fields[0])?.then( + compare_ascii(&left, prior.fields[1], &right, row.fields[1])?, + ) + } else { + validation::compare_decoded_spans( + &left, + prior.fields[0], + &right, + row.fields[0], + memory, + )? + }; + if cmp != std::cmp::Ordering::Less { + return Err(StagingIssue::Unknown); + } + } + } + previous_row = Some(row); + extend_chain(&mut chain, &bytes); + emit(&row)?; + ordinal = ordinal.checked_add(1).ok_or(StagingIssue::Unknown)?; + } + StagingReplayEvent::EndPayload => { + let cert = current.take().ok_or(StagingIssue::Unknown)?; + if ordinal != cert.rows || chain != cert.witness_chain { + return Err(StagingIssue::Changed); + } + recheck(cert.row.stamp, opened.as_ref().unwrap())?; + let named = + root.open_readonly_file(&cert.row.name[..usize::from(cert.row.name_len)])?; + recheck(cert.row.stamp, &named)?; + opened = None; + complete.update(cert.certificate_digest()); + summary.payloads = summary + .payloads + .checked_add(1) + .ok_or(StagingIssue::Unknown)?; + summary.rows = summary + .rows + .checked_add(ordinal) + .ok_or(StagingIssue::Unknown)?; + } + } + Ok(()) + })?; + if current.is_some() { + return Err(StagingIssue::Unknown); + } + summary.digest = complete.finalize().into(); + Ok(summary) +} +fn compare_ascii( + left: &dyn ByteSource, + a: Span, + right: &dyn ByteSource, + b: Span, +) -> Result { + let mut order = std::cmp::Ordering::Equal; + let mut index = 0u64; + let mut x = [0; 128]; + let mut y = [0; 128]; + let alen = a.end.checked_sub(a.start).ok_or(StagingIssue::Unknown)?; + let blen = b.end.checked_sub(b.start).ok_or(StagingIssue::Unknown)?; + while index < alen.max(blen) { + let nx = usize::try_from((alen.saturating_sub(index)).min(128)).unwrap(); + let ny = usize::try_from((blen.saturating_sub(index)).min(128)).unwrap(); + if nx > 0 && left.read_at(a.start + index, &mut x[..nx])? != nx + || ny > 0 && right.read_at(b.start + index, &mut y[..ny])? != ny + { + return Err(StagingIssue::Changed); + } + if x[..nx].iter().chain(y[..ny].iter()).any(|b| !b.is_ascii()) { + return Err(StagingIssue::Unknown); + } + if order == std::cmp::Ordering::Equal { + order = x[..nx].cmp(&y[..ny]); + } + index = index.checked_add(128).ok_or(StagingIssue::Unknown)?; + } + Ok(order) +} + +fn validation_error(e: StagingIssue) -> ValidationIssue { + match e { + StagingIssue::Resource(e) => ValidationIssue::Resource(e), + StagingIssue::Io(e) => ValidationIssue::Io(e), + StagingIssue::Cancelled => ValidationIssue::Io(io::ErrorKind::Interrupted), + _ => ValidationIssue::Schema, + } +} +struct Slice<'a>(&'a [u8]); +impl ByteSource for Slice<'_> { + fn len(&self) -> u64 { + self.0.len() as u64 + } + fn read_at(&self, offset: u64, out: &mut [u8]) -> Result { + self.0.read_at(offset, out) + } +} +fn derived_id( + source: &dyn ByteSource, + span: Span, + encoding: KeyEncoding, + prefix: &[u8], + memory: &CleanupMemory, +) -> Result<([u8; 64], usize), ValidationIssue> { + let sha = validation::key_sha1_prefix20(source, span, encoding, memory)?; + if prefix.len() + 21 > 64 { + return Err(ValidationIssue::Schema); + } + let mut bytes = [0; 64]; + bytes[..prefix.len()].copy_from_slice(prefix); + bytes[prefix.len()] = b':'; + for (i, b) in sha[..10].iter().enumerate() { + bytes[prefix.len() + 1 + i * 2] = b"0123456789abcdef"[(b >> 4) as usize]; + bytes[prefix.len() + 2 + i * 2] = b"0123456789abcdef"[(b & 15) as usize]; + } + Ok((bytes, prefix.len() + 21)) +} +fn support_id( + source: &[u8], + target: &[u8], + kind: &[u8], + memory: &CleanupMemory, +) -> Result<([u8; 64], usize), ValidationIssue> { + let mut canonical = [0; 256]; + let size = 9usize + .checked_add(source.len()) + .and_then(|n| n.checked_add(target.len())) + .and_then(|n| n.checked_add(kind.len())) + .and_then(|n| n.checked_add(2)) + .ok_or(ValidationIssue::Overflow)?; + if size > canonical.len() { + return Err(ValidationIssue::Schema); + } + let mut n = 0; + for part in [b"Contains|".as_slice(), source, b"|", target, b"|", kind] { + canonical[n..n + part.len()].copy_from_slice(part); + n += part.len(); + } + let slice = Slice(&canonical[..n]); + derived_id( + &slice, + Span { + start: 0, + end: n as u64, + }, + KeyEncoding::NativeUtf8, + b"edge", + memory, + ) +} +#[derive(Clone, Copy)] +enum StagingKey { + Empty, + Known(u8), + Field { row: StagingRowWitness, field: u8 }, + FileSupportId(StagingRowWitness), +} +// Compact event recipes keep repeated full row witnesses out of visitor +// frames. Expansion happens once in the separate emission stage. +#[derive(Clone, Copy)] +enum StagingField { + Empty, + Known(u8), + Field(u8), + FileSupportId, +} +struct StagingWitnesses<'a, R: ReplayStagingCertificates + ?Sized> { + root: &'a RootedDirectory, + replay: &'a R, + context: [u8; 32], + summary: ReplaySummary, + cancel: &'a dyn Fn() -> bool, + repository: &'a [u8], + label: &'a [u8], + repo_id: ([u8; 64], usize), + root_id: ([u8; 64], usize), + contains_id: ([u8; 64], usize), + force_collision: bool, +} +impl StagingWitnesses<'_, R> { + fn known(&self, index: u8) -> Result<&[u8], ValidationIssue> { + Ok(match index { + 0 => self.label, + 1 => self.repository, + 2 => &self.repo_id.0[..self.repo_id.1], + 3 => &self.root_id.0[..self.root_id.1], + 4 => b"repository", + 5 => b"sourceroot", + 6 => b"repository_source_root", + 7 => b"source_root_file", + 8 => &self.contains_id.0[..self.contains_id.1], + _ => return Err(ValidationIssue::Schema), + }) + } + fn resolve( + &self, + key: StagingKey, + memory: &CleanupMemory, + run: &mut impl FnMut(&dyn ByteSource, Span, KeyEncoding) -> Result, + ) -> Result { + match key { + StagingKey::Empty => run(&Slice(b""), Span::default(), KeyEncoding::NativeUtf8), + StagingKey::Known(index) => { + let bytes = self.known(index)?; + run( + &Slice(bytes), + Span { + start: 0, + end: bytes.len() as u64, + }, + KeyEncoding::NativeUtf8, + ) + } + StagingKey::Field { row, .. } | StagingKey::FileSupportId(row) => { + if row.parent != self.root.identity() { + return Err(ValidationIssue::Schema); + } + let file = self + .root + .open_readonly_file(&row.name[..usize::from(row.name_len)]) + .map_err(|e| ValidationIssue::Io(e.kind()))?; + row.stamp + .recheck(&file) + .map_err(|_| ValidationIssue::Schema)?; + let source = Source { + file: &file, + len: row.stamp.len(), + }; + let result = if matches!(key, StagingKey::FileSupportId(_)) { + let mut file_id = [0; 64]; + let n = validation::copy_decoded_key( + &source, + row.fields[0], + KeyEncoding::JsonDecoded, + &mut file_id, + memory, + )?; + let (id, n) = support_id( + &self.root_id.0[..self.root_id.1], + &file_id[..n], + b"source_root_file", + memory, + )?; + run( + &Slice(&id[..n]), + Span { + start: 0, + end: n as u64, + }, + KeyEncoding::NativeUtf8, + ) + } else { + let StagingKey::Field { field, .. } = key else { + unreachable!() + }; + let span = *row + .fields + .get(usize::from(field)) + .ok_or(ValidationIssue::Schema)?; + run( + &source, + span, + if row.profile.kind == StagingPayloadKind::Connector { + KeyEncoding::NativeUtf8 + } else { + KeyEncoding::JsonDecoded + }, + ) + }; + row.stamp + .recheck(&file) + .map_err(|_| ValidationIssue::Schema)?; + result + } + } + } + fn equals( + &self, + key: StagingKey, + literal: &[u8], + memory: &CleanupMemory, + ) -> Result { + self.resolve(key, memory, &mut |source, span, encoding| { + Ok(validation::compare_encoded_keys( + source, + span, + encoding, + &Slice(literal), + Span { + start: 0, + end: literal.len() as u64, + }, + KeyEncoding::NativeUtf8, + memory, + )? == std::cmp::Ordering::Equal) + }) + } + #[inline(never)] + fn row_event( + &self, + row: &StagingRowWitness, + domain: u64, + native: bool, + ordinal: u64, + fields: [StagingField; 5], + emit: &mut dyn FnMut(validation::WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue> { + self.event( + domain, + native, + ordinal, + fields.map(|field| match field { + StagingField::Empty => StagingKey::Empty, + StagingField::Known(index) => StagingKey::Known(index), + StagingField::Field(field) => StagingKey::Field { row: *row, field }, + StagingField::FileSupportId => StagingKey::FileSupportId(*row), + }), + emit, + ) + } + fn event( + &self, + domain: u64, + native: bool, + ordinal: u64, + fields: [StagingKey; 5], + emit: &mut dyn FnMut(validation::WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue> { + emit(validation::WitnessEvent { + domain, + native, + ordinal, + fields: fields.map(Some), + }) + } +} +impl validation::ReplayWitnesses + for StagingWitnesses<'_, R> +{ + type Key = StagingKey; + fn replay( + &self, + memory: &CleanupMemory, + emit: &mut dyn FnMut(validation::WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue> { + use StagingKey::{Empty as E, Known as K}; + let _floor = memory.reserve(std::mem::size_of::() * 20 + 32768)?; + self.event(3001, false, 0, [K(2), K(0), K(4), E, K(0)], emit)?; + self.event(3002, false, 0, [K(3), K(1), K(5), K(1), K(1)], emit)?; + self.event(3003, false, 0, [K(8), K(6), K(2), K(3), E], emit)?; + let summary = replay_checked( + self.root, + self.context, + self.replay, + memory, + self.cancel, + &mut |row| { + use StagingField::{Empty as E, Known as K}; + let field = StagingField::Field; + let profile = row.profile; + if profile.kind == StagingPayloadKind::Connector { + self.row_event( + row, + 100 + u64::from(profile.from), + false, + row.ordinal, + [field(0), E, E, E, E], + emit, + ) + .map_err(StagingIssue::from)?; + self.row_event( + row, + 100 + u64::from(profile.to), + false, + row.ordinal, + [field(1), E, E, E, E], + emit, + ) + .map_err(StagingIssue::from)?; + let target = profile.table >= 87; + let edge = if target { + profile.table - 87 + } else { + profile.table - 62 + }; + self.row_event( + row, + 1000 + u64::from(edge) * 2 + u64::from(target), + false, + row.ordinal, + if target { + [field(0), field(1), E, E, E] + } else { + [field(1), field(0), E, E, E] + }, + emit, + ) + .map_err(StagingIssue::from)?; + } else { + self.row_event( + row, + 100 + u64::from(profile.table), + true, + row.ordinal, + [field(0), E, E, E, E], + emit, + ) + .map_err(StagingIssue::from)?; + // A declared row need not have connectors; connector endpoint + // rows still require exactly one declared typed ID. + self.row_event( + row, + 100 + u64::from(profile.table), + false, + row.ordinal, + [field(0), E, E, E, E], + emit, + ) + .map_err(StagingIssue::from)?; + if profile.kind == StagingPayloadKind::Edge { + let edge = profile.table - 37; + self.row_event( + row, + 1000 + u64::from(edge) * 2, + true, + row.ordinal, + [field(0), field(5), E, E, E], + emit, + ) + .map_err(StagingIssue::from)?; + self.row_event( + row, + 1001 + u64::from(edge) * 2, + true, + row.ordinal, + [field(0), field(6), E, E, E], + emit, + ) + .map_err(StagingIssue::from)?; + if profile.table == 39 + && (self + .equals( + StagingKey::Field { + row: *row, + field: 2, + }, + b"repository_source_root", + memory, + ) + .map_err(StagingIssue::from)? + || self + .equals( + StagingKey::Field { + row: *row, + field: 2, + }, + b"source_root_file", + memory, + ) + .map_err(StagingIssue::from)?) + { + self.row_event( + row, + 3003, + true, + row.ordinal, + [field(0), field(2), field(5), field(6), E], + emit, + ) + .map_err(StagingIssue::from)?; + } + } else if profile.table == 26 { + self.row_event( + row, + 3001, + true, + row.ordinal, + [field(0), field(1), field(2), field(3), field(4)], + emit, + ) + .map_err(StagingIssue::from)?; + } else if profile.table == 31 { + self.row_event( + row, + 3002, + true, + row.ordinal, + [field(0), field(1), field(2), field(3), field(4)], + emit, + ) + .map_err(StagingIssue::from)?; + } else { + if profile.table == 15 { + if !self + .equals( + StagingKey::Field { + row: *row, + field: 2, + }, + b"file", + memory, + ) + .map_err(StagingIssue::from)? + { + return Err(StagingIssue::Unknown); + } + self.row_event( + row, + 2000, + true, + row.ordinal, + [field(3), E, E, E, E], + emit, + ) + .map_err(StagingIssue::from)?; + self.row_event( + row, + 3003, + false, + row.ordinal, + [StagingField::FileSupportId, K(7), K(3), field(0), E], + emit, + ) + .map_err(StagingIssue::from)?; + } + if !self + .equals( + StagingKey::Field { + row: *row, + field: 3, + }, + b"", + memory, + ) + .map_err(StagingIssue::from)? + { + self.row_event( + row, + 2000, + false, + row.ordinal, + [field(3), E, E, E, E], + emit, + ) + .map_err(StagingIssue::from)?; + } + } + } + Ok(()) + }, + ) + .map_err(validation_error)?; + if summary != self.summary { + return Err(ValidationIssue::Schema); + } + Ok(()) + } + fn hash( + &self, + key: StagingKey, + domain: u64, + memory: &CleanupMemory, + ) -> Result<[u8; 32], ValidationIssue> { + let hash = self.resolve(key, memory, &mut |source, span, encoding| { + validation::key_stream_hash(source, span, encoding, domain, memory) + })?; + Ok(if self.force_collision { [0; 32] } else { hash }) + } + fn compare( + &self, + left: StagingKey, + right: StagingKey, + memory: &CleanupMemory, + ) -> Result { + self.resolve(left, memory, &mut |a, sa, ea| { + self.resolve(right, memory, &mut |b, sb, eb| { + validation::compare_encoded_keys(a, sa, ea, b, sb, eb, memory) + }) + }) + } + fn empty(&self, key: StagingKey, memory: &CleanupMemory) -> Result { + self.equals(key, b"", memory) + } +} + +/// Complete source/schema/endpoint/support association facts. This is neither +/// legacy quiescence nor a mutation capability. A durable cohort prefix retains +/// the certificate set before the first leaf is retired. +#[derive(Clone, Copy)] +pub(crate) struct StagingAssociation { + root: FileIdentity, + repository: FileIdentity, + source_context: [u8; 32], + summary: ReplaySummary, +} +impl StagingAssociation { + pub(crate) fn root_identity(&self) -> FileIdentity { + self.root + } + pub(crate) fn repository_identity(&self) -> FileIdentity { + self.repository + } + pub(crate) fn source_context(&self) -> [u8; 32] { + self.source_context + } + pub(crate) fn payload_count(&self) -> u64 { + self.summary.payloads + } + pub(crate) fn row_count(&self) -> u64 { + self.summary.rows + } + pub(crate) fn certificate_set_digest(&self) -> [u8; 32] { + self.summary.digest + } + pub(crate) fn encode(&self, out: &mut [u8; 512]) { + out.fill(0); + out[..8].copy_from_slice(b"CGSTGA01"); + put_id(&mut out[16..32], self.root); + put_id(&mut out[32..48], self.repository); + out[48..80].copy_from_slice(&self.source_context); + out[80..112].copy_from_slice(&self.summary.digest); + put64(&mut out[112..], self.summary.payloads); + put64(&mut out[120..], self.summary.rows); + put64(&mut out[128..], 1); + finish(out); + } + /// Decodes retained facts only. The caller must authenticate the exact + /// completed private cohort/source context before using them for admission. + pub(crate) fn decode(bytes: &[u8; 512]) -> Result { + if &bytes[..8] != b"CGSTGA01" + || !checked(bytes) + || get64(&bytes[128..]) != 1 + || bytes[8..16] + .iter() + .chain(bytes[136..480].iter()) + .any(|b| *b != 0) + || get64(&bytes[112..]) == 0 + || get64(&bytes[120..]) == 0 + { + return Err(StagingIssue::Unknown); + } + Ok(Self { + root: get_id(&bytes[16..32]), + repository: get_id(&bytes[32..48]), + source_context: bytes[48..80].try_into().unwrap(), + summary: ReplaySummary { + payloads: get64(&bytes[112..]), + rows: get64(&bytes[120..]), + digest: bytes[80..112].try_into().unwrap(), + }, + }) + } +} +fn known_payload_count( + root: &RootedDirectory, + cancel: &dyn Fn() -> bool, +) -> Result { + let mut count = 0u64; + let mut scratch = [0; 4096]; + let mut cancelled = false; + root.visit_entries(&mut scratch, &mut |name| { + if cancel() { + cancelled = true; + return Err(io::ErrorKind::Interrupted.into()); + } + if staging_profile(name).is_some() { + file_stamp(&root.open_readonly_file(name)?) + .map_err(|_| io::Error::from(io::ErrorKind::InvalidData))?; + count = count.checked_add(1).ok_or(io::ErrorKind::InvalidData)?; + } + Ok(()) + }) + .map_err(|e| { + if cancelled { + StagingIssue::Cancelled + } else { + StagingIssue::Io(e.kind()) + } + })?; + Ok(count) +} +/// No surviving database, native handle, manifest or cache body is used. Every +/// typed row, connector endpoint, File path and mandatory support edge joins +/// exactly through the committed row-span stream, with finite collision fallback. +// Full association keeps rooted source, immutable context, bounded replay and cancellation explicit. +#[allow(clippy::too_many_arguments)] +pub(crate) fn associate_staging_bundle( + root: &RootedDirectory, + repository_root: &std::path::Path, + source_context: [u8; 32], + replay: &R, + capacity: usize, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + source_check: &dyn Fn() -> Result<(), StagingIssue>, +) -> Result { + associate_staging_inner( + root, + repository_root, + source_context, + replay, + capacity, + memory, + cancel, + source_check, + false, + ) +} +// Full association keeps rooted source, immutable context, bounded replay and cancellation explicit. +#[allow(clippy::too_many_arguments)] +fn associate_staging_inner( + root: &RootedDirectory, + repository_root: &std::path::Path, + source_context: [u8; 32], + replay: &R, + capacity: usize, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + source_check: &dyn Fn() -> Result<(), StagingIssue>, + force_collision: bool, +) -> Result { + if cancel() { + return Err(StagingIssue::Cancelled); + } + let _floor = memory.reserve( + std::mem::size_of::>() * 3 + + std::mem::size_of::() * 20 + + 32768, + )?; + source_check()?; + if !repository_root.is_absolute() || !super::inventory::real_path(repository_root) { + return Err(StagingIssue::Unknown); + } + let repository = repository_root + .to_str() + .ok_or(StagingIssue::Unknown)? + .as_bytes(); + let label = repository_root + .file_name() + .and_then(|v| v.to_str()) + .ok_or(StagingIssue::Unknown)? + .as_bytes(); + let repository_identity = + locks::path_identity(repository_root).map_err(|_| StagingIssue::Changed)?; + let known = known_payload_count(root, cancel)?; + let summary = replay_checked( + root, + source_context, + replay, + memory, + cancel, + &mut |_| Ok(()), + )?; + if known == 0 || known != summary.payloads { + return Err(StagingIssue::Unknown); + } + let repo_id = derived_id( + &Slice(label), + Span { + start: 0, + end: label.len() as u64, + }, + KeyEncoding::NativeUtf8, + b"Repository", + memory, + )?; + let root_id = derived_id( + &Slice(repository), + Span { + start: 0, + end: repository.len() as u64, + }, + KeyEncoding::NativeUtf8, + b"SourceRoot", + memory, + )?; + let contains_id = support_id( + &repo_id.0[..repo_id.1], + &root_id.0[..root_id.1], + b"repository_source_root", + memory, + )?; + let witnesses = StagingWitnesses { + root, + replay, + context: source_context, + summary, + cancel, + repository, + label, + repo_id, + root_id, + contains_id, + force_collision, + }; + validation::join_selected_witnesses(&witnesses, memory, capacity)?; + if known_payload_count(root, cancel)? != known + || locks::path_identity(repository_root).map_err(|_| StagingIssue::Changed)? + != repository_identity + { + return Err(StagingIssue::Changed); + } + source_check()?; + if cancel() { + return Err(StagingIssue::Cancelled); + } + Ok(StagingAssociation { + root: root.identity(), + repository: repository_identity, + source_context, + summary, + }) +} + +pub(crate) const STAGING_CHECKPOINT_BYTES: usize = 768; +pub(crate) const STAGING_ROWS_PER_BATCH: usize = 4; +/// Row-boundary progress, never a completed payload certificate. The portable +/// chain is over committed fixed row records, not an opaque sha2 object image. +#[derive(Clone, Copy)] +pub(crate) struct StagingProjectionCheckpoint { + row: StagingRowWitness, + source_context: [u8; 32], + next: u64, + rows: u64, + chain: [u8; 32], + previous: [Span; 3], + stage: u8, +} +impl StagingProjectionCheckpoint { + pub(crate) fn payload_name(&self) -> &[u8] { + &self.row.name[..usize::from(self.row.name_len)] + } + pub(crate) fn finished(&self) -> bool { + self.stage == 2 + } + pub(crate) fn encode(&self, out: &mut [u8; STAGING_CHECKPOINT_BYTES]) { + out.fill(0); + out[..8].copy_from_slice(b"CGSTGP01"); + out[8] = self.stage; + out[9] = self.row.name_len; + out[10] = match self.row.profile.kind { + StagingPayloadKind::Node => 0, + StagingPayloadKind::Edge => 1, + StagingPayloadKind::Connector => 2, + }; + out[11] = self.row.profile.table; + out[12] = self.row.profile.from; + out[13] = self.row.profile.to; + put64(&mut out[16..], self.row.profile.chunk); + put64(&mut out[24..], self.next); + out[32..287].copy_from_slice(&self.row.name); + put_id(&mut out[288..304], self.row.parent); + self.row + .stamp + .encode_checkpoint((&mut out[304..384]).try_into().unwrap()); + out[384..416].copy_from_slice(&self.source_context); + put64(&mut out[416..], self.rows); + out[424..456].copy_from_slice(&self.chain); + for (i, s) in self.previous.iter().enumerate() { + put64(&mut out[456 + i * 16..], s.start); + put64(&mut out[464 + i * 16..], s.end); + } + finish(out); + } + pub(crate) fn decode(bytes: &[u8; STAGING_CHECKPOINT_BYTES]) -> Result { + if &bytes[..8] != b"CGSTGP01" + || !checked(bytes) + || bytes[8] > 2 + || bytes[14..16] + .iter() + .chain(bytes[287..288].iter()) + .chain(bytes[504..736].iter()) + .any(|b| *b != 0) + { + return Err(StagingIssue::Unknown); + } + let mut address = *bytes; + address[..8].copy_from_slice(b"CGLGSR01"); + address[8] = bytes[10]; + address[9] = bytes[11]; + address[10] = bytes[12]; + address[11] = bytes[13]; + address[12] = bytes[9]; + address[13..16].fill(0); + address[24..32].fill(0); + address[384..736].fill(0); + finish(&mut address); + let row = StagingRowWitness::decode(&address)?; + let next = get64(&bytes[24..]); + let rows = get64(&bytes[416..]); + let mut previous = [Span::default(); 3]; + for (i, s) in previous.iter_mut().enumerate() { + s.start = get64(&bytes[456 + i * 16..]); + s.end = get64(&bytes[464 + i * 16..]); + if s.start > s.end || s.end > row.stamp.len() { + return Err(StagingIssue::Unknown); + } + } + if next > row.stamp.len() + || (rows == 0 + && (next != 0 || bytes[8] != 0 || previous.iter().any(|s| *s != Span::default()))) + || (bytes[8] > 0 && next != row.stamp.len()) + { + return Err(StagingIssue::Unknown); + } + Ok(Self { + row, + source_context: bytes[384..416].try_into().unwrap(), + next, + rows, + chain: bytes[424..456].try_into().unwrap(), + previous, + stage: bytes[8], + }) + } + pub(crate) fn digest(&self) -> [u8; 32] { + let mut b = [0; STAGING_CHECKPOINT_BYTES]; + self.encode(&mut b); + hash(&b) + } + pub(crate) fn source_context(&self) -> [u8; 32] { + self.source_context + } +} +pub(crate) struct StagingProjectionBatch { + pub(crate) rows: [Option; STAGING_ROWS_PER_BATCH], + pub(crate) checkpoint: StagingProjectionCheckpoint, + pub(crate) certificate: Option, +} +/// An ordinary advisory job; no writer/family/root/selector authority is held. +/// ACK only after the exact <=3840B row+checkpoint segment is durably committed +/// through the caller's real private publication transport. +pub(crate) struct StagingProjection { + committed: StagingProjectionCheckpoint, + pending: Option, + _floor: super::maintenance_budget::MemoryReservation, +} +impl StagingProjection { + pub(crate) fn new( + root: &RootedDirectory, + name: &[u8], + source_context: [u8; 32], + memory: &CleanupMemory, + ) -> Result, StagingIssue> { + let _temporary = memory.reserve(std::mem::size_of::() * 3 + 8192)?; + let profile = staging_profile(name).ok_or(StagingIssue::Unknown)?; + let file = root.open_readonly_file(name)?; + let stamp = file_stamp(&file)?; + let mut copy = [0; 255]; + copy[..name.len()].copy_from_slice(name); + let cp = StagingProjectionCheckpoint { + row: StagingRowWitness { + profile, + parent: root.identity(), + name: copy, + name_len: name.len() as u8, + stamp, + ordinal: 0, + fields: [Span::default(); 8], + }, + source_context, + next: 0, + rows: 0, + chain: [0; 32], + previous: [Span::default(); 3], + stage: 0, + }; + Self::restore(root, cp, source_context, memory) + } + pub(crate) fn restore( + root: &RootedDirectory, + checkpoint: StagingProjectionCheckpoint, + source_context: [u8; 32], + memory: &CleanupMemory, + ) -> Result, StagingIssue> { + let floor = memory.reserve(std::mem::size_of::() * 3 + 8192)?; + if checkpoint.source_context != source_context || checkpoint.row.parent != root.identity() { + return Err(StagingIssue::Changed); + } + recheck( + checkpoint.row.stamp, + &root + .open_readonly_file(&checkpoint.row.name[..usize::from(checkpoint.row.name_len)])?, + )?; + super::maintenance_budget::MemoryBox::new( + Self { + committed: checkpoint, + pending: None, + _floor: floor, + }, + memory, + ) + .map_err(StagingIssue::Resource) + } + pub(crate) fn checkpoint(&self) -> StagingProjectionCheckpoint { + self.committed + } + pub(crate) fn acknowledge(&mut self, published_digest: [u8; 32]) -> Result<(), StagingIssue> { + let cp = self.pending.ok_or(StagingIssue::Unknown)?; + if cp.digest() != published_digest { + return Err(StagingIssue::Changed); + } + self.committed = cp; + self.pending = None; + Ok(()) + } + pub(crate) fn advance( + &mut self, + root: &RootedDirectory, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + ) -> Result>, StagingIssue> + { + if cancel() { + return Err(StagingIssue::Cancelled); + } + if self.pending.is_some() { + return Err(StagingIssue::Unknown); + } + if self.committed.stage == 2 { + return Ok(None); + } + let _temporary = memory.reserve( + std::mem::size_of::() * 4 + + std::mem::size_of::>() + + std::mem::size_of::<[Option; 127]>() + + 32768, + )?; + let mut cp = self.committed; + if root.identity() != cp.row.parent { + return Err(StagingIssue::Changed); + } + let file = root.open_readonly_file(&cp.row.name[..usize::from(cp.row.name_len)])?; + recheck(cp.row.stamp, &file)?; + let source = Source { + file: &file, + len: cp.row.stamp.len(), + }; + let mut batch = super::maintenance_budget::MemoryBox::new( + StagingProjectionBatch { + rows: [None; STAGING_ROWS_PER_BATCH], + checkpoint: cp, + certificate: None, + }, + memory, + )?; + if cp.stage == 1 { + let mut sha = Sha256::new(); + let mut offset = 0u64; + let mut bytes = [0; 4096]; + while offset < source.len { + if cancel() { + return Err(StagingIssue::Cancelled); + } + let n = source.read_at(offset, &mut bytes)?; + if n == 0 { + return Err(StagingIssue::Changed); + } + sha.update(&bytes[..n]); + offset = offset.checked_add(n as u64).ok_or(StagingIssue::Unknown)?; + } + recheck(cp.row.stamp, &file)?; + batch.certificate = Some(StagingPayloadCertificate { + row: cp.row, + digest: sha.finalize().into(), + rows: cp.rows, + witness_chain: cp.chain, + source_context: cp.source_context, + }); + cp.stage = 2; + } else if cp.row.profile.kind == StagingPayloadKind::Connector { + project_csv(&source, &mut cp, &mut batch.rows, memory, cancel)?; + } else { + let resumed = cp.next != 0; + let reader = MaintenanceJsonReader::new( + Sequential { + source: &source, + offset: cp.next, + prefix_array: resumed, + cancel, + }, + memory, + )?; + let mut scan = Scanner { + source: &source, + reader, + memory, + cancel, + adjustment: if resumed { + Some(cp.next.checked_sub(1).ok_or(StagingIssue::Unknown)?) + } else { + None + }, + }; + if scan.token()?.kind != TokenKind::ArrayStart { + return Err(StagingIssue::Unknown); + } + let mut top = scan.token()?; + for slot in &mut batch.rows { + if top.kind == TokenKind::ArrayEnd { + break; + } + if top.kind != TokenKind::ObjectStart { + return Err(StagingIssue::Unknown); + } + let mut row = cp.row; + row.ordinal = cp.rows; + let row = scan.row(row)?; + if cp.rows > 0 + && validation::compare_decoded_spans( + &source, + cp.previous[0], + &source, + row.fields[0], + memory, + )? != std::cmp::Ordering::Less + { + return Err(StagingIssue::Unknown); + } + let mut bytes = [0; STAGING_ROW_BYTES]; + row.encode(&mut bytes); + extend_chain(&mut cp.chain, &bytes); + cp.rows = cp.rows.checked_add(1).ok_or(StagingIssue::Unknown)?; + cp.previous[0] = row.fields[0]; + *slot = Some(row); + top = scan.token()?; + } + if top.kind == TokenKind::ArrayEnd { + if cp.rows == 0 || scan.reader.next_token()?.is_some() { + return Err(StagingIssue::Unknown); + } + cp.next = source.len; + cp.stage = 1; + } else if top.kind == TokenKind::ObjectStart { + cp.next = top.start; + } else { + return Err(StagingIssue::Unknown); + } + } + recheck(cp.row.stamp, &file)?; + if cancel() { + return Err(StagingIssue::Cancelled); + } + batch.checkpoint = cp; + self.pending = Some(cp); + Ok(Some(batch)) + } +} +fn project_csv( + source: &Source<'_>, + cp: &mut StagingProjectionCheckpoint, + rows: &mut [Option; STAGING_ROWS_PER_BATCH], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result<(), StagingIssue> { + let _floor = memory.reserve(std::mem::size_of::>() + 8192)?; + let p = cp.row.profile; + if !super::maintenance_legacy_association::staging_endpoint_allowed(p.table, p.from, p.to) { + return Err(StagingIssue::Unknown); + } + let target = p.table >= 87; + let mut csv = Csv { + source, + bytes: [0; 4096], + cursor: 0, + filled: 0, + loaded: cp.next, + offset: cp.next, + hash: Sha256::new(), + cancel, + }; + if cp.next == 0 { + for b in b"from_id,to_id,role\r\n" { + if csv.byte()? != Some(*b) { + return Err(StagingIssue::Unknown); + } + } + } + for slot in rows { + let start = csv.offset; + let Some(first) = csv.byte()? else { break }; + let mut line = [0; 192]; + line[0] = first; + let mut n = 1; + loop { + let b = csv.byte()?.ok_or(StagingIssue::Unknown)?; + if n == line.len() { + return Err(StagingIssue::Unknown); + } + line[n] = b; + n += 1; + if b == b'\n' { + break; + } + } + if n < 2 || line[n - 2] != b'\r' { + return Err(StagingIssue::Unknown); + } + let body = &line[..n - 2]; + let a = body + .iter() + .position(|b| *b == b',') + .ok_or(StagingIssue::Unknown)?; + let b = a + + 1 + + body[a + 1..] + .iter() + .position(|b| *b == b',') + .ok_or(StagingIssue::Unknown)?; + if !id_literal(&body[..a], p.from) + || !id_literal(&body[a + 1..b], p.to) + || &body[b + 1..] + != if target { + b"target".as_slice() + } else { + b"source" + } + { + return Err(StagingIssue::Unknown); + } + let mut row = cp.row; + row.ordinal = cp.rows; + row.fields[0] = Span { + start, + end: start + a as u64, + }; + row.fields[1] = Span { + start: start + a as u64 + 1, + end: start + b as u64, + }; + row.fields[2] = Span { + start: start + b as u64 + 1, + end: start + (n - 2) as u64, + }; + if cp.rows > 0 + && compare_ascii(source, cp.previous[0], source, row.fields[0])?.then(compare_ascii( + source, + cp.previous[1], + source, + row.fields[1], + )?) != std::cmp::Ordering::Less + { + return Err(StagingIssue::Unknown); + } + let mut bytes = [0; STAGING_ROW_BYTES]; + row.encode(&mut bytes); + extend_chain(&mut cp.chain, &bytes); + cp.rows = cp.rows.checked_add(1).ok_or(StagingIssue::Unknown)?; + cp.previous.copy_from_slice(&row.fields[..3]); + *slot = Some(row); + } + cp.next = csv.offset; + if cp.next == source.len { + if cp.rows == 0 { + return Err(StagingIssue::Unknown); + } + cp.stage = 1; + } + Ok(()) +} + +/// Advisory per-leaf preparation from the actual completed rooted cohort reader. +/// Raw parsed certificates cannot construct this owner or its membership token. +pub(crate) struct PreparedStagingLeaf { + _payload: File, + association: StagingAssociation, + certificate: StagingPayloadCertificate, + original: super::maintenance_legacy_bootstrap::LegacyClosedVersion, + current: super::maintenance_legacy_bootstrap::LegacyClosedVersion, + _floor: super::maintenance_budget::MemoryReservation, +} +pub(crate) fn prepare_staging_leaf( + cohort: &super::maintenance_candidate::LegacyStagingCohortLeaf, + owner: &super::maintenance_legacy_bootstrap::LegacySourceOwner, + root: &RootedDirectory, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, StagingIssue> { + if cancel() { + return Err(StagingIssue::Cancelled); + } + let floor = memory.reserve( + std::mem::size_of::() * 3 + + std::mem::size_of::() * 3 + + 16384, + )?; + let association = *cohort.association(); + let certificate = *cohort.certificate(); + let original = *cohort.original_source(); + let current = owner.closed_version(); + if original.phase() != super::maintenance_legacy_guard::LegacyPhase::Preparing + || current.phase() != super::maintenance_legacy_guard::LegacyPhase::Retiring + || !current.same_job_source(&original) + || original.context_digest() != association.source_context + || certificate.source_context != association.source_context + || certificate.row.parent != association.root + || root.identity() != association.root + || !owner + .is_current(memory) + .map_err(|_| StagingIssue::Changed)? + { + return Err(StagingIssue::Changed); + } + let file = + root.open_readonly_file(&certificate.row.name[..usize::from(certificate.row.name_len)])?; + recheck(certificate.row.stamp, &file)?; + let source = Source { + file: &file, + len: certificate.row.stamp.len(), + }; + let mut sha = Sha256::new(); + let mut offset = 0u64; + let mut bytes = [0; 4096]; + while offset < source.len { + if cancel() { + return Err(StagingIssue::Cancelled); + } + let n = source.read_at(offset, &mut bytes)?; + if n == 0 { + return Err(StagingIssue::Changed); + } + sha.update(&bytes[..n]); + offset = offset.checked_add(n as u64).ok_or(StagingIssue::Unknown)?; + } + if <[u8; 32]>::from(sha.finalize()) != certificate.digest { + return Err(StagingIssue::Changed); + } + recheck(certificate.row.stamp, &file)?; + recheck( + certificate.row.stamp, + &root.open_readonly_file(&certificate.row.name[..usize::from(certificate.row.name_len)])?, + )?; + if !owner + .is_current(memory) + .map_err(|_| StagingIssue::Changed)? + || !owner.closed_version().same_job_source(¤t) + { + return Err(StagingIssue::Changed); + } + if cancel() { + return Err(StagingIssue::Cancelled); + } + super::maintenance_budget::MemoryBox::new( + PreparedStagingLeaf { + _payload: file, + association, + certificate, + original, + current, + _floor: floor, + }, + memory, + ) + .map_err(StagingIssue::Resource) +} +/// This borrowed hot frame exists only while the actual closed-source gate and +/// retained source owner exclude producers for this exact incarnation/job. +pub(crate) struct StagingLeafMutation<'a> { + parent: &'a RootedDirectory, + leaf: &'a StagingRowWitness, +} +impl StagingLeafMutation<'_> { + pub(crate) fn source_parent(&self) -> &RootedDirectory { + self.parent + } + pub(crate) fn name(&self) -> &[u8] { + &self.leaf.name[..usize::from(self.leaf.name_len)] + } + pub(crate) fn payload_identity(&self) -> FileIdentity { + self.leaf.stamp.identity() + } +} +impl PreparedStagingLeaf { + pub(crate) fn with_admitted( + &self, + scope: &mut super::maintenance_legacy_bootstrap::PreparedLegacyStagingScope, + owner: &super::maintenance_legacy_bootstrap::LegacySourceOwner, + grant: &mut super::maintenance_budget::MutationGrant, + caller_callback_entries: usize, + memory: &CleanupMemory, + apply: &mut impl FnMut( + &StagingLeafMutation<'_>, + &mut super::maintenance_budget::MutationGrant, + ) -> Result, + ) -> Result { + use super::maintenance_legacy_guard::LegacyPhase; + use locks::{FixedControlError as Error, FixedControlProblem as Problem}; + // Complete caller effects are reserved before any source-role opens. + // The actual scope helper charges its own observations independently. + grant.take_entries( + 2usize + .checked_add(caller_callback_entries) + .ok_or(Error::Resource(ResourceLimit::ArithmeticOverflow))?, + )?; + super::maintenance_legacy_bootstrap::with_closed_staging_source( + scope, + owner, + grant, + memory, + &mut |authority, grant| { + let actual = authority.closed_version(); + if actual.phase() != LegacyPhase::Retiring + || !actual.same_job_source(&self.current) + || !actual.same_job_source(&self.original) + || authority.staging_directory().identity() != self.association.root + { + return Err(Error::Invalid(Problem::Changed)); + } + grant.check_time()?; + let parent = authority.staging_directory(); + let file = parent.open_readonly_file( + &self.certificate.row.name[..usize::from(self.certificate.row.name_len)], + )?; + self.certificate.row.stamp.recheck(&file)?; + grant.check_time()?; + apply( + &StagingLeafMutation { + parent, + leaf: &self.certificate.row, + }, + grant, + ) + }, + ) + } +} diff --git a/src/storage/maintenance_legacy_stream.rs b/src/storage/maintenance_legacy_stream.rs new file mode 100644 index 00000000..aef467cb --- /dev/null +++ b/src/storage/maintenance_legacy_stream.rs @@ -0,0 +1,2042 @@ +//! Bounded transport over the existing prebound maintenance stream. Frames and +//! checkpoints are facts; the admission facade supplies actual write authority. +use super::inventory::MaintenanceTarget; +use super::locks::{FixedControlError, FixedControlProblem}; +use super::maintenance_admission::RootOriginBinding; +use super::maintenance_admission::{ + self as admission, PreparedControlUpdate, PreparedPrivateMetadata, +}; +use super::maintenance_budget::{CleanupMemory, MemoryReservation, MutationGrant, ReservedBytes}; +use super::maintenance_legacy_association::{ + LegacyCacheCertificate, LegacyCheckpoint, LegacyIssue, ReplayLegacyCertificates, +}; +use super::maintenance_legacy_bootstrap::LegacyClosedVersion; +use super::maintenance_legacy_staging::{ + ReplayStagingCertificates, StagingIssue, StagingPayloadCertificate, + StagingProjectionCheckpoint, StagingReplayEvent, StagingRowWitness, +}; +use sha2::{Digest, Sha256}; +pub(crate) const CURSOR_BYTES: usize = 1105; +pub(crate) const FRAME_LIMIT: usize = 4096; +const HEADER_BYTES: usize = 2048; +const FRAME_PREFIX: usize = 96; +const FRAME_FOOTER: usize = 32; +fn invalid() -> FixedControlError { + FixedControlError::Invalid(FixedControlProblem::InvalidRecord) +} +fn put(b: &mut [u8], v: u64) { + b[..8].copy_from_slice(&v.to_le_bytes()); +} +fn num(b: &[u8]) -> u64 { + u64::from_le_bytes(b[..8].try_into().unwrap()) +} +fn finish(b: &mut [u8]) { + let end = b.len() - 32; + let d = Sha256::digest(&b[..end]); + b[end..].copy_from_slice(&d); +} +fn checked(b: &[u8]) -> bool { + b.len() >= 32 && Sha256::digest(&b[..b.len() - 32])[..] == b[b.len() - 32..] +} +#[derive(Clone, Copy)] +pub(crate) struct ActiveSourceSnapshot { + database: super::locks::FixedFileStamp, + manifest: super::locks::FixedFileStamp, + cache: super::locks::FileIdentity, + epoch: [u8; 32], +} +impl ActiveSourceSnapshot { + pub(crate) fn observe( + database: &std::fs::File, + manifest: &std::fs::File, + cache: &super::locks::RootedDirectory, + domain: &super::maintenance_ownership::ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + memory: &CleanupMemory, + ) -> Result { + let _floor = memory.reserve(4096).map_err(FixedControlError::Resource)?; + if !super::maintenance_ownership::expected_reference_current(domain, expected, memory)? { + return Err(invalid()); + } + let database_stamp = + super::locks::FixedFileStamp::capture(database, super::locks::FixedControlKind::File)?; + let manifest_stamp = + super::locks::FixedFileStamp::capture(manifest, super::locks::FixedControlKind::File)?; + let mut epoch = [0; 512]; + expected.encode_checkpoint(&mut epoch); + database_stamp.recheck(database)?; + manifest_stamp.recheck(manifest)?; + if !super::maintenance_ownership::expected_reference_current(domain, expected, memory)? { + return Err(invalid()); + } + Ok(Self { + database: database_stamp, + manifest: manifest_stamp, + cache: cache.identity(), + epoch: Sha256::digest(epoch).into(), + }) + } + fn encode(&self, out: &mut [u8; 512]) { + out.fill(0); + out[..8].copy_from_slice(b"CGLGAS01"); + self.database + .encode_checkpoint((&mut out[16..96]).try_into().unwrap()); + self.manifest + .encode_checkpoint((&mut out[96..176]).try_into().unwrap()); + let (a, b) = self.cache.fixed_words(); + put(&mut out[176..], a); + put(&mut out[184..], b); + out[192..224].copy_from_slice(&self.epoch); + finish(out); + } + fn decode(b: &[u8; 512]) -> Result { + if &b[..8] != b"CGLGAS01" + || !checked(b) + || b[8..16].iter().chain(b[224..480].iter()).any(|b| *b != 0) + { + return Err(invalid()); + } + Ok(Self { + database: super::locks::FixedFileStamp::decode_checkpoint( + b[16..96].try_into().unwrap(), + )?, + manifest: super::locks::FixedFileStamp::decode_checkpoint( + b[96..176].try_into().unwrap(), + )?, + cache: super::locks::FileIdentity::from_fixed_words(num(&b[176..]), num(&b[184..])), + epoch: b[192..224].try_into().unwrap(), + }) + } + pub(crate) fn context_digest(&self) -> [u8; 32] { + let mut image = [0; 512]; + self.encode(&mut image); + Sha256::digest(image).into() + } + fn matches(&self, cp: &LegacyCheckpoint) -> bool { + let mut epoch = [0; 512]; + cp.source_epoch().encode_checkpoint(&mut epoch); + cp.matches_closed_source(Some(self.database), Some(self.manifest), self.cache) + && <[u8; 32]>::from(Sha256::digest(epoch)) == self.epoch + } +} +#[derive(Clone, Copy)] +enum StreamSource { + Closed(LegacyClosedVersion), + Active(ActiveSourceSnapshot), +} +impl StreamSource { + fn encode(&self, out: &mut [u8; 512]) { + match self { + Self::Closed(s) => s.encode_checkpoint(out), + Self::Active(s) => s.encode(out), + } + } + fn decode(b: &[u8; 512]) -> Result { + if &b[..8] == b"CGLGAS01" { + Ok(Self::Active(ActiveSourceSnapshot::decode(b)?)) + } else { + Ok(Self::Closed(LegacyClosedVersion::decode_checkpoint(b)?)) + } + } + fn digest(&self) -> [u8; 32] { + match self { + Self::Closed(s) => s.context_digest(), + Self::Active(s) => s.context_digest(), + } + } + fn matches(&self, cp: &LegacyCheckpoint) -> bool { + match self { + Self::Closed(s) => { + cp.matches_closed_source(s.database_stamp(), s.manifest_stamp(), s.cache_identity()) + } + Self::Active(s) => s.matches(cp), + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum LegacyStreamKind { + Association, + Staging, + Backup, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum LegacyFrameKind { + CacheCertificate, + AssociationCheckpoint, + StagingRows, + StagingPayload, + StagingCheckpoint, + StagingSeal, + NativeCohort, +} +impl LegacyFrameKind { + fn byte(self) -> u8 { + match self { + Self::CacheCertificate => 1, + Self::AssociationCheckpoint => 2, + Self::StagingRows => 3, + Self::StagingPayload => 4, + Self::StagingCheckpoint => 5, + Self::StagingSeal => 6, + Self::NativeCohort => 7, + } + } + fn parse(v: u8) -> Result { + match v { + 1 => Ok(Self::CacheCertificate), + 2 => Ok(Self::AssociationCheckpoint), + 3 => Ok(Self::StagingRows), + 4 => Ok(Self::StagingPayload), + 5 => Ok(Self::StagingCheckpoint), + 6 => Ok(Self::StagingSeal), + 7 => Ok(Self::NativeCohort), + _ => Err(invalid()), + } + } + fn checkpoint(self) -> bool { + matches!( + self, + Self::AssociationCheckpoint + | Self::StagingCheckpoint + | Self::StagingSeal + | Self::NativeCohort + ) + } + fn validate(self, p: &[u8]) -> Result<(), FixedControlError> { + match self { + Self::CacheCertificate => { + LegacyCacheCertificate::decode(p.try_into().map_err(|_| invalid())?) + .map_err(|_| invalid())?; + } + Self::AssociationCheckpoint => { + LegacyCheckpoint::decode(p.try_into().map_err(|_| invalid())?) + .map_err(|_| invalid())?; + } + Self::StagingRows => { + if p.is_empty() || p.len() % 768 != 0 || p.len() > 3072 { + return Err(invalid()); + } + for row in p.chunks_exact(768) { + StagingRowWitness::decode(row.try_into().unwrap()).map_err(|_| invalid())?; + } + } + Self::StagingPayload => { + StagingPayloadCertificate::decode(p.try_into().map_err(|_| invalid())?) + .map_err(|_| invalid())?; + } + Self::NativeCohort => { + if p.len() != 3968 || &p[..8] != b"CGLGNCP1" || !checked(p) { + return Err(invalid()); + } + let original = LegacyStreamHeader::decode(&p[8..2056])?; + let retiring = + LegacyClosedVersion::decode_checkpoint(p[2056..2568].try_into().unwrap())?; + if !original.done() + || original.kind() != LegacyStreamKind::Association + || retiring.phase() != super::maintenance_legacy_guard::LegacyPhase::Retiring + || !retiring.same_job_source(&original.source_version().ok_or_else(invalid)?) + { + return Err(invalid()); + } + } + Self::StagingSeal => { + super::maintenance_legacy_staging::StagingAssociation::decode( + p.try_into().map_err(|_| invalid())?, + ) + .map_err(|_| invalid())?; + } + Self::StagingCheckpoint => { + StagingProjectionCheckpoint::decode(p.try_into().map_err(|_| invalid())?) + .map_err(|_| invalid())?; + } + } + Ok(()) + } +} +#[derive(Clone, Copy, PartialEq, Eq)] +enum Phase { + Running, + AwaitCheckpoint, + AppendIntent, + Done, + ResetIntent, +} +/// Copy fixed facts. Decode never acquires a source or destination capability. +#[derive(Clone, Copy)] +pub(crate) struct LegacyStreamHeader { + cursor: [u8; CURSOR_BYTES], + source: StreamSource, + context: [u8; 32], + kind: LegacyStreamKind, + phase: Phase, + physical_end: u64, + committed_end: u64, + sequence: u64, + committed_sequence: u64, + chain: [u8; 32], + committed_chain: [u8; 32], + checkpoint_offset: u64, + checkpoint_len: u64, + pending_kind: LegacyFrameKind, + pending_len: u64, + pending_digest: [u8; 32], + ack: [u8; 32], +} +impl LegacyStreamHeader { + pub(crate) fn cursor(&self) -> &[u8; CURSOR_BYTES] { + &self.cursor + } + pub(crate) fn source_version(&self) -> Option { + match self.source { + StreamSource::Closed(s) => Some(s), + StreamSource::Active(_) => None, + } + } + pub(crate) fn context_digest(&self) -> [u8; 32] { + self.context + } + pub(crate) fn committed_end(&self) -> u64 { + self.committed_end + } + pub(crate) fn sequence(&self) -> u64 { + self.sequence + } + pub(crate) fn chain_digest(&self) -> [u8; 32] { + self.chain + } + pub(crate) fn kind(&self) -> LegacyStreamKind { + self.kind + } + pub(crate) fn awaits_checkpoint(&self) -> bool { + self.phase == Phase::AwaitCheckpoint + } + pub(crate) fn done(&self) -> bool { + self.phase == Phase::Done + } + pub(crate) fn resetting(&self) -> bool { + self.phase == Phase::ResetIntent + } + pub(crate) fn pending_kind(&self) -> Option { + if self.pending() { + Some(self.pending_kind) + } else { + None + } + } + pub(crate) fn pending(&self) -> bool { + self.phase == Phase::AppendIntent + } + pub(crate) fn encode(&self, out: &mut [u8; HEADER_BYTES]) { + out.fill(0); + out[..8].copy_from_slice(b"CGLEG001"); + out[8] = match self.kind { + LegacyStreamKind::Association => 1, + LegacyStreamKind::Staging => 2, + LegacyStreamKind::Backup => 3, + }; + out[9] = match self.phase { + Phase::Running => 1, + Phase::AwaitCheckpoint => 2, + Phase::AppendIntent => 3, + Phase::Done => 4, + Phase::ResetIntent => 5, + }; + out[16..1121].copy_from_slice(&self.cursor); + self.source + .encode((&mut out[1128..1640]).try_into().unwrap()); + out[1640..1672].copy_from_slice(&self.context); + for (offset, value) in [ + (1672, self.physical_end), + (1680, self.committed_end), + (1688, self.sequence), + (1696, self.committed_sequence), + (1768, self.checkpoint_offset), + (1776, self.checkpoint_len), + (1792, self.pending_len), + ] { + put(&mut out[offset..], value); + } + out[1704..1736].copy_from_slice(&self.chain); + out[1736..1768].copy_from_slice(&self.committed_chain); + out[1784] = self.pending_kind.byte(); + out[1800..1832].copy_from_slice(&self.pending_digest); + out[1832..1864].copy_from_slice(&self.ack); + finish(out); + } + pub(crate) fn decode(bytes: &[u8]) -> Result { + if bytes.len() != HEADER_BYTES + || &bytes[..8] != b"CGLEG001" + || !checked(bytes) + || bytes[10..16] + .iter() + .chain(bytes[1121..1128].iter()) + .chain(bytes[1785..1792].iter()) + .chain(bytes[1864..2016].iter()) + .any(|b| *b != 0) + { + return Err(invalid()); + } + let h = Self { + kind: match bytes[8] { + 1 => LegacyStreamKind::Association, + 2 => LegacyStreamKind::Staging, + 3 => LegacyStreamKind::Backup, + _ => return Err(invalid()), + }, + phase: match bytes[9] { + 1 => Phase::Running, + 2 => Phase::AwaitCheckpoint, + 3 => Phase::AppendIntent, + 4 => Phase::Done, + 5 => Phase::ResetIntent, + _ => return Err(invalid()), + }, + cursor: bytes[16..1121].try_into().unwrap(), + source: StreamSource::decode(bytes[1128..1640].try_into().unwrap())?, + context: bytes[1640..1672].try_into().unwrap(), + physical_end: num(&bytes[1672..]), + committed_end: num(&bytes[1680..]), + sequence: num(&bytes[1688..]), + committed_sequence: num(&bytes[1696..]), + chain: bytes[1704..1736].try_into().unwrap(), + committed_chain: bytes[1736..1768].try_into().unwrap(), + checkpoint_offset: num(&bytes[1768..]), + checkpoint_len: num(&bytes[1776..]), + pending_kind: LegacyFrameKind::parse(bytes[1784])?, + pending_len: num(&bytes[1792..]), + pending_digest: bytes[1800..1832].try_into().unwrap(), + ack: bytes[1832..1864].try_into().unwrap(), + }; + if h.committed_end > h.physical_end + || h.committed_sequence > h.sequence + || h.pending_len > FRAME_LIMIT as u64 + // Native custody seals retain the complete original header plus + // retiring source; their authenticated checkpoint is 3968 bytes. + || (h.checkpoint_len > 2048 + && !(h.pending_kind == LegacyFrameKind::NativeCohort && h.checkpoint_len == 3968)) + || h.checkpoint_offset > h.committed_end + || (h.phase != Phase::AppendIntent + && (h.pending_len != 0 || h.pending_digest != [0; 32])) + { + return Err(invalid()); + } + Ok(h) + } +} +pub(crate) struct LegacyStreamFrame { + bytes: ReservedBytes, + kind: LegacyFrameKind, + sequence: u64, + context: [u8; 32], + previous: [u8; 32], + digest: [u8; 32], +} +impl LegacyStreamFrame { + pub(crate) fn new( + kind: LegacyFrameKind, + context: [u8; 32], + sequence: u64, + previous: [u8; 32], + payload: &[u8], + memory: &CleanupMemory, + ) -> Result { + let _floor = memory.reserve(8192).map_err(FixedControlError::Resource)?; + let len = payload + .len() + .checked_add(FRAME_PREFIX + FRAME_FOOTER) + .ok_or_else(invalid)?; + if len > FRAME_LIMIT { + return Err(invalid()); + } + kind.validate(payload)?; + let mut bytes = memory + .reserve(len + std::mem::size_of::()) + .map_err(FixedControlError::Resource)? + .bytes(len) + .map_err(FixedControlError::Resource)?; + let wire = bytes.as_mut_slice(); + wire.fill(0); + wire[..8].copy_from_slice(b"CGLGFR01"); + wire[8] = kind.byte(); + wire[16..48].copy_from_slice(&context); + put(&mut wire[48..], sequence); + put(&mut wire[56..], payload.len() as u64); + wire[64..96].copy_from_slice(&previous); + wire[96..96 + payload.len()].copy_from_slice(payload); + finish(wire); + let digest = wire[len - 32..].try_into().unwrap(); + Ok(Self { + bytes, + kind, + sequence, + context, + previous, + digest, + }) + } + fn read( + metadata: &PreparedPrivateMetadata, + offset: u64, + end: u64, + memory: &CleanupMemory, + ) -> Result { + let _floor = memory.reserve(256).map_err(FixedControlError::Resource)?; + let mut prefix = [0; FRAME_PREFIX]; + metadata.read_stream_exact(offset, &mut prefix)?; + if &prefix[..8] != b"CGLGFR01" || prefix[9..16].iter().any(|b| *b != 0) { + return Err(invalid()); + } + let payload = usize::try_from(num(&prefix[56..])).map_err(|_| invalid())?; + let len = payload + .checked_add(FRAME_PREFIX + FRAME_FOOTER) + .ok_or_else(invalid)?; + if len > FRAME_LIMIT || offset.checked_add(len as u64).is_none_or(|n| n > end) { + return Err(invalid()); + } + let mut bytes = memory + .reserve(len + std::mem::size_of::()) + .map_err(FixedControlError::Resource)? + .bytes(len) + .map_err(FixedControlError::Resource)?; + metadata.read_stream_exact(offset, bytes.as_mut_slice())?; + let wire = bytes.as_slice(); + if !checked(wire) || wire[..96] != prefix { + return Err(invalid()); + } + let kind = LegacyFrameKind::parse(wire[8])?; + kind.validate(&wire[96..len - 32])?; + let result = Self { + kind, + sequence: num(&wire[48..]), + context: wire[16..48].try_into().unwrap(), + previous: wire[64..96].try_into().unwrap(), + digest: wire[len - 32..].try_into().unwrap(), + bytes, + }; + Ok(result) + } + fn payload(&self) -> &[u8] { + &self.bytes.as_slice()[96..self.bytes.as_slice().len() - 32] + } +} +pub(crate) enum LegacyStreamAction { + Start { + cursor: [u8; CURSOR_BYTES], + source: LegacyClosedVersion, + context: [u8; 32], + kind: LegacyStreamKind, + }, + StartActive { + cursor: [u8; CURSOR_BYTES], + source: ActiveSourceSnapshot, + context: [u8; 32], + }, + BeginAppend(LegacyStreamFrame), + FinishAppend(LegacyStreamFrame), + Done, + BeginReset, + FinishReset, +} +pub(crate) enum CommittedLegacyStreamStep { + Intent(LegacyStreamHeader), + Data(LegacyStreamHeader), + Checkpoint { + header: LegacyStreamHeader, + ack: [u8; 32], + }, + Done(LegacyStreamHeader), + Reset, +} +pub(crate) struct PreparedLegacyStreamStep { + metadata: super::maintenance_budget::MemoryBox, + control: super::maintenance_budget::MemoryBox, + append: Option, + offset: u64, + reset: bool, + next: Option, + outcome: u8, + _floor: MemoryReservation, +} +fn start_cursor_matches( + payload: Option<&[u8]>, + scope: &RootOriginBinding, + cursor: &[u8; CURSOR_BYTES], +) -> bool { + let Some(payload) = payload else { + return false; + }; + super::maintenance_candidate::idle_control_cursor(payload, *scope) + .ok() + .flatten() + .is_some_and(|observed| observed.encode() == *cursor) +} +#[inline(never)] +pub(crate) fn prepare_legacy_stream_step( + target: &MaintenanceTarget, + scope: &RootOriginBinding, + action: LegacyStreamAction, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let metadata = boxed_source_metadata(target, scope, memory)?; + prepare_stream_step_from_metadata(metadata, scope, action, memory) +} + +// Metadata preparation completes before the header/action construction frame +// exists. The authenticated metadata stays charged and keeps the same handles. +#[inline(never)] +pub(super) fn prepare_stream_step_from_metadata( + metadata: super::maintenance_budget::MemoryBox, + scope: &RootOriginBinding, + action: LegacyStreamAction, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + if metadata.origin_scope() != *scope { + return Err(invalid()); + } + let floor = memory + .reserve(std::mem::size_of::() + HEADER_BYTES * 3 + 8192) + .map_err(FixedControlError::Resource)?; + let before = metadata.committed().ok_or_else(invalid)?; + if !matches!( + metadata.recovery(), + super::maintenance_controls::RecoveryStep::Committed(_) + ) { + return Err(invalid()); + } + let mut append = None; + let mut offset = 0; + let mut reset = false; + let mut outcome = 0; + let mut h = match &action { + LegacyStreamAction::Start { + cursor, + source, + context, + kind, + } => { + if metadata.stream_len() != 0 + || !start_cursor_matches(metadata.payload(), scope, cursor) + { + return Err(invalid()); + } + LegacyStreamHeader { + cursor: *cursor, + source: StreamSource::Closed(*source), + context: *context, + kind: *kind, + phase: Phase::Running, + physical_end: 0, + committed_end: 0, + sequence: 0, + committed_sequence: 0, + chain: [0; 32], + committed_chain: [0; 32], + checkpoint_offset: 0, + checkpoint_len: 0, + pending_kind: LegacyFrameKind::CacheCertificate, + pending_len: 0, + pending_digest: [0; 32], + ack: [0; 32], + } + } + LegacyStreamAction::StartActive { + cursor, + source, + context, + } => { + if metadata.stream_len() != 0 + || !start_cursor_matches(metadata.payload(), scope, cursor) + { + return Err(invalid()); + } + LegacyStreamHeader { + cursor: *cursor, + source: StreamSource::Active(*source), + context: *context, + kind: LegacyStreamKind::Backup, + phase: Phase::Running, + physical_end: 0, + committed_end: 0, + sequence: 0, + committed_sequence: 0, + chain: [0; 32], + committed_chain: [0; 32], + checkpoint_offset: 0, + checkpoint_len: 0, + pending_kind: LegacyFrameKind::CacheCertificate, + pending_len: 0, + pending_digest: [0; 32], + ack: [0; 32], + } + } + _ => LegacyStreamHeader::decode(metadata.payload().ok_or_else(invalid)?)?, + }; + match action { + LegacyStreamAction::Start { .. } | LegacyStreamAction::StartActive { .. } => {} + LegacyStreamAction::BeginAppend(frame) => { + if !(matches!(h.phase, Phase::Running | Phase::AwaitCheckpoint) + || h.phase == Phase::Done && frame.kind == LegacyFrameKind::NativeCohort) + || frame.sequence != h.sequence + || frame.context != h.context + || frame.previous != h.chain + { + return Err(invalid()); + } + if frame.kind == LegacyFrameKind::AssociationCheckpoint { + let checkpoint = + LegacyCheckpoint::decode(frame.payload().try_into().map_err(|_| invalid())?) + .map_err(|_| invalid())?; + if checkpoint.context_digest() != h.context || !h.source.matches(&checkpoint) { + return Err(invalid()); + } + } + if frame.kind == LegacyFrameKind::StagingSeal { + let seal = super::maintenance_legacy_staging::StagingAssociation::decode( + frame.payload().try_into().map_err(|_| invalid())?, + ) + .map_err(|_| invalid())?; + if seal.source_context() != h.source.digest() { + return Err(invalid()); + } + } + if frame.kind == LegacyFrameKind::StagingCheckpoint { + let checkpoint = StagingProjectionCheckpoint::decode( + frame.payload().try_into().map_err(|_| invalid())?, + ) + .map_err(|_| invalid())?; + if checkpoint.source_context() != h.source.digest() { + return Err(invalid()); + } + } + let association = matches!( + frame.kind, + LegacyFrameKind::CacheCertificate + | LegacyFrameKind::AssociationCheckpoint + | LegacyFrameKind::NativeCohort + ); + if association != (h.kind != LegacyStreamKind::Staging) { + return Err(invalid()); + } + if h.kind != LegacyStreamKind::Staging + && frame.kind != LegacyFrameKind::NativeCohort + && ((h.phase == Phase::Running && frame.kind != LegacyFrameKind::CacheCertificate) + || (h.phase == Phase::AwaitCheckpoint + && frame.kind != LegacyFrameKind::AssociationCheckpoint)) + { + return Err(invalid()); + } + h.phase = Phase::AppendIntent; + h.pending_kind = frame.kind; + h.pending_len = frame.bytes.as_slice().len() as u64; + h.pending_digest = frame.digest; + outcome = 1; + } + LegacyStreamAction::FinishAppend(frame) => { + if h.phase != Phase::AppendIntent + || frame.sequence != h.sequence + || frame.context != h.context + || frame.previous != h.chain + || frame.kind != h.pending_kind + || frame.digest != h.pending_digest + || frame.bytes.as_slice().len() as u64 != h.pending_len + { + return Err(invalid()); + } + offset = h.physical_end; + let end = offset.checked_add(h.pending_len).ok_or_else(invalid)?; + if metadata.stream_len() < offset || metadata.stream_len() > end { + return Err(invalid()); + } + h.physical_end = end; + h.sequence = h.sequence.checked_add(1).ok_or_else(invalid)?; + h.chain = frame.digest; + h.pending_len = 0; + h.pending_digest = [0; 32]; + if frame.kind.checkpoint() { + h.phase = Phase::Running; + h.committed_end = end; + h.committed_chain = h.chain; + h.committed_sequence = h.sequence; + h.checkpoint_offset = offset; + h.checkpoint_len = frame.payload().len() as u64; + outcome = 3; + if frame.kind == LegacyFrameKind::StagingCheckpoint { + h.ack = StagingProjectionCheckpoint::decode( + frame.payload().try_into().map_err(|_| invalid())?, + ) + .map_err(|_| invalid())? + .digest(); + } + } else { + h.phase = Phase::AwaitCheckpoint; + outcome = 2; + if frame.kind == LegacyFrameKind::CacheCertificate { + h.ack = LegacyCacheCertificate::decode( + frame.payload().try_into().map_err(|_| invalid())?, + ) + .map_err(|_| invalid())? + .digest() + .map_err(|_| invalid())?; + } + } + append = Some(frame); + } + LegacyStreamAction::Done => { + if h.phase != Phase::Running || h.physical_end != h.committed_end { + return Err(invalid()); + } + h.phase = Phase::Done; + outcome = 4; + } + LegacyStreamAction::BeginReset => { + if h.phase != Phase::Done { + return Err(invalid()); + } + validate_prefix(&metadata, &h, memory)?; + h.phase = Phase::ResetIntent; + outcome = 1; + } + LegacyStreamAction::FinishReset => { + if h.phase != Phase::ResetIntent { + return Err(invalid()); + } + if metadata.stream_len() == h.physical_end { + validate_prefix(&metadata, &h, memory)?; + reset = true; + offset = h.physical_end; + } else if metadata.stream_len() != 0 { + return Err(invalid()); + } + outcome = 5; + } + } + let mut image = [0; HEADER_BYTES]; + h.encode(&mut image); + let payload = if outcome == 5 { + &h.cursor[..] + } else { + &image[..] + }; + let control = boxed_stream_control(scope, before, payload, memory)?; + super::maintenance_budget::MemoryBox::new( + PreparedLegacyStreamStep { + metadata, + control, + append, + offset, + reset, + next: if outcome == 5 { None } else { Some(h) }, + outcome, + _floor: floor, + }, + memory, + ) + .map_err(FixedControlError::Resource) +} +#[inline(never)] +fn boxed_stream_control( + scope: &RootOriginBinding, + before: super::maintenance_controls::CommitFact, + payload: &[u8], + memory: &CleanupMemory, +) -> Result, FixedControlError> { + let control = admission::prepare_metadata_control(scope, before, payload, memory)?; + super::maintenance_budget::MemoryBox::new(control, memory).map_err(FixedControlError::Resource) +} + +impl PreparedLegacyStreamStep { + pub(crate) fn effects(&self) -> usize { + self.control.effects() + + if self.append.is_some() { + 3 + } else if self.reset { + 2 + } else { + 0 + } + } + pub(crate) fn advance( + &mut self, + grant: &mut MutationGrant, + ) -> Result { + self.advance_inner(None, grant) + } + /// The outer genuine source role must reserve effects() before acquiring + /// its guards. This borrows and verifies its actual exclusive root lease. + pub(crate) fn advance_with_owned_root( + &mut self, + root_use: &super::locks::FixedLockedFile, + grant: &mut MutationGrant, + ) -> Result { + self.advance_inner(Some(root_use), grant) + } + fn advance_inner( + &mut self, + root_use: Option<&super::locks::FixedLockedFile>, + grant: &mut MutationGrant, + ) -> Result { + if self.outcome == 255 { + return Err(invalid()); + } + let effects = self.effects(); + let append = &self.append; + let control = &self.control; + let offset = self.offset; + let reset = self.reset; + let mut apply = |mutation: &mut admission::PrivateMetadataMutation<'_>, + grant: &mut MutationGrant| { + if let Some(frame) = append { + mutation.append_frame(offset, frame.bytes.as_slice(), grant)?; + } + if reset { + mutation.reset_stream(offset, grant)?; + } + mutation.publish_control(control, grant) + }; + if let Some(root_use) = root_use { + admission::with_reserved_private_metadata_owned_root( + &mut self.metadata, + root_use, + grant, + effects, + &mut apply, + )?; + } else { + admission::with_private_metadata(&mut self.metadata, grant, effects, &mut apply)?; + } + let outcome = self.outcome; + self.outcome = 255; + if outcome == 5 { + return Ok(CommittedLegacyStreamStep::Reset); + } + let header = self.next.unwrap(); + Ok(match outcome { + 1 => CommittedLegacyStreamStep::Intent(header), + 2 => CommittedLegacyStreamStep::Data(header), + 3 => CommittedLegacyStreamStep::Checkpoint { + ack: header.ack, + header, + }, + 4 => CommittedLegacyStreamStep::Done(header), + _ => CommittedLegacyStreamStep::Data(header), + }) + } +} +fn scan( + metadata: &PreparedPrivateMetadata, + h: &LegacyStreamHeader, + memory: &CleanupMemory, + end: u64, + emit: &mut dyn FnMut(u64, &LegacyStreamFrame) -> Result<(), FixedControlError>, +) -> Result<(), FixedControlError> { + let mut offset = 0u64; + let mut sequence = 0u64; + let mut previous = [0; 32]; + while offset < end { + let frame = LegacyStreamFrame::read(metadata, offset, end, memory)?; + if frame.sequence != sequence || frame.previous != previous || frame.context != h.context { + return Err(invalid()); + } + emit(offset, &frame)?; + previous = frame.digest; + sequence = sequence.checked_add(1).ok_or_else(invalid)?; + offset = offset + .checked_add(frame.bytes.as_slice().len() as u64) + .ok_or_else(invalid)?; + } + if offset != end || sequence != h.committed_sequence || previous != h.committed_chain { + return Err(invalid()); + } + Ok(()) +} +fn validate_prefix( + metadata: &PreparedPrivateMetadata, + h: &LegacyStreamHeader, + memory: &CleanupMemory, +) -> Result<(), FixedControlError> { + if h.committed_end != h.physical_end { + return Err(invalid()); + } + scan(metadata, h, memory, h.committed_end, &mut |_, _| Ok(())) +} + +pub(crate) struct LegacyStreamReplay<'a> { + metadata: &'a PreparedPrivateMetadata, + header: LegacyStreamHeader, + memory: &'a CleanupMemory, + _floor: MemoryReservation, +} +impl<'a> LegacyStreamReplay<'a> { + pub(crate) fn new( + metadata: &'a PreparedPrivateMetadata, + expected: &LegacyClosedVersion, + context: [u8; 32], + memory: &'a CleanupMemory, + ) -> Result { + let floor = memory + .reserve(std::mem::size_of::() + 8192) + .map_err(FixedControlError::Resource)?; + let header = LegacyStreamHeader::decode(metadata.payload().ok_or_else(invalid)?)?; + if header.source.digest() != expected.context_digest() + || header.context != context + || metadata.stream_len() < header.committed_end + { + return Err(invalid()); + } + Ok(Self { + metadata, + header, + memory, + _floor: floor, + }) + } + pub(crate) fn new_active( + metadata: &'a PreparedPrivateMetadata, + expected: &ActiveSourceSnapshot, + context: [u8; 32], + memory: &'a CleanupMemory, + ) -> Result { + let floor = memory + .reserve(std::mem::size_of::() + 8192) + .map_err(FixedControlError::Resource)?; + let header = LegacyStreamHeader::decode(metadata.payload().ok_or_else(invalid)?)?; + if header.kind != LegacyStreamKind::Backup + || header.source.digest() != expected.context_digest() + || header.context != context + || metadata.stream_len() < header.committed_end + { + return Err(invalid()); + } + Ok(Self { + metadata, + header, + memory, + _floor: floor, + }) + } + pub(crate) fn checkpoint(&self) -> Result { + if self.header.checkpoint_len == 0 { + return Err(invalid()); + } + let frame = LegacyStreamFrame::read( + self.metadata, + self.header.checkpoint_offset, + self.header.committed_end, + self.memory, + )?; + if !frame.kind.checkpoint() + || frame.context != self.header.context + || frame.payload().len() as u64 != self.header.checkpoint_len + { + return Err(invalid()); + } + let mut bytes = self + .memory + .reserve(frame.payload().len()) + .map_err(FixedControlError::Resource)? + .bytes(frame.payload().len()) + .map_err(FixedControlError::Resource)?; + bytes.as_mut_slice().copy_from_slice(frame.payload()); + Ok(bytes) + } +} +fn legacy_error(e: FixedControlError) -> LegacyIssue { + match e { + FixedControlError::Resource(e) => LegacyIssue::Resource(e), + FixedControlError::Io(e) => LegacyIssue::Io(e.kind()), + _ => LegacyIssue::Unknown, + } +} +fn staging_error(e: FixedControlError) -> StagingIssue { + match e { + FixedControlError::Resource(e) => StagingIssue::Resource(e), + FixedControlError::Io(e) => StagingIssue::Io(e.kind()), + _ => StagingIssue::Unknown, + } +} +impl ReplayLegacyCertificates for LegacyStreamReplay<'_> { + fn replay( + &self, + memory: &CleanupMemory, + emit: &mut dyn FnMut(LegacyCacheCertificate) -> Result<(), LegacyIssue>, + ) -> Result<(), LegacyIssue> { + if self.header.kind == LegacyStreamKind::Staging { + return Err(LegacyIssue::Unknown); + } + // Preserve the consumer's precise error while the transport callback uses its + // own fixed control error type. No error string or unbounded result is kept. + let mut consumer = None; + let result = scan( + self.metadata, + &self.header, + memory, + self.header.committed_end, + &mut |_, frame| { + match frame.kind { + LegacyFrameKind::CacheCertificate => { + let cert = LegacyCacheCertificate::decode( + frame.payload().try_into().map_err(|_| invalid())?, + ) + .map_err(|_| invalid())?; + if let Err(e) = emit(cert) { + consumer = Some(e); + return Err(invalid()); + } + } + LegacyFrameKind::AssociationCheckpoint | LegacyFrameKind::NativeCohort => {} + _ => return Err(invalid()), + } + Ok(()) + }, + ); + if let Some(error) = consumer { + return Err(error); + } + result.map_err(legacy_error) + } +} +impl ReplayStagingCertificates for LegacyStreamReplay<'_> { + fn replay( + &self, + emit: &mut dyn FnMut(StagingReplayEvent) -> Result<(), StagingIssue>, + ) -> Result<(), StagingIssue> { + if self.header.kind != LegacyStreamKind::Staging { + return Err(StagingIssue::Unknown); + } + let mut start = 0u64; + let mut start_sequence = 0; + let mut start_previous = [0; 32]; + let mut consumer = None; + let result = scan( + self.metadata, + &self.header, + self.memory, + self.header.committed_end, + &mut |offset, frame| { + match frame.kind { + LegacyFrameKind::StagingRows + | LegacyFrameKind::StagingCheckpoint + | LegacyFrameKind::StagingSeal => {} + LegacyFrameKind::StagingPayload => { + let cert = StagingPayloadCertificate::decode( + frame.payload().try_into().map_err(|_| invalid())?, + ) + .map_err(|_| invalid())?; + if cert.source_context != self.header.source.digest() { + return Err(invalid()); + } + if let Err(e) = emit(StagingReplayEvent::Payload(cert)) { + consumer = Some(e); + return Err(invalid()); + } + let mut at = start; + let mut sequence = start_sequence; + let mut previous = start_previous; + while at < offset { + let item = + LegacyStreamFrame::read(self.metadata, at, offset, self.memory)?; + if item.context != self.header.context + || item.sequence != sequence + || item.previous != previous + { + return Err(invalid()); + } + match item.kind { + LegacyFrameKind::StagingRows => { + for row in item.payload().chunks_exact(768) { + let row = + StagingRowWitness::decode(row.try_into().unwrap()) + .map_err(|_| invalid())?; + if let Err(e) = emit(StagingReplayEvent::Row(row)) { + consumer = Some(e); + return Err(invalid()); + } + } + } + LegacyFrameKind::StagingCheckpoint => {} + _ => return Err(invalid()), + } + at = at + .checked_add(item.bytes.as_slice().len() as u64) + .ok_or_else(invalid)?; + sequence = sequence.checked_add(1).ok_or_else(invalid)?; + previous = item.digest; + } + if at != offset || sequence != frame.sequence || previous != frame.previous + { + return Err(invalid()); + } + if let Err(e) = emit(StagingReplayEvent::EndPayload) { + consumer = Some(e); + return Err(invalid()); + } + start = offset + .checked_add(frame.bytes.as_slice().len() as u64) + .ok_or_else(invalid)?; + start_sequence = frame.sequence.checked_add(1).ok_or_else(invalid)?; + start_previous = frame.digest; + } + _ => return Err(invalid()), + } + Ok(()) + }, + ); + if let Some(error) = consumer { + return Err(error); + } + result.map_err(staging_error) + } +} + +// Complete transfers the existing real source pair off authority; bounded preparation stages account for this fixed transport layout. +#[allow(clippy::large_enum_variant)] +pub(crate) enum AssociationSourceProgress { + Pending, + Step(super::maintenance_budget::MemoryBox), + Complete { + association: super::maintenance_legacy_association::LegacyAssociation, + database: std::fs::File, + manifest: std::fs::File, + }, +} +pub(super) struct AssociationTransportState { + source: StreamSource, + context: [u8; 32], + pending: Option, + checkpoint: Option, + completed: Option>, + reset_complete: bool, + _floor: MemoryReservation, +} +struct AssociationComplete { + association: super::maintenance_legacy_association::LegacyAssociation, + database: std::fs::File, + manifest: std::fs::File, + _floor: MemoryReservation, +} +fn source_equal(a: StreamSource, b: StreamSource) -> bool { + a.digest() == b.digest() +} +fn make_frame( + kind: LegacyFrameKind, + header: &LegacyStreamHeader, + transport: &AssociationTransportState, + memory: &CleanupMemory, +) -> Result { + let _floor = memory.reserve(8192).map_err(FixedControlError::Resource)?; + match kind { + LegacyFrameKind::CacheCertificate => { + let mut bytes = [0; 768]; + transport.pending.ok_or_else(invalid)?.encode(&mut bytes); + LegacyStreamFrame::new( + kind, + header.context, + header.sequence, + header.chain, + &bytes, + memory, + ) + } + LegacyFrameKind::AssociationCheckpoint => { + let mut bytes = [0; 2048]; + transport + .checkpoint + .as_ref() + .ok_or_else(invalid)? + .encode(&mut bytes); + LegacyStreamFrame::new( + kind, + header.context, + header.sequence, + header.chain, + &bytes, + memory, + ) + } + _ => Err(invalid()), + } +} +/// One advisory advance, or one prebound short stream mutation. The state is +/// parked on the joined preparation worker and never dropped under admission. +#[allow(clippy::too_many_arguments)] +pub(crate) fn prepare_association_source( + target: &MaintenanceTarget, + domain: &super::maintenance_ownership::ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + database: std::fs::File, + manifest: std::fs::File, + artifact_root: super::locks::RootedDirectory, + artifact_root_path: &std::path::Path, + repository_root: &std::path::Path, + owner: Option<&super::maintenance_legacy_bootstrap::LegacySourceOwner>, + backup: Option, + state: &mut super::maintenance_legacy_association::SelectedBackupAssociationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + if cancel() { + return Err(invalid()); + } + let scope = association_origin(target, domain, expected, memory)?; + prepare_association_source_inner( + target, + scope, + domain, + expected, + Some((database, manifest, artifact_root)), + artifact_root_path, + repository_root, + owner, + backup, + state, + memory, + cancel, + ) +} +#[allow(clippy::too_many_arguments)] +pub(crate) fn prepare_parked_association_source( + target: &MaintenanceTarget, + domain: &super::maintenance_ownership::ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + artifact_root_path: &std::path::Path, + repository_root: &std::path::Path, + owner: Option<&super::maintenance_legacy_bootstrap::LegacySourceOwner>, + backup: Option, + state: &mut super::maintenance_legacy_association::SelectedBackupAssociationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + if cancel() { + return Err(invalid()); + } + let scope = association_origin(target, domain, expected, memory)?; + prepare_association_source_inner( + target, + scope, + domain, + expected, + None, + artifact_root_path, + repository_root, + owner, + backup, + state, + memory, + cancel, + ) +} +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn prepare_association_source_inner( + target: &MaintenanceTarget, + scope: RootOriginBinding, + domain: &super::maintenance_ownership::ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + mut inputs: Option<(std::fs::File, std::fs::File, super::locks::RootedDirectory)>, + artifact_root_path: &std::path::Path, + repository_root: &std::path::Path, + owner: Option<&super::maintenance_legacy_bootstrap::LegacySourceOwner>, + backup: Option, + state: &mut super::maintenance_legacy_association::SelectedBackupAssociationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + let _floor = memory.reserve(65536).map_err(FixedControlError::Resource)?; + if cancel() { + return Err(invalid()); + } + let mut source = observe_association_source( + domain, + expected, + inputs.as_ref(), + artifact_root_path, + repository_root, + owner, + backup.is_some(), + state, + memory, + cancel, + )?; + let metadata = boxed_source_metadata(target, &scope, memory)?; + let observed = boxed_observed_header(&metadata, memory)?; + if let Some(header) = observed.as_ref() { + if let (StreamSource::Closed(original), StreamSource::Closed(actual)) = + (header.source, source) + { + if !actual.same_job_source(&original) { + return Err(invalid()); + } + source = StreamSource::Closed(original); + } + if !source_equal(header.source, source) + || (backup.is_some() != (header.kind == LegacyStreamKind::Backup)) + { + return Err(invalid()); + } + } else if super::maintenance_candidate::idle_control_cursor( + metadata.payload().ok_or_else(invalid)?, + scope, + ) + .map_err(|_| invalid())? + .is_none() + { + return Err(invalid()); + } + if let Some(transport) = state.transport.as_mut() { + if !source_equal(transport.source, source) { + return Err(invalid()); + } + if transport.reset_complete { + return take_association_completion(state); + } + } + if state.job.is_none() + && state + .transport + .as_ref() + .is_none_or(|t| t.completed.is_none()) + { + let checkpoint = if let Some(header) = observed.as_ref() { + let replay = match source { + StreamSource::Closed(original) => { + LegacyStreamReplay::new(&metadata, &original, header.context, memory)? + } + StreamSource::Active(original) => { + LegacyStreamReplay::new_active(&metadata, &original, header.context, memory)? + } + }; + replay.checkpoint().ok() + } else { + None + }; + let (database, manifest, artifact_root) = inputs.take().ok_or_else(invalid)?; + let job = boxed_source_job( + database, + manifest, + artifact_root, + artifact_root_path, + repository_root, + expected, + backup, + checkpoint.as_ref().map(|bytes| bytes.as_slice()), + memory, + )?; + let context = job + .checkpoint() + .map_err(association_error)? + .context_digest(); + if observed.as_ref().is_some_and(|h| h.context != context) { + return Err(invalid()); + } + state.job = Some(job); + if state.transport.is_none() { + state.transport = Some(AssociationTransportState { + source, + context, + pending: None, + checkpoint: None, + completed: None, + reset_complete: false, + _floor: memory + .reserve(std::mem::size_of::() + 8192) + .map_err(FixedControlError::Resource)?, + }); + } + } else { + if let Some((database, manifest, artifact_root)) = inputs.take() { + if let Some(job) = state.job.as_ref() { + if !job.source_matches( + &database, + &manifest, + &artifact_root, + artifact_root_path, + repository_root, + expected, + ) { + return Err(invalid()); + } + } + drop((database, manifest, artifact_root)); + } + } + if let (Some(job), Some(owner)) = (state.job.as_mut(), owner) { + job.authorize_closed_source(owner, memory) + .map_err(association_error)?; + } + if state + .transport + .as_ref() + .is_some_and(|transport| transport.completed.is_some()) + && (matches!(source, StreamSource::Closed(_)) || backup.is_some()) + && observed.as_ref().is_some_and(|header| header.done()) + { + return take_association_completion(state); + } + if let Some(action) = association_stream_action( + &metadata, + &scope, + source, + observed.as_deref(), + state.transport.as_ref().ok_or_else(invalid)?, + memory, + )? { + drop(metadata); + return Ok(AssociationSourceProgress::Step(boxed_association_step( + target, + &scope, + action.into_inner(), + memory, + )?)); + } + advance_association_producer(domain, owner, state, &metadata, source, memory, cancel)?; + // A preparation callback may be the last one before worker shutdown. Carry + // its new certificate or seal into the durable stream in this same callback + // rather than requiring a warm-only Pending callback to survive a restart. + if (matches!(source, StreamSource::Closed(_)) || backup.is_some()) + && observed.as_ref().is_some_and(|header| header.done()) + && state + .transport + .as_ref() + .is_some_and(|transport| transport.completed.is_some()) + { + return take_association_completion(state); + } + let action = association_stream_action( + &metadata, + &scope, + source, + observed.as_deref(), + state.transport.as_ref().ok_or_else(invalid)?, + memory, + )?; + if let Some(action) = action { + drop(metadata); + return Ok(AssociationSourceProgress::Step(boxed_association_step( + target, + &scope, + action.into_inner(), + memory, + )?)); + } + Ok(AssociationSourceProgress::Pending) +} + +// Decide and retain only the next short mutation, outside the large source +// preparation frame. No extra stream or payload descriptors are opened here. +#[inline(never)] +fn association_stream_action( + metadata: &PreparedPrivateMetadata, + scope: &RootOriginBinding, + source: StreamSource, + header: Option<&LegacyStreamHeader>, + transport: &AssociationTransportState, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + if header.is_none() { + if metadata.stream_len() != 0 { + return Err(invalid()); + } + let cursor = super::maintenance_candidate::idle_control_cursor( + metadata.payload().ok_or_else(invalid)?, + *scope, + ) + .map_err(|_| invalid())? + .ok_or_else(invalid)? + .encode(); + let action = match source { + StreamSource::Closed(source) => LegacyStreamAction::Start { + cursor, + source, + context: transport.context, + kind: LegacyStreamKind::Association, + }, + StreamSource::Active(source) => LegacyStreamAction::StartActive { + cursor, + source, + context: transport.context, + }, + }; + return super::maintenance_budget::MemoryBox::new(action, memory) + .map(Some) + .map_err(FixedControlError::Resource); + } + let header = header.unwrap(); + if transport.completed.is_some() { + let action = if header.resetting() { + LegacyStreamAction::FinishReset + } else if header.done() { + LegacyStreamAction::BeginReset + } else { + LegacyStreamAction::Done + }; + return super::maintenance_budget::MemoryBox::new(action, memory) + .map(Some) + .map_err(FixedControlError::Resource); + } + if transport.pending.is_some() { + let kind = if let Some(kind) = header.pending_kind() { + kind + } else if header.awaits_checkpoint() { + LegacyFrameKind::AssociationCheckpoint + } else { + LegacyFrameKind::CacheCertificate + }; + let frame = make_frame(kind, header, transport, memory)?; + let action = if header.pending() { + LegacyStreamAction::FinishAppend(frame) + } else { + LegacyStreamAction::BeginAppend(frame) + }; + return super::maintenance_budget::MemoryBox::new(action, memory) + .map(Some) + .map_err(FixedControlError::Resource); + } + Ok(None) +} + +#[inline(never)] +fn take_association_completion( + state: &mut super::maintenance_legacy_association::SelectedBackupAssociationState, +) -> Result { + let completed = state + .transport + .as_mut() + .ok_or_else(invalid)? + .completed + .take() + .ok_or_else(invalid)? + .into_inner(); + state.clear(); + Ok(AssociationSourceProgress::Complete { + association: completed.association, + database: completed.database, + manifest: completed.manifest, + }) +} + +// Keep native producer results and completion moves out of stream preparation's +// frame. These stages run on the same joined, off-admission worker. +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn advance_association_producer( + domain: &super::maintenance_ownership::ReferenceDomain, + owner: Option<&super::maintenance_legacy_bootstrap::LegacySourceOwner>, + state: &mut super::maintenance_legacy_association::SelectedBackupAssociationState, + metadata: &PreparedPrivateMetadata, + source: StreamSource, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result<(), FixedControlError> { + use super::maintenance_budget::MemoryBox; + use super::maintenance_legacy_association::LegacyProgress; + let transport = state.transport.as_mut().ok_or_else(invalid)?; + let replay = match source { + StreamSource::Closed(original) => { + LegacyStreamReplay::new(metadata, &original, transport.context, memory)? + } + StreamSource::Active(original) => { + LegacyStreamReplay::new_active(metadata, &original, transport.context, memory)? + } + }; + // Catalog/resume/end-of-cursor transitions validate no cache payload. + // Continue those finite stages until one CacheValidated result, then stop: + // every preparation still validates at most one actual cache payload. + loop { + if cancel() { + return Err(invalid()); + } + let progress = state + .job + .as_mut() + .ok_or_else(invalid)? + .advance_with_certificates(domain, memory, cancel, &replay) + .map_err(association_error)?; + if let Some(owner) = owner { + if !owner.is_current(memory)? { + return Err(invalid()); + } + } + match progress { + LegacyProgress::Pending => continue, + LegacyProgress::CacheValidated(cert) => { + transport.pending = Some(cert); + transport.checkpoint = Some( + state + .job + .as_ref() + .unwrap() + .checkpoint_after_cache() + .map_err(association_error)?, + ); + return Ok(()); + } + LegacyProgress::Complete(association) => { + let (database, manifest) = + state.job.take().unwrap().into_inner().into_source_handles(); + transport.completed = Some( + MemoryBox::new( + AssociationComplete { + association, + database, + manifest, + _floor: memory + .reserve(std::mem::size_of::()) + .map_err(FixedControlError::Resource)?, + }, + memory, + ) + .map_err(FixedControlError::Resource)?, + ); + return Ok(()); + } + } + } +} +fn association_error(error: LegacyIssue) -> FixedControlError { + match error { + LegacyIssue::Resource(e) => FixedControlError::Resource(e), + LegacyIssue::Io(e) => FixedControlError::Io(e.into()), + _ => invalid(), + } +} +/// Called only by the driver's offpermit finish path, after the exact prepared +/// mutation returned. This is where warm producer cursors are acknowledged. +pub(crate) fn finish_association_source_step( + state: &mut super::maintenance_legacy_association::SelectedBackupAssociationState, + result: CommittedLegacyStreamStep, +) -> Result<(), FixedControlError> { + let transport = state.transport.as_mut().ok_or_else(invalid)?; + match result { + CommittedLegacyStreamStep::Checkpoint { header, ack } => { + if header.context != transport.context || !source_equal(header.source, transport.source) + { + return Err(invalid()); + } + let cert = transport.pending.ok_or_else(invalid)?; + if cert.digest().map_err(association_error)? != ack { + return Err(invalid()); + } + state + .job + .as_mut() + .ok_or_else(invalid)? + .acknowledge_cache(ack) + .map_err(association_error)?; + transport.pending = None; + transport.checkpoint = None; + } + CommittedLegacyStreamStep::Reset => { + transport.reset_complete = true; + } + CommittedLegacyStreamStep::Intent(h) + | CommittedLegacyStreamStep::Data(h) + | CommittedLegacyStreamStep::Done(h) => { + if h.context != transport.context || !source_equal(h.source, transport.source) { + return Err(invalid()); + } + } + } + Ok(()) +} + +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn observe_association_source( + domain: &super::maintenance_ownership::ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + inputs: Option<&(std::fs::File, std::fs::File, super::locks::RootedDirectory)>, + artifact_root_path: &std::path::Path, + repository_root: &std::path::Path, + owner: Option<&super::maintenance_legacy_bootstrap::LegacySourceOwner>, + backup_selected: bool, + state: &mut super::maintenance_legacy_association::SelectedBackupAssociationState, + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + let source = if let Some((database, manifest, artifact_root)) = inputs { + if let Some(owner) = owner { + if !owner.is_current(memory)? { + return Err(invalid()); + } + let original = owner.closed_version(); + if original.database_stamp() + != Some(super::locks::FixedFileStamp::capture( + database, + super::locks::FixedControlKind::File, + )?) + || original.manifest_stamp() + != Some(super::locks::FixedFileStamp::capture( + manifest, + super::locks::FixedControlKind::File, + )?) + || original.cache_identity() != artifact_root.identity() + { + return Err(invalid()); + } + StreamSource::Closed(original) + } else { + if !backup_selected { + return Err(invalid()); + } + StreamSource::Active(ActiveSourceSnapshot::observe( + database, + manifest, + artifact_root, + domain, + expected, + memory, + )?) + } + } else { + let transport = state.transport.as_ref().ok_or_else(invalid)?; + if matches!(transport.source, StreamSource::Closed(_)) != owner.is_some() { + return Err(invalid()); + } + if let Some(owner) = owner { + if !owner.is_current(memory)? + || !match transport.source { + StreamSource::Closed(original) => { + owner.closed_version().same_job_source(&original) + } + _ => false, + } + { + return Err(invalid()); + } + } + if let Some(job) = state.job.as_mut() { + if let Some(owner) = owner { + job.authorize_closed_source(owner, memory) + .map_err(association_error)?; + } + job.check_parked_source( + domain, + expected, + artifact_root_path, + repository_root, + memory, + cancel, + ) + .map_err(association_error)?; + } else if let Some(done) = transport.completed.as_ref() { + done.association.database_stamp().recheck(&done.database)?; + done.association.manifest_stamp().recheck(&done.manifest)?; + if !super::maintenance_ownership::expected_reference_current(domain, expected, memory)? + { + return Err(invalid()); + } + } else { + return Err(invalid()); + } + transport.source + }; + Ok(source) +} + +#[inline(never)] +fn association_origin( + target: &MaintenanceTarget, + domain: &super::maintenance_ownership::ReferenceDomain, + expected: &super::maintenance_ownership::ReferenceEpoch, + memory: &CleanupMemory, +) -> Result { + let prepared = admission::prepare_canonical(target, domain, expected, memory)?; + Ok(prepared.origin_scope()) +} +// The authenticated header is retained across source setup and stream steps; +// keep its cursor image charged on the heap instead of copying it per branch. +#[inline(never)] +fn boxed_observed_header( + metadata: &PreparedPrivateMetadata, + memory: &CleanupMemory, +) -> Result>, FixedControlError> { + metadata + .payload() + .and_then(|bytes| LegacyStreamHeader::decode(bytes).ok()) + .map(|header| { + super::maintenance_budget::MemoryBox::new(header, memory) + .map_err(FixedControlError::Resource) + }) + .transpose() +} + +#[inline(never)] +fn boxed_source_metadata( + target: &MaintenanceTarget, + scope: &RootOriginBinding, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + admission::prepare_private_metadata_boxed(target, scope, memory) +} +#[inline(never)] +fn boxed_association_step( + target: &MaintenanceTarget, + scope: &RootOriginBinding, + action: LegacyStreamAction, + memory: &CleanupMemory, +) -> Result, FixedControlError> { + prepare_legacy_stream_step(target, scope, action, memory) +} + +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn boxed_source_job( + database: std::fs::File, + manifest: std::fs::File, + artifact_root: super::locks::RootedDirectory, + artifact_root_path: &std::path::Path, + repository_root: &std::path::Path, + expected: &super::maintenance_ownership::ReferenceEpoch, + backup: Option, + checkpoint: Option<&[u8]>, + memory: &CleanupMemory, +) -> Result< + super::maintenance_budget::MemoryBox< + super::maintenance_legacy_association::LegacyAssociationPreparation, + >, + FixedControlError, +> { + use super::maintenance_legacy_association::{LegacyAssociationPreparation, LegacyCheckpoint}; + let job = if let Some(bytes) = checkpoint { + LegacyAssociationPreparation::restore( + database, + manifest, + artifact_root, + artifact_root_path, + repository_root, + LegacyCheckpoint::decode(bytes.try_into().map_err(|_| invalid())?) + .map_err(association_error)?, + memory, + ) + .map_err(association_error)? + } else { + return boxed_new_source_job( + database, + manifest, + artifact_root, + artifact_root_path, + repository_root, + expected, + backup, + memory, + ); + }; + super::maintenance_budget::MemoryBox::new(job, memory).map_err(FixedControlError::Resource) +} +#[inline(never)] +#[allow(clippy::too_many_arguments)] +fn boxed_new_source_job( + database: std::fs::File, + manifest: std::fs::File, + artifact_root: super::locks::RootedDirectory, + artifact_root_path: &std::path::Path, + repository_root: &std::path::Path, + expected: &super::maintenance_ownership::ReferenceEpoch, + backup: Option, + memory: &CleanupMemory, +) -> Result< + super::maintenance_budget::MemoryBox< + super::maintenance_legacy_association::LegacyAssociationPreparation, + >, + FixedControlError, +> { + let mut job = super::maintenance_legacy_association::LegacyAssociationPreparation::new( + database, + manifest, + artifact_root, + artifact_root_path, + repository_root, + expected.clone(), + memory, + ) + .map_err(association_error)?; + if let Some(backup) = backup { + job.select_backup_cache(backup.key(), backup.name()) + .map_err(association_error)?; + } + super::maintenance_budget::MemoryBox::new(job, memory).map_err(FixedControlError::Resource) +} + +impl AssociationTransportState { + pub(super) fn has_completed(&self) -> bool { + self.completed.is_some() + } +} + +impl LegacyStreamReplay<'_> { + /// Reads only the seal from an already authenticated, complete private + /// prefix. The caller must additionally verify rooted source custody and + /// exact certificate membership before constructing an effect capability. + pub(crate) fn staging_seal( + &self, + ) -> Result, FixedControlError> + { + if self.header.kind != LegacyStreamKind::Staging || self.header.checkpoint_len != 512 { + return Ok(None); + } + scan( + self.metadata, + &self.header, + self.memory, + self.header.committed_end, + &mut |_, _| Ok(()), + )?; + let frame = LegacyStreamFrame::read( + self.metadata, + self.header.checkpoint_offset, + self.header.committed_end, + self.memory, + )?; + if frame.kind != LegacyFrameKind::StagingSeal || frame.context != self.header.context { + return Err(invalid()); + } + let seal = super::maintenance_legacy_staging::StagingAssociation::decode( + frame.payload().try_into().map_err(|_| invalid())?, + ) + .map_err(|_| invalid())?; + if seal.source_context() != self.header.source.digest() { + return Err(invalid()); + } + Ok(Some(seal)) + } +} + +impl LegacyStreamHeader { + pub(crate) fn before_native_cohort(&self) -> Result { + if self.kind != LegacyStreamKind::Association { + return Err(invalid()); + } + if self.phase == Phase::Done { + return Ok(*self); + } + if self.phase != Phase::AppendIntent || self.pending_kind != LegacyFrameKind::NativeCohort { + return Err(invalid()); + } + let mut original = *self; + original.phase = Phase::Done; + original.pending_kind = LegacyFrameKind::AssociationCheckpoint; + original.pending_len = 0; + original.pending_digest = [0; 32]; + Ok(original) + } +} +impl<'a> LegacyStreamReplay<'a> { + pub(crate) fn from_header( + metadata: &'a PreparedPrivateMetadata, + original: LegacyStreamHeader, + memory: &'a CleanupMemory, + ) -> Result { + if !original.done() || metadata.stream_len() < original.committed_end { + return Err(invalid()); + } + validate_prefix(metadata, &original, memory)?; + Ok(Self { + metadata, + header: original, + memory, + _floor: memory + .reserve(std::mem::size_of::() + 8192) + .map_err(FixedControlError::Resource)?, + }) + } +} diff --git a/src/storage/maintenance_native_projection.rs b/src/storage/maintenance_native_projection.rs new file mode 100644 index 00000000..c8b422cb --- /dev/null +++ b/src/storage/maintenance_native_projection.rs @@ -0,0 +1,2952 @@ +//! Finite native format facts only. These do not certify a producer profile, +//! database origin, loader usability, source stability or retirement authority. +use super::maintenance_budget::CleanupMemory; +use super::maintenance_validation::{ByteSource, ValidationIssue}; + +const PAGE_BYTES: u64 = 4096; +const HEADER_BYTES: usize = 49; +const INVALID_PAGE: u32 = u32::MAX; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct NativePageRange { + pub(crate) start: u32, + pub(crate) count: u32, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct NativeHeader43Facts { + pub(crate) catalog: NativePageRange, + pub(crate) metadata: NativePageRange, + pub(crate) database_id: [u8; 16], + pub(crate) data_file_pages: u32, + pub(crate) available_pages: u64, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum NativeUnknown { + Source(ValidationIssue), + UnsupportedLayout, + Truncated, + InvalidRange, + Overflow, + #[cfg(test)] + SourceChanged, + Cancelled, + UnsupportedField { + offset: u64, + tag: u64, + }, +} +impl From for NativeUnknown { + fn from(value: ValidationIssue) -> Self { + Self::Source(value) + } +} + +/// Parse only the selected storage43, no-debug, little-endian, 4096-byte-page, +/// format2 header layout. Its structure is not evidence that a program created +/// these bytes. Native page_idx_t is u32; the actual format2 prefix is49 bytes. +pub(crate) fn scan_native_header43( + source: &S, + account: &CleanupMemory, +) -> Result { + let floor = HEADER_BYTES + .checked_add(std::mem::size_of::()) + .and_then(|n| n.checked_add(128)) + .ok_or(NativeUnknown::Overflow)?; + let _reservation = account.reserve(floor).map_err(ValidationIssue::from)?; + let length = source.len(); + if length < HEADER_BYTES as u64 { + return Err(NativeUnknown::Truncated); + } + if length % PAGE_BYTES != 0 { + return Err(NativeUnknown::UnsupportedLayout); + } + let available_pages = length / PAGE_BYTES; + let mut bytes = [0; HEADER_BYTES]; + let mut at = 0; + while at < bytes.len() { + let count = source.read_at(at as u64, &mut bytes[at..])?; + if count == 0 || count > bytes.len() - at { + return Err(NativeUnknown::Truncated); + } + at = at.checked_add(count).ok_or(NativeUnknown::Overflow)?; + } + if &bytes[..4] != b"LBUG" + || u64::from_le_bytes(bytes[4..12].try_into().unwrap()) != 43 + || bytes[44] != 2 + { + return Err(NativeUnknown::UnsupportedLayout); + } + let word = |at: usize| u32::from_le_bytes(bytes[at..at + 4].try_into().unwrap()); + let catalog = NativePageRange { + start: word(12), + count: word(16), + }; + let metadata = NativePageRange { + start: word(20), + count: word(24), + }; + check_range(catalog, available_pages)?; + check_range(metadata, available_pages)?; + let data_file_pages = word(45); + if u64::from(data_file_pages) > available_pages { + return Err(NativeUnknown::InvalidRange); + } + Ok(NativeHeader43Facts { + catalog, + metadata, + database_id: bytes[28..44].try_into().unwrap(), + data_file_pages, + available_pages, + }) +} +fn check_range(range: NativePageRange, pages: u64) -> Result<(), NativeUnknown> { + if range.start == INVALID_PAGE { + return if range.count == 0 { + Ok(()) + } else { + Err(NativeUnknown::InvalidRange) + }; + } + let start = u64::from(range.start); + let count = u64::from(range.count); + if count == 0 || start >= pages || count > pages - start { + return Err(NativeUnknown::InvalidRange); + } + start + .checked_mul(PAGE_BYTES) + .and_then(|s| count.checked_mul(PAGE_BYTES).and_then(|n| s.checked_add(n))) + .ok_or(NativeUnknown::Overflow)?; + Ok(()) +} + +// Catalog-only checkpoint: no row/string-page decoding and no origin factory. +const PROFILE_TABLES: usize = 112; +const PROFILE_PROPERTIES: usize = 18; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum NativeDefaultFacts { + Null, + Text(super::maintenance_validation::Span), +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum NativeCatalogEvent { + BuiltinJsonType { + id: u64, + name: super::maintenance_validation::Span, + }, + Set { + number: u8, + next_id: u64, + count: u64, + }, + Table { + id: u64, + name: super::maintenance_validation::Span, + node: bool, + }, + Property { + table: u64, + id: u32, + name: super::maintenance_validation::Span, + logical: u8, + physical: u8, + default: NativeDefaultFacts, + }, + Column { + table: u64, + property: u32, + column: u32, + }, + RelationshipGroup { + table: u64, + source_multiplicity: u8, + target_multiplicity: u8, + direction: u8, + }, + RelationshipEndpoint { + table: u64, + source: u64, + target: u64, + inner: u64, + source_flag: bool, + target_flag: bool, + }, + Index { + entry: u64, + table: u64, + name: super::maintenance_validation::Span, + kind: super::maintenance_validation::Span, + property: u32, + }, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct NativeCatalogTraceFacts { + pub(crate) tables: u64, + pub(crate) nodes: u64, + pub(crate) indexes: u64, + pub(crate) consumed_bytes: u64, +} +#[derive(Clone, Copy, Default)] +struct PropertyCell { + id: u32, + mapped: bool, + column: u32, +} +#[derive(Clone, Copy)] +struct CatalogTableCell { + id: u64, + name: super::maintenance_validation::Span, + node: bool, + primary: u32, + index: u64, +} +struct CatalogCursor<'a, S: ByteSource + ?Sized> { + source: &'a S, + at: u64, + end: u64, + bytes: [u8; 256], + index: usize, + filled: usize, +} +impl<'a, S: ByteSource + ?Sized> CatalogCursor<'a, S> { + fn new(source: &'a S, start: u64, end: u64) -> Result { + if start > end || end > source.len() { + return Err(NativeUnknown::InvalidRange); + } + Ok(Self { + source, + at: start, + end, + bytes: [0; 256], + index: 0, + filled: 0, + }) + } + fn fail(&self, tag: u64) -> NativeUnknown { + NativeUnknown::UnsupportedField { + offset: self.at, + tag, + } + } + fn byte(&mut self) -> Result { + if self.at >= self.end { + return Err(NativeUnknown::Truncated); + } + if self.index == self.filled { + let n = usize::try_from((self.end - self.at).min(256)) + .map_err(|_| NativeUnknown::Overflow)?; + self.filled = self.source.read_at(self.at, &mut self.bytes[..n])?; + self.index = 0; + if self.filled == 0 || self.filled > n { + return Err(NativeUnknown::Truncated); + } + } + let b = self.bytes[self.index]; + self.index += 1; + self.at += 1; + Ok(b) + } + fn array(&mut self) -> Result<[u8; N], NativeUnknown> { + let mut out = [0; N]; + for b in &mut out { + *b = self.byte()?; + } + Ok(out) + } + fn u32(&mut self) -> Result { + Ok(u32::from_le_bytes(self.array()?)) + } + fn u64(&mut self) -> Result { + Ok(u64::from_le_bytes(self.array()?)) + } + fn flag(&mut self) -> Result { + match self.byte()? { + 0 => Ok(false), + 1 => Ok(true), + v => Err(self.fail(u64::from(v))), + } + } + fn skip(&mut self, n: u64) -> Result<(), NativeUnknown> { + self.at = self.at.checked_add(n).ok_or(NativeUnknown::Overflow)?; + if self.at > self.end { + return Err(NativeUnknown::Truncated); + } + self.index = 0; + self.filled = 0; + Ok(()) + } + fn text( + &mut self, + memory: &CleanupMemory, + ) -> Result { + let len = self.u64()?; + let span = super::maintenance_validation::Span { + start: self.at, + end: self.at.checked_add(len).ok_or(NativeUnknown::Overflow)?, + }; + if span.end > self.end { + return Err(NativeUnknown::Truncated); + } + super::maintenance_validation::key_stream_hash( + self.source, + span, + super::maintenance_validation::KeyEncoding::NativeUtf8, + 0, + memory, + )?; + self.skip(len)?; + Ok(span) + } + fn matches( + &self, + span: super::maintenance_validation::Span, + text: &[u8], + ) -> Result { + if span.end - span.start != text.len() as u64 { + return Ok(false); + } + for (i, b) in text.iter().enumerate() { + let mut out = [0]; + if self.source.read_at(span.start + i as u64, &mut out)? != 1 { + return Err(NativeUnknown::Truncated); + } + if out[0] != *b { + return Ok(false); + } + } + Ok(true) + } + fn concat_matches( + &self, + span: super::maintenance_validation::Span, + prefix: &[u8], + middle: super::maintenance_validation::Span, + suffix: &[u8], + ) -> Result { + let len = (prefix.len() as u64) + .checked_add(middle.end - middle.start) + .and_then(|n| n.checked_add(suffix.len() as u64)) + .ok_or(NativeUnknown::Overflow)?; + if span.end - span.start != len { + return Ok(false); + } + let start = span.start + prefix.len() as u64; + let end = start + middle.end - middle.start; + if !self.matches( + super::maintenance_validation::Span { + start: span.start, + end: start, + }, + prefix, + )? || !self.matches( + super::maintenance_validation::Span { + start: end, + end: span.end, + }, + suffix, + )? { + return Ok(false); + } + for i in 0..middle.end - middle.start { + let mut a = [0]; + let mut b = [0]; + if self.source.read_at(start + i, &mut a)? != 1 + || self.source.read_at(middle.start + i, &mut b)? != 1 + { + return Err(NativeUnknown::Truncated); + } + if a != b { + return Ok(false); + } + } + Ok(true) + } + fn primitive(&mut self) -> Result<(u8, u8), NativeUnknown> { + let logical = self.byte()?; + let physical = self.byte()?; + let category = self.byte()?; + if category != 0 + || !matches!( + (logical, physical), + (0, 0) + | (22, 1) + | (23, 2) + | (27, 6) + | (28, 7) + | (30, 9) + | (32, 11) + | (42, 14) + | (50, 20) + | (60, 21) + ) + { + return Err(self.fail( + (u64::from(logical) << 16) | (u64::from(physical) << 8) | u64::from(category), + )); + } + Ok((logical, physical)) + } + fn literal( + &mut self, + memory: &CleanupMemory, + expected: (u8, u8), + ) -> Result { + if self.byte()? != 70 { + return Err(self.fail(70)); + } + let alias = self.text(memory)?; + if alias.start != alias.end { + return Err(self.fail(71)); + } + let raw = self.text(memory)?; + if self.u64()? != 0 { + return Err(self.fail(72)); + } + let (logical, physical) = self.primitive()?; + let null = self.flag()?; + if self.u32()? != 0 { + return Err(self.fail(73)); + } + if null { + if !self.matches(raw, b"NULL")? + || ((logical, physical) != (0, 0) && (logical, physical) != expected) + { + return Err(self.fail(74)); + } + // Binder::resolvePropertyDefault creates typed NULLs. Value's native + // serializer still writes the physical payload; unused numeric bits + // have no null semantics and are not required to be zero or finite. + match physical { + 0 => {} + 1 | 9 => self.skip(1)?, + 7 => self.skip(4)?, + 2 | 6 | 11 => self.skip(8)?, + 14 => self.skip(16)?, + 20 | 21 => { + let text = self.text(memory)?; + if text.start != text.end { + return Err(self.fail(86)); + } + } + _ => return Err(self.fail(74)), + } + return Ok(NativeDefaultFacts::Null); + } + if (logical, physical) == (50, 20) && expected == (50, 20) { + Ok(NativeDefaultFacts::Text(self.text(memory)?)) + } else { + Err(self.fail(74)) + } + } +} +/// Complete supported catalog trace only. Returned events borrow no buffers; +/// sink-owned results, if any, must also be charged by that caller's SAME account. +/// Prefix events must be discarded if any later field or source read fails. +/// This deliberately stops before primitive storage/string DATA interpretation. +pub(crate) fn trace_native_catalog43( + source: &S, + memory: &CleanupMemory, + emit: &mut impl FnMut(NativeCatalogEvent) -> Result<(), NativeUnknown>, +) -> Result { + let header = scan_native_header43(source, memory)?; + if header.catalog.start == INVALID_PAGE { + return Err(NativeUnknown::UnsupportedLayout); + } + let start = u64::from(header.catalog.start) + .checked_mul(PAGE_BYTES) + .ok_or(NativeUnknown::Overflow)?; + let end = u64::from(header.catalog.count) + .checked_mul(PAGE_BYTES) + .and_then(|n| start.checked_add(n)) + .ok_or(NativeUnknown::Overflow)?; + let floor = std::mem::size_of::>() + .checked_add(std::mem::size_of::<[CatalogTableCell; PROFILE_TABLES]>()) + .and_then(|n| n.checked_add(std::mem::size_of::<[PropertyCell; PROFILE_PROPERTIES]>())) + .and_then(|n| n.checked_add(512)) + .ok_or(NativeUnknown::Overflow)?; + let _reservation = memory.reserve(floor).map_err(ValidationIssue::from)?; + let mut reader = CatalogCursor::new(source, start, end)?; + let mut tables = [CatalogTableCell { + id: u64::MAX, + name: super::maintenance_validation::Span::default(), + node: false, + primary: u32::MAX, + index: u64::MAX, + }; PROFILE_TABLES]; + let mut nodes = 0; + let mut indexes = 0; + for set in 0..10u8 { + let next_id = reader.u64()?; + let count = reader.u64()?; + emit(NativeCatalogEvent::Set { + number: set, + next_id, + count, + })?; + if set == 0 && count != PROFILE_TABLES as u64 { + return Err(reader.fail(count)); + } + if set == 4 && count > nodes { + return Err(reader.fail(count)); + } + if set == 3 && count != 1 { + return Err(reader.fail(count)); + } + if set != 0 && set != 3 && set != 4 && count != 0 { + return Err(reader.fail(count)); + } + for ordinal in 0..count { + let tag = reader.byte()?; + let name = reader.text(memory)?; + let id = reader.u64()?; + let parent = reader.flag()?; + if id >= next_id || parent { + return Err(reader.fail(id)); + } + if set == 3 { + if tag != 41 + || id != 0 + || !reader.matches(name, b"JSON")? + || reader.primitive()? != (60, 21) + { + return Err(reader.fail(87)); + } + emit(NativeCatalogEvent::BuiltinJsonType { id, name })?; + continue; + } + if set == 4 { + if tag != 42 { + return Err(reader.fail(u64::from(tag))); + } + let kind = reader.text(memory)?; + if !reader.matches(kind, b"HASH")? { + return Err(reader.fail(42)); + } + let table = reader.u64()?; + if !tables.iter().any(|v| v.id == table && v.node) { + return Err(reader.fail(table)); + } + let index_name = reader.text(memory)?; + if reader.u64()? != 1 { + return Err(reader.fail(43)); + } + let property = reader.u32()?; + let aux = reader.u64()?; + if aux != 0 { + return Err(reader.fail(aux)); + } + let slot = tables + .iter() + .position(|v| v.id == table) + .ok_or(reader.fail(table))?; + if tables[slot].index != u64::MAX + || tables.iter().any(|v| v.index == id) + || property != tables[slot].primary + { + return Err(reader.fail(83)); + } + if !reader.concat_matches(index_name, b"pk_", tables[slot].name, b"_id")? { + return Err(reader.fail(84)); + } + let mut prefix = [0; 21]; + prefix[20] = b'_'; + let mut cursor = 20; + let mut number = table; + loop { + cursor -= 1; + prefix[cursor] = b'0' + (number % 10) as u8; + number /= 10; + if number == 0 { + break; + } + } + if !reader.concat_matches(name, &prefix[cursor..], index_name, b"")? { + return Err(reader.fail(85)); + } + tables[slot].index = id; + emit(NativeCatalogEvent::Index { + entry: id, + table, + name: index_name, + kind, + property, + })?; + indexes += 1; + continue; + } + if set != 0 || !matches!(tag, 0 | 2) { + return Err(reader.fail(u64::from(tag))); + } + if tables[..ordinal as usize].iter().any(|v| v.id == id) { + return Err(reader.fail(id)); + } + tables[ordinal as usize].id = id; + tables[ordinal as usize].name = name; + let node = tag == 0; + tables[ordinal as usize].node = node; + if node { + nodes += 1 + } + emit(NativeCatalogEvent::Table { id, name, node })?; + let comment = reader.text(memory)?; + if comment.start != comment.end { + return Err(reader.fail(75)); + } + let next_column = reader.u32()?; + let next_property = reader.u32()?; + let definitions = reader.u64()?; + if definitions > PROFILE_PROPERTIES as u64 + || next_property > PROFILE_PROPERTIES as u32 + || next_column > PROFILE_PROPERTIES as u32 + 1 + { + return Err(reader.fail(definitions)); + } + let mut properties = [PropertyCell::default(); PROFILE_PROPERTIES]; + for p in 0..definitions { + let property = reader.u32()?; + if property >= next_property + || properties[..p as usize].iter().any(|v| v.id == property) + { + return Err(reader.fail(u64::from(property))); + } + let property_name = reader.text(memory)?; + let (logical, physical) = reader.primitive()?; + let default = reader.literal(memory, (logical, physical))?; + properties[p as usize] = PropertyCell { + id: property, + mapped: false, + column: u32::MAX, + }; + if reader.matches(property_name, b"id")? { + tables[ordinal as usize].primary = property; + } + emit(NativeCatalogEvent::Property { + table: id, + id: property, + name: property_name, + logical, + physical, + default, + })?; + } + let mappings = reader.u64()?; + if mappings != definitions { + return Err(reader.fail(mappings)); + } + for _ in 0..mappings { + let property = reader.u32()?; + let column = reader.u32()?; + if properties[..definitions as usize] + .iter() + .any(|v| v.mapped && v.column == column) + { + return Err(reader.fail(u64::from(column))); + } + let Some(cell) = properties[..definitions as usize] + .iter_mut() + .find(|v| v.id == property) + else { + return Err(reader.fail(u64::from(property))); + }; + if cell.mapped || column >= next_column { + return Err(reader.fail(u64::from(column))); + } + cell.mapped = true; + cell.column = column; + emit(NativeCatalogEvent::Column { + table: id, + property, + column, + })?; + } + if node { + let primary = reader.text(memory)?; + if !reader.matches(primary, b"id")? { + return Err(reader.fail(76)); + } + if reader.u64()? != 0 { + return Err(reader.fail(77)); + } + let storage = reader.text(memory)?; + if storage.start != storage.end || reader.byte()? != 0 { + return Err(reader.fail(78)); + } + } else { + let src = reader.byte()?; + let dst = reader.byte()?; + let direction = reader.byte()?; + if src > 1 || dst > 1 || direction > 2 { + return Err(reader.fail(79)); + } + emit(NativeCatalogEvent::RelationshipGroup { + table: id, + source_multiplicity: src, + target_multiplicity: dst, + direction, + })?; + let storage = reader.text(memory)?; + if storage.start != storage.end || reader.flag()? { + return Err(reader.fail(80)); + } + let infos = reader.u64()?; + for _ in 0..infos { + let source = reader.u64()?; + let target = reader.u64()?; + let inner = reader.u64()?; + let source_flag = reader.flag()?; + let target_flag = reader.flag()?; + emit(NativeCatalogEvent::RelationshipEndpoint { + table: id, + source, + target, + inner, + source_flag, + target_flag, + })?; + } + if reader.byte()? != 0 { + return Err(reader.fail(82)); + } + } + } + } + Ok(NativeCatalogTraceFacts { + tables: PROFILE_TABLES as u64, + nodes, + indexes, + consumed_bytes: reader.at - start, + }) +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct NativePrimitiveMetadata { + pub(crate) page: u32, + pub(crate) pages: u32, + pub(crate) values: u64, + pub(crate) min: [u8; 16], + pub(crate) max: [u8; 16], + pub(crate) compression: u8, +} +const _: [(); 56] = [(); std::mem::size_of::()]; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum NativeMetadataEvent { + Table { + id: u64, + groups: u64, + }, + Group { + table: u64, + group: u64, + columns: u64, + rows: u64, + start: u64, + }, + Chunk { + table: u64, + group: u64, + column: u64, + segment: u64, + logical: u8, + physical: u8, + metadata: NativePrimitiveMetadata, + }, + Index { + table: u64, + name: super::maintenance_validation::Span, + kind: super::maintenance_validation::Span, + column: u32, + key_type: u8, + primary: bool, + builtin: bool, + storage_bytes: u64, + }, +} +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub(crate) struct NativeMetadataTraceFacts { + pub(crate) tables: u64, + pub(crate) groups: u64, + pub(crate) chunks: u64, + pub(crate) indexes: u64, + pub(crate) consumed_bytes: u64, +} +impl CatalogCursor<'_, S> { + fn compression_metadata( + &mut self, + available: u64, + allow_alp: bool, + ) -> Result { + let page = self.u32()?; + let pages = self.u32()?; + let values = self.u64()?; + let min = self.array()?; + let max = self.array()?; + let compression = self.byte()?; + if compression > 4 || (!allow_alp && compression == 4) { + return Err(self.fail(90)); + } + // Metadata ranges are physical framing facts. Constant/parent-string chunks + // may have no physical DATA pages. Actual scalar readers impose their own + // mode/width/page/numValues requirements before any DATA access. + if pages != 0 { + check_range( + NativePageRange { + start: page, + count: pages, + }, + available, + )?; + } + if compression == 4 { + self.skip(10)?; // exp:u8,fac:u8,exceptionCount:u32,exceptionCapacity:u32 + let _child_min = self.array::<16>()?; + let _child_max = self.array::<16>()?; + let child = self.byte()?; + if !matches!(child, 0 | 1 | 3) { + return Err(self.fail(91)); + } + } + Ok(NativePrimitiveMetadata { + page, + pages, + values, + min, + max, + compression, + }) + } + fn chunk_data( + &mut self, + available: u64, + depth: u8, + ) -> Result<(u8, u8, NativePrimitiveMetadata), NativeUnknown> { + if depth > 1 { + return Err(self.fail(92)); + } + let (logical, physical) = self.primitive()?; + let metadata = self.compression_metadata(available, physical == 11)?; + let _compression_enabled = self.flag()?; + let has_null = self.flag()?; + if has_null { + let _null = self.compression_metadata(available, false)?; + } + if matches!(physical, 20 | 21) { + if depth != 0 { + return Err(self.fail(93)); + } + let (index_l, index_p, index) = self.chunk_data(available, 1)?; + let (offset_l, offset_p, _offset) = self.chunk_data(available, 1)?; + let (data_l, data_p, _data) = self.chunk_data(available, 1)?; + if (index_l, index_p) != (28, 7) + || (offset_l, offset_p) != (27, 6) + || (data_l, data_p) != (30, 9) + || index.values != metadata.values + { + return Err(self.fail(94)); + } + } + Ok((logical, physical, metadata)) + } + fn versions(&mut self) -> Result<(), NativeUnknown> { + if !self.flag()? { + return Ok(()); + } + let count = self.u64()?; + for _ in 0..count { + if !self.flag()? { + continue; + } + let insert = self.byte()?; + let deletion = self.byte()?; + if !matches!(insert, 0 | 2) || deletion > 1 { + return Err(self.fail(95)); + } + if deletion == 1 { + let same = self.u64()?; + if same == u64::MAX { + for _ in 0..2048 { + let value = self.u64()?; + if value != u64::MAX && value >= (1u64 << 63) { + return Err(self.fail(96)); + } + } + } else if same >= (1u64 << 63) { + return Err(self.fail(97)); + } + } + } + // A missing trailing vector entry is implicitly inserted/not deleted; no + // vector-count equality with ceil(row_count/2048) is asserted here. + Ok(()) + } +} +/// First-checkpoint metadata framing only: no string DATA/row extraction, +/// physical index loading, origin, source-stability or usability claim. +pub(crate) fn trace_native_metadata43( + source: &S, + memory: &CleanupMemory, + emit: &mut impl FnMut(NativeMetadataEvent) -> Result<(), NativeUnknown>, +) -> Result { + let floor = std::mem::size_of::<[u64; PROFILE_TABLES]>() + .checked_add(std::mem::size_of::<[bool; PROFILE_TABLES]>()) + .and_then(|n| n.checked_add(std::mem::size_of::>())) + .and_then(|n| n.checked_add(5 * std::mem::size_of::())) + .and_then(|n| n.checked_add(std::mem::size_of::())) + .and_then(|n| n.checked_add(512)) + .ok_or(NativeUnknown::Overflow)?; + let _reservation = memory.reserve(floor).map_err(ValidationIssue::from)?; + let mut nodes = [u64::MAX; PROFILE_TABLES]; + let mut next = 0; + let catalog = trace_native_catalog43(source, memory, &mut |event| { + if let NativeCatalogEvent::Table { id, node: true, .. } = event { + nodes[next] = id; + next += 1; + } + Ok(()) + })?; + let header = scan_native_header43(source, memory)?; + if header.metadata.start == INVALID_PAGE { + return Err(NativeUnknown::UnsupportedLayout); + } + let start = u64::from(header.metadata.start) + .checked_mul(PAGE_BYTES) + .ok_or(NativeUnknown::Overflow)?; + let end = u64::from(header.metadata.count) + .checked_mul(PAGE_BYTES) + .and_then(|n| start.checked_add(n)) + .ok_or(NativeUnknown::Overflow)?; + let mut reader = CatalogCursor::new(source, start, end)?; + let tables = reader.u64()?; + if tables != catalog.nodes { + return Err(reader.fail(98)); + } + let mut seen = [false; PROFILE_TABLES]; + let mut facts = NativeMetadataTraceFacts { + tables, + ..Default::default() + }; + for _ in 0..tables { + let table = reader.u64()?; + let Some(slot) = nodes[..next].iter().position(|id| *id == table) else { + return Err(reader.fail(table)); + }; + if seen[slot] { + return Err(reader.fail(99)); + } + seen[slot] = true; + let groups = reader.u64()?; + facts.groups = facts + .groups + .checked_add(groups) + .ok_or(NativeUnknown::Overflow)?; + emit(NativeMetadataEvent::Table { id: table, groups })?; + for _ in 0..groups { + let group = reader.u64()?; + let _compression = reader.flag()?; + if reader.byte()? != 0 { + return Err(reader.fail(100)); + } + let ready = reader.flag()?; + if !ready { + emit(NativeMetadataEvent::Group { + table, + group, + columns: 0, + rows: 0, + start: 0, + })?; + continue; + } + let columns = reader.u64()?; + if columns > PROFILE_PROPERTIES as u64 { + return Err(reader.fail(columns)); + } + let mut rows = None; + for column in 0..columns { + let _compression_enabled = reader.flag()?; + let segments = reader.u64()?; + let mut total = 0u64; + for segment in 0..segments { + let (logical, physical, metadata) = + reader.chunk_data(header.available_pages, 0)?; + total = total + .checked_add(metadata.values) + .ok_or(NativeUnknown::Overflow)?; + facts.chunks = facts.chunks.checked_add(1).ok_or(NativeUnknown::Overflow)?; + emit(NativeMetadataEvent::Chunk { + table, + group, + column, + segment, + logical, + physical, + metadata, + })?; + } + if rows.is_some_and(|n| n != total) { + return Err(reader.fail(101)); + } + rows = Some(total); + } + let start = reader.u64()?; + reader.versions()?; + emit(NativeMetadataEvent::Group { + table, + group, + columns, + rows: rows.unwrap_or(0), + start, + })?; + } + let _cardinality = reader.u64()?; + let stats = reader.u64()?; + for _ in 0..stats { + if reader.flag()? { + reader.skip(64)?; + } + } + let indexes = reader.u64()?; + facts.indexes = facts + .indexes + .checked_add(indexes) + .ok_or(NativeUnknown::Overflow)?; + for _ in 0..indexes { + let name = reader.text(memory)?; + let kind = reader.text(memory)?; + if !reader.matches(kind, b"HASH")? || reader.u64()? != table || reader.u64()? != 1 { + return Err(reader.fail(102)); + } + let column = reader.u32()?; + if reader.u64()? != 1 { + return Err(reader.fail(103)); + } + let key_type = reader.byte()?; + let primary = reader.flag()?; + let builtin = reader.flag()?; + let storage_bytes = reader.u64()?; + reader.skip(storage_bytes)?; + emit(NativeMetadataEvent::Index { + table, + name, + kind, + column, + key_type, + primary, + builtin, + storage_bytes, + })?; + } + } + facts.consumed_bytes = reader.at - start; + Ok(facts) +} + +// Selected strings are resolved before a callback and contain no group-local +// references. The only retained stream is the dictionary's unsigned byte DATA. +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct NativeStringLocator { + source: u8, + compression: u8, + bits: u8, + reserved: [u8; 5], + page: u32, + pages: u32, + values: u64, + min: u64, + max: u64, + pub(crate) start: u64, + pub(crate) length: u64, +} +const _: [(); 56] = [(); std::mem::size_of::()]; + +struct NativeDataScratch { + page: [u8; 4096], + tag: Option, +} +impl NativeDataScratch { + fn new() -> Self { + Self { + page: [0; 4096], + tag: None, + } + } + fn read( + &mut self, + source: &S, + page: u32, + offset: usize, + out: &mut [u8], + ) -> Result<(), NativeUnknown> { + if offset.checked_add(out.len()).is_none_or(|n| n > 4096) { + return Err(NativeUnknown::InvalidRange); + } + if self.tag != Some(page) { + self.tag = None; + let base = u64::from(page) + .checked_mul(PAGE_BYTES) + .ok_or(NativeUnknown::Overflow)?; + let mut done = 0; + while done < self.page.len() { + let n = source.read_at(base + done as u64, &mut self.page[done..])?; + if n == 0 || n > self.page.len() - done { + return Err(NativeUnknown::Truncated); + } + done += n; + } + self.tag = Some(page); + } + out.copy_from_slice(&self.page[offset..offset + out.len()]); + Ok(()) + } +} +fn unsigned_bounds(m: NativePrimitiveMetadata, bits: u8) -> Result<(u64, u64), NativeUnknown> { + if !matches!(bits, 1 | 8 | 32 | 64) { + return Err(NativeUnknown::UnsupportedLayout); + } + let bytes = usize::from(bits.div_ceil(8)); + if m.min[bytes..].iter().any(|v| *v != 0) || m.max[bytes..].iter().any(|v| *v != 0) { + return Err(NativeUnknown::UnsupportedLayout); + } + let min = u64::from_le_bytes(m.min[..8].try_into().unwrap()); + let max = u64::from_le_bytes(m.max[..8].try_into().unwrap()); + if min > max || (bits == 1 && max > 1) { + return Err(NativeUnknown::InvalidRange); + } + Ok((min, max)) +} +fn scalar( + source: &S, + scratch: &mut NativeDataScratch, + m: NativePrimitiveMetadata, + bits: u8, + ordinal: u64, +) -> Result { + if ordinal >= m.values { + return Err(NativeUnknown::InvalidRange); + } + let (min, max) = unsigned_bounds(m, bits)?; + let (width, offset) = match m.compression { + 3 => { + return if min == max { + Ok(min) + } else { + Err(NativeUnknown::InvalidRange) + } + } + 0 if bits != 1 => (u32::from(bits), 0), + 2 if bits == 1 => (1, 0), + 1 if bits != 1 => { + let full = 64 - max.leading_zeros(); + let delta = 64 - (max - min).leading_zeros(); + if min > 0 && delta < full { + (delta, min) + } else { + (full, 0) + } + } + _ => return Err(NativeUnknown::UnsupportedLayout), + }; + // Equal-value producer chunks use CONSTANT; a zero-width packed mode has + // not been observed from the qualified writer and is kept unknown. + if width == 0 + || width > u32::from(bits) + || m.page == INVALID_PAGE + || m.page == 0 + || m.pages == 0 + { + return Err(NativeUnknown::InvalidRange); + } + let per_page = PAGE_BYTES * 8 / u64::from(width); + let page_index = ordinal / per_page; + if page_index >= u64::from(m.pages) { + return Err(NativeUnknown::InvalidRange); + } + let page = m + .page + .checked_add(u32::try_from(page_index).map_err(|_| NativeUnknown::Overflow)?) + .ok_or(NativeUnknown::Overflow)?; + let bit = (ordinal % per_page) * u64::from(width); + let shift = (bit % 8) as u32; + let bytes = ((shift + width).div_ceil(8)) as usize; + let mut encoded = [0u8; 12]; + scratch.read(source, page, (bit / 8) as usize, &mut encoded[..bytes])?; + let mut assembled = 0u128; + for (i, b) in encoded[..bytes].iter().enumerate() { + assembled |= u128::from(*b) << (i * 8); + } + let mask = (1u128 << width) - 1; + let value = u64::try_from((assembled >> shift) & mask) + .map_err(|_| NativeUnknown::Overflow)? + .checked_add(offset) + .ok_or(NativeUnknown::Overflow)?; + // Uncompressed metadata may carry no meaningful extrema, but compressed + // ranges define the frame and must cover each decoded scalar. + if m.compression != 0 && (value < min || value > max) { + return Err(NativeUnknown::InvalidRange); + } + Ok(value) +} +impl NativeStringLocator { + fn metadata(self) -> NativePrimitiveMetadata { + let mut min = [0; 16]; + let mut max = [0; 16]; + min[..8].copy_from_slice(&self.min.to_le_bytes()); + max[..8].copy_from_slice(&self.max.to_le_bytes()); + NativePrimitiveMetadata { + page: self.page, + pages: self.pages, + values: self.values, + min, + max, + compression: self.compression, + } + } +} +/// A logical UTF8 source with an independent DATA cache. Caller retains the +/// immutable source incarnation; this wrapper neither opens nor owns a path. +pub(crate) struct NativeStringSource<'a, S: ByteSource + ?Sized> { + source: &'a S, + locator: NativeStringLocator, + scratch: std::cell::RefCell, + _floor: super::maintenance_budget::MemoryReservation, +} +impl<'a, S: ByteSource + ?Sized> NativeStringSource<'a, S> { + pub(crate) fn new( + source: &'a S, + locator: NativeStringLocator, + memory: &CleanupMemory, + ) -> Result { + if locator.source != 0 + || locator.bits != 8 + || locator.reserved != [0; 5] + || locator + .start + .checked_add(locator.length) + .is_none_or(|end| end > locator.values) + { + return Err(NativeUnknown::InvalidRange); + } + let floor = memory + .reserve(std::mem::size_of::()) + .map_err(ValidationIssue::from)?; + Ok(Self { + source, + locator, + scratch: std::cell::RefCell::new(NativeDataScratch::new()), + _floor: floor, + }) + } +} +impl ByteSource for NativeStringSource<'_, S> { + fn len(&self) -> u64 { + self.locator.length + } + fn read_at(&self, offset: u64, out: &mut [u8]) -> Result { + let remaining = self + .locator + .length + .checked_sub(offset) + .ok_or(ValidationIssue::InvalidSpan)?; + let n = remaining.min(out.len() as u64) as usize; + let mut scratch = self.scratch.borrow_mut(); + for (i, b) in out[..n].iter_mut().enumerate() { + *b = scalar( + self.source, + &mut scratch, + self.locator.metadata(), + 8, + self.locator.start + offset + i as u64, + ) + .map_err(native_validation_issue)? as u8; + } + Ok(n) + } +} +fn native_validation_issue(e: NativeUnknown) -> ValidationIssue { + match e { + NativeUnknown::Source(e) => e, + NativeUnknown::Overflow => ValidationIssue::Overflow, + _ => ValidationIssue::Schema, + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum NativeSelectedTable { + Repository, + SourceRoot, + File, + Contains, +} +impl NativeSelectedTable { + fn fields(self) -> [&'static [u8]; 5] { + match self { + Self::Repository | Self::SourceRoot => [b"id", b"label", b"kind", b"path", b"metadata"], + Self::File => [b"id", b"kind", b"path", b"content_hash", b"metadata"], + Self::Contains => [b"id", b"kind", b"source_id", b"target_id", b"metadata"], + } + } +} +#[derive(Debug, Clone, Copy)] +pub(crate) struct NativeSelectedRow { + pub(crate) table: NativeSelectedTable, + pub(crate) ordinal: u64, + /// SQL NULL is None; an empty string is Some(locator with length zero). + pub(crate) fields: [Option; 5], + /// Quoted JSON string span in the logical metadata field, if present. + pub(crate) canonical_key: Option, +} +#[derive(Clone, Copy)] +struct SelectedMap { + table: u64, + kind: NativeSelectedTable, + properties: [u32; 5], + columns: [u32; 5], +} +#[derive(Clone, Copy)] +struct StringSegment { + logical: u8, + physical: u8, + parent: NativePrimitiveMetadata, + null: Option, + index: NativePrimitiveMetadata, + offsets: NativePrimitiveMetadata, + data: NativePrimitiveMetadata, +} +impl CatalogCursor<'_, S> { + fn string_segment(&mut self, available: u64) -> Result { + let ty = self.primitive()?; + if !matches!(ty, (50, 20) | (60, 21)) { + return Err(self.fail(110)); + } + let parent = self.compression_metadata(available, false)?; + self.flag()?; + let null = if self.flag()? { + Some(self.compression_metadata(available, false)?) + } else { + None + }; + let mut child = |expected| -> Result { + if self.primitive()? != expected { + return Err(self.fail(111)); + } + let m = self.compression_metadata(available, false)?; + self.flag()?; + if self.flag()? { + return Err(self.fail(112)); + } + Ok(m) + }; + let index = child((28, 7))?; + let offsets = child((27, 6))?; + let data = child((30, 9))?; + if index.values != parent.values || null.is_some_and(|n| n.values != parent.values) { + return Err(self.fail(113)); + } + Ok(StringSegment { + logical: ty.0, + physical: ty.1, + parent, + null, + index, + offsets, + data, + }) + } +} +fn check_selected_segment_ranges( + segment: StringSegment, + header: NativeHeader43Facts, +) -> Result<(), NativeUnknown> { + for m in [ + Some(segment.parent), + segment.null, + Some(segment.index), + Some(segment.offsets), + Some(segment.data), + ] + .into_iter() + .flatten() + { + if m.pages != 0 { + if m.page == 0 { + return Err(NativeUnknown::InvalidRange); + } + let start = u64::from(m.page); + let end = start + u64::from(m.pages); + for range in [header.catalog, header.metadata] { + let r = u64::from(range.start); + if start < r + u64::from(range.count) && r < end { + return Err(NativeUnknown::InvalidRange); + } + } + } + } + Ok(()) +} +struct ColumnCursor<'a, S: ByteSource + ?Sized> { + reader: CatalogCursor<'a, S>, + remaining: u64, + end_row: u64, + start_row: u64, + current: Option, +} +impl<'a, S: ByteSource + ?Sized> ColumnCursor<'a, S> { + fn new( + source: &'a S, + span: super::maintenance_validation::Span, + ) -> Result { + let mut reader = CatalogCursor::new(source, span.start, span.end)?; + reader.flag()?; + let remaining = reader.u64()?; + Ok(Self { + reader, + remaining, + end_row: 0, + start_row: 0, + current: None, + }) + } + fn value( + &mut self, + row: u64, + header: NativeHeader43Facts, + scratch: &mut NativeDataScratch, + ) -> Result, NativeUnknown> { + while self.current.is_none() || row >= self.end_row { + if self.remaining == 0 { + return Err(self.reader.fail(114)); + } + self.current = Some( + self.reader + .string_segment(u64::from(header.data_file_pages))?, + ); + self.remaining -= 1; + self.start_row = self.end_row; + self.end_row = self + .end_row + .checked_add(self.current.unwrap().parent.values) + .ok_or(NativeUnknown::Overflow)?; + } + let segment = self.current.unwrap(); + check_selected_segment_ranges(segment, header)?; + let ordinal = row + .checked_sub(self.start_row) + .ok_or(NativeUnknown::InvalidRange)?; + if let Some(null) = segment.null { + if scalar(self.reader.source, scratch, null, 1, ordinal)? != 0 { + return Ok(None); + } + } + let index = scalar(self.reader.source, scratch, segment.index, 32, ordinal)?; + if index >= segment.offsets.values { + return Err(self.reader.fail(115)); + } + let start = scalar(self.reader.source, scratch, segment.offsets, 64, index)?; + let end = if index + 1 < segment.offsets.values { + scalar(self.reader.source, scratch, segment.offsets, 64, index + 1)? + } else { + segment.data.values + }; + if start > end || end > segment.data.values { + return Err(self.reader.fail(116)); + } + let (min, max) = unsigned_bounds(segment.data, 8)?; + Ok(Some(NativeStringLocator { + source: 0, + compression: segment.data.compression, + bits: 8, + reserved: [0; 5], + page: segment.data.page, + pages: segment.data.pages, + values: segment.data.values, + min, + max, + start, + length: end - start, + })) + } +} +struct VersionCursor<'a, S: ByteSource + ?Sized> { + reader: CatalogCursor<'a, S>, + vectors: u64, + insert: bool, + deletion: u8, + same: u64, +} +impl<'a, S: ByteSource + ?Sized> VersionCursor<'a, S> { + fn new(source: &'a S, start: u64, end: u64) -> Result { + let mut reader = CatalogCursor::new(source, start, end)?; + let vectors = if reader.flag()? { reader.u64()? } else { 0 }; + Ok(Self { + reader, + vectors, + insert: true, + deletion: 0, + same: 0, + }) + } + fn visible(&mut self, row: u64) -> Result { + if row % 2048 == 0 { + self.insert = true; + self.deletion = 0; + self.same = 0; + if row / 2048 < self.vectors && self.reader.flag()? { + self.insert = self.reader.byte()? == 2; + self.deletion = self.reader.byte()?; + if self.deletion == 1 { + self.same = self.reader.u64()?; + } + } + } + let deleted = if self.deletion == 0 { + false + } else if self.same == u64::MAX { + self.reader.u64()? != u64::MAX + } else { + true + }; + Ok(self.insert && !deleted) + } +} + +struct RowStringSource<'a, 'b, S: ByteSource + ?Sized> { + source: &'a S, + locator: NativeStringLocator, + scratch: &'b std::cell::RefCell, +} +impl ByteSource for RowStringSource<'_, '_, S> { + fn len(&self) -> u64 { + self.locator.length + } + fn read_at(&self, offset: u64, out: &mut [u8]) -> Result { + let n = self + .locator + .length + .checked_sub(offset) + .ok_or(ValidationIssue::InvalidSpan)? + .min(out.len() as u64) as usize; + let mut scratch = self.scratch.borrow_mut(); + for (i, b) in out[..n].iter_mut().enumerate() { + *b = scalar( + self.source, + &mut scratch, + self.locator.metadata(), + 8, + self.locator.start + offset + i as u64, + ) + .map_err(native_validation_issue)? as u8; + } + Ok(n) + } +} +fn validate_logical_utf8( + source: &impl ByteSource, + memory: &CleanupMemory, +) -> Result<(), NativeUnknown> { + super::maintenance_validation::key_stream_hash( + source, + super::maintenance_validation::Span { + start: 0, + end: source.len(), + }, + super::maintenance_validation::KeyEncoding::NativeUtf8, + 0, + memory, + )?; + Ok(()) +} +fn logical_matches(source: &impl ByteSource, text: &[u8]) -> Result { + if source.len() != text.len() as u64 { + return Ok(false); + } + let mut byte = [0]; + for (i, b) in text.iter().enumerate() { + if source.read_at(i as u64, &mut byte)? != 1 { + return Err(NativeUnknown::Truncated); + } + if byte[0] != *b { + return Ok(false); + } + } + Ok(true) +} +struct LogicalRead<'a, S: ByteSource + ?Sized> { + source: &'a S, + at: u64, + error: &'a std::cell::Cell>, +} +impl std::io::Read for LogicalRead<'_, S> { + fn read(&mut self, out: &mut [u8]) -> std::io::Result { + let n = self.source.read_at(self.at, out).map_err(|e| { + self.error.set(Some(e)); + // The tokenizer retries Interrupted as ordinary I/O; a source's + // cancellation/stale error is terminal and must retain its type. + std::io::Error::from(std::io::ErrorKind::InvalidData) + })?; + self.at = self.at.checked_add(n as u64).ok_or_else(|| { + self.error.set(Some(ValidationIssue::Overflow)); + std::io::Error::from(std::io::ErrorKind::InvalidData) + })?; + Ok(n) + } +} +fn metadata_canonical_key( + source: &impl ByteSource, + memory: &CleanupMemory, +) -> Result, NativeUnknown> { + use super::maintenance_json::{MaintenanceJsonReader, TokenKind}; + use super::maintenance_validation::{compare_native_json, Span}; + let source_error = std::cell::Cell::new(None); + let mut reader = MaintenanceJsonReader::new( + LogicalRead { + source, + at: 0, + error: &source_error, + }, + memory, + ) + .map_err(ValidationIssue::from)?; + let mut depth = 0u64; + let mut canonical = false; + let mut result = None; + while let Some(token) = reader.next_token().map_err(|e| { + NativeUnknown::Source( + source_error + .take() + .unwrap_or_else(|| ValidationIssue::from(e)), + ) + })? { + if canonical { + if !matches!(token.kind, TokenKind::String { key: false, .. }) || result.is_some() { + return Err(NativeUnknown::UnsupportedLayout); + } + result = Some(Span { + start: token.start, + end: token.end, + }); + canonical = false; + } + match token.kind { + TokenKind::ObjectStart => { + depth += 1; + if depth == 1 && token.start != 0 { + return Err(NativeUnknown::UnsupportedLayout); + } + } + TokenKind::ArrayStart => { + if depth == 0 { + return Err(NativeUnknown::UnsupportedLayout); + } + depth += 1; + } + TokenKind::ObjectEnd | TokenKind::ArrayEnd => { + depth = depth.checked_sub(1).ok_or(NativeUnknown::InvalidRange)?; + } + TokenKind::String { key: true, .. } if depth == 1 => { + canonical = compare_native_json( + b"canonical_key".as_slice(), + Span { start: 0, end: 13 }, + source, + Span { + start: token.start, + end: token.end, + }, + memory, + )? == std::cmp::Ordering::Equal; + } + _ if depth == 0 => return Err(NativeUnknown::UnsupportedLayout), + _ => {} + } + } + if depth != 0 || canonical { + return Err(NativeUnknown::UnsupportedLayout); + } + Ok(result) +} + +/// Visit the four selected NODE tables. Framing and DATA facts are revoked by +/// any later error. `check` must enforce cancellation and source-incarnation +/// stability; it runs before/after every pass and before every emitted row. +/// Table/schema provenance and cache association remain separate consumers. +pub(crate) fn scan_native_selected43( + source: &S, + memory: &CleanupMemory, + check: &mut impl FnMut() -> Result<(), NativeUnknown>, + emit: &mut impl FnMut(NativeSelectedRow) -> Result<(), NativeUnknown>, +) -> Result { + use super::maintenance_validation::Span; + check()?; + let floor = std::mem::size_of::<[SelectedMap; 4]>() + .checked_add(std::mem::size_of::<[Option>; 5]>()) + .and_then(|n| n.checked_add(std::mem::size_of::>())) + .and_then(|n| n.checked_add(std::mem::size_of::>())) + .and_then(|n| n.checked_add(std::mem::size_of::())) + .and_then(|n| n.checked_add(std::mem::size_of::() * 2)) + .and_then(|n| n.checked_add(std::mem::size_of::() * 2)) + .and_then(|n| n.checked_add(std::mem::size_of::() * 5)) + .and_then(|n| n.checked_add(std::mem::size_of::<[Span; 5]>() + 512)) + .ok_or(NativeUnknown::Overflow)?; + let _floor = memory.reserve(floor).map_err(ValidationIssue::from)?; + let mut maps = std::array::from_fn::<_, 4, _>(|i| SelectedMap { + table: u64::MAX, + kind: [ + NativeSelectedTable::Repository, + NativeSelectedTable::SourceRoot, + NativeSelectedTable::File, + NativeSelectedTable::Contains, + ][i], + properties: [u32::MAX; 5], + columns: [u32::MAX; 5], + }); + let header = scan_native_header43(source, memory)?; + let matcher = CatalogCursor::new(source, 0, source.len())?; + trace_native_catalog43(source, memory, &mut |event| { + match event { + NativeCatalogEvent::Table { + id, + name, + node: true, + } => { + for (i, n) in [ + b"Repository".as_slice(), + b"SourceRoot", + b"File", + b"Contains", + ] + .iter() + .enumerate() + { + if matcher.matches(name, n)? { + if maps[i].table != u64::MAX { + return Err(NativeUnknown::UnsupportedLayout); + } + maps[i].table = id; + } + } + } + NativeCatalogEvent::Property { + table, + id, + name, + logical, + physical, + .. + } => { + if let Some(m) = maps.iter_mut().find(|m| m.table == table) { + for (i, n) in m.kind.fields().iter().enumerate() { + if matcher.matches(name, n)? { + if (logical, physical) != if i == 4 { (60, 21) } else { (50, 20) } + || m.properties[i] != u32::MAX + { + return Err(NativeUnknown::UnsupportedLayout); + } + m.properties[i] = id; + } + } + } + } + NativeCatalogEvent::Column { + table, + property, + column, + } => { + if let Some(m) = maps.iter_mut().find(|m| m.table == table) { + for (i, p) in m.properties.iter().enumerate() { + if *p == property { + m.columns[i] = column; + } + } + } + } + _ => {} + } + Ok(()) + })?; + if maps + .iter() + .any(|m| m.table == u64::MAX || m.columns.contains(&u32::MAX)) + { + return Err(NativeUnknown::UnsupportedLayout); + } + // Complete framing validates duplicate/missing metadata tables and all + // version trailers before any selected row callback can be accepted. + trace_native_metadata43(source, memory, &mut |_| check())?; + check()?; + if header.catalog.start == 0 + || header.metadata.start == 0 + || header.data_file_pages == 0 + || u64::from(header.catalog.start) + u64::from(header.catalog.count) + > u64::from(header.data_file_pages) + || u64::from(header.metadata.start) + u64::from(header.metadata.count) + > u64::from(header.data_file_pages) + { + return Err(NativeUnknown::InvalidRange); + } + let a = u64::from(header.catalog.start); + let b = u64::from(header.metadata.start); + if a < b + u64::from(header.metadata.count) && b < a + u64::from(header.catalog.count) { + return Err(NativeUnknown::InvalidRange); + } + let start = u64::from(header.metadata.start) * PAGE_BYTES; + let end = start + u64::from(header.metadata.count) * PAGE_BYTES; + let mut reader = CatalogCursor::new(source, start, end)?; + let tables = reader.u64()?; + let mut emitted = 0u64; + let scratch = std::cell::RefCell::new(NativeDataScratch::new()); + for _ in 0..tables { + check()?; + let table = reader.u64()?; + let groups = reader.u64()?; + let selected = maps.iter().find(|m| m.table == table).copied(); + for _ in 0..groups { + check()?; + reader.u64()?; + reader.flag()?; + if reader.byte()? != 0 { + return Err(reader.fail(117)); + } + if !reader.flag()? { + continue; + } + let columns = reader.u64()?; + let mut spans = [Span::default(); 5]; + let mut rows = 0; + for column in 0..columns { + let begin = reader.at; + reader.flag()?; + let segments = reader.u64()?; + let mut total = 0u64; + for _ in 0..segments { + let field = selected + .and_then(|m| m.columns.iter().position(|c| u64::from(*c) == column)); + let values = if let Some(field) = field { + let segment = reader.string_segment(u64::from(header.data_file_pages))?; + let expected = if field == 4 { (60, 21) } else { (50, 20) }; + if (segment.logical, segment.physical) != expected { + return Err(reader.fail(121)); + } + check_selected_segment_ranges(segment, header)?; + segment.parent.values + } else { + reader.chunk_data(header.available_pages, 0)?.2.values + }; + total = total.checked_add(values).ok_or(NativeUnknown::Overflow)?; + } + rows = total; + if let Some(m) = selected { + for (i, c) in m.columns.iter().enumerate() { + if u64::from(*c) == column { + spans[i] = Span { + start: begin, + end: reader.at, + }; + } + } + } + } + let row_start = reader.u64()?; + let version_start = reader.at; + reader.versions()?; + let resume = reader.at; + if let Some(m) = selected { + if spans.iter().any(|s| s.start == s.end) { + return Err(reader.fail(118)); + } + let mut cursors = std::array::from_fn::<_, 5, _>(|_| None); + for i in 0..5 { + cursors[i] = Some(ColumnCursor::new(source, spans[i])?); + } + let mut versions = VersionCursor::new(source, version_start, resume)?; + for row in 0..rows { + check()?; + let visible = versions.visible(row)?; + if !visible { + continue; + } + let mut fields = [None; 5]; + if m.kind == NativeSelectedTable::Contains { + fields[1] = cursors[1].as_mut().unwrap().value( + row, + header, + &mut scratch.borrow_mut(), + )?; + let Some(kind) = fields[1] else { + return Err(reader.fail(119)); + }; + let logical = RowStringSource { + source, + locator: kind, + scratch: &scratch, + }; + validate_logical_utf8(&logical, memory)?; + if !logical_matches(&logical, b"repository_source_root")? + && !logical_matches(&logical, b"source_root_file")? + { + continue; + } + } + for i in 0..5 { + if fields[i].is_none() { + fields[i] = cursors[i].as_mut().unwrap().value( + row, + header, + &mut scratch.borrow_mut(), + )?; + } + } + for locator in fields.iter().flatten() { + validate_logical_utf8( + &RowStringSource { + source, + locator: *locator, + scratch: &scratch, + }, + memory, + )?; + } + let canonical_key = if let Some(locator) = fields[4] { + metadata_canonical_key( + &RowStringSource { + source, + locator, + scratch: &scratch, + }, + memory, + )? + } else { + None + }; + if canonical_key.is_none() { + return Err(reader.fail(120)); + } + emit(NativeSelectedRow { + table: m.kind, + ordinal: row_start.checked_add(row).ok_or(NativeUnknown::Overflow)?, + fields, + canonical_key, + })?; + emitted = emitted.checked_add(1).ok_or(NativeUnknown::Overflow)?; + } + } + } + reader.u64()?; + let stats = reader.u64()?; + for _ in 0..stats { + if reader.flag()? { + reader.skip(64)?; + } + } + let indexes = reader.u64()?; + for _ in 0..indexes { + reader.text(memory)?; + reader.text(memory)?; + reader.u64()?; + let n = reader.u64()?; + for _ in 0..n { + reader.u32()?; + } + let n = reader.u64()?; + reader.skip(n)?; + reader.flag()?; + reader.flag()?; + let n = reader.u64()?; + reader.skip(n)?; + } + } + check()?; + Ok(emitted) +} + +#[cfg(test)] +mod tests { + use super::*; + fn header() -> Vec { + let mut bytes = vec![0; 3 * PAGE_BYTES as usize]; + bytes[..4].copy_from_slice(b"LBUG"); + bytes[4..12].copy_from_slice(&43u64.to_le_bytes()); + for (at, value) in [(12, 1u32), (16, 1), (20, 2), (24, 1), (45, 3)] { + bytes[at..at + 4].copy_from_slice(&value.to_le_bytes()); + } + bytes[28..44].copy_from_slice(&[0x55; 16]); + bytes[44] = 2; + bytes + } + #[test] + fn dc_native_header43_exact_layout_ranges_and_account() { + let memory = CleanupMemory::new(4096).unwrap(); + let floor = memory.used(); + let bytes = header(); + let facts = scan_native_header43(bytes.as_slice(), &memory).unwrap(); + assert_eq!(facts.catalog, NativePageRange { start: 1, count: 1 }); + assert_eq!(facts.metadata, NativePageRange { start: 2, count: 1 }); + assert_eq!(facts.database_id, [0x55; 16]); + assert_eq!(facts.data_file_pages, 3); + assert_eq!(memory.used(), floor); + let small = CleanupMemory::new(64).unwrap(); + assert!(matches!( + scan_native_header43(bytes.as_slice(), &small), + Err(NativeUnknown::Source(ValidationIssue::Resource(_))) + )); + } + #[test] + fn dc_native_header43_truncation_sentinel_tail_and_range_rejections() { + let memory = CleanupMemory::new(4096).unwrap(); + let bytes = header(); + for n in 0..HEADER_BYTES { + assert_eq!( + scan_native_header43(&bytes[..n], &memory), + Err(NativeUnknown::Truncated) + ); + } + for (at, value) in [ + (12, u32::MAX), + (16, 0), + (16, u32::MAX), + (20, 3), + (24, 2), + (45, 4), + ] { + let mut invalid = bytes.clone(); + invalid[at..at + 4].copy_from_slice(&value.to_le_bytes()); + assert_eq!( + scan_native_header43(invalid.as_slice(), &memory), + Err(NativeUnknown::InvalidRange) + ); + } + let mut empty = bytes.clone(); + for at in [12, 20] { + empty[at..at + 4].copy_from_slice(&u32::MAX.to_le_bytes()); + empty[at + 4..at + 8].fill(0); + } + assert!(scan_native_header43(empty.as_slice(), &memory).is_ok()); + for at in [0, 4, 44] { + let mut invalid = bytes.clone(); + invalid[at] ^= 1; + assert_eq!( + scan_native_header43(invalid.as_slice(), &memory), + Err(NativeUnknown::UnsupportedLayout) + ); + } + assert_eq!( + scan_native_header43(&bytes[..bytes.len() - 1], &memory), + Err(NativeUnknown::UnsupportedLayout) + ); + } + #[test] + fn dc_native_header43_short_reads_and_inconsistent_sources() { + struct Short<'a> { + bytes: &'a [u8], + stop: usize, + } + impl ByteSource for Short<'_> { + fn len(&self) -> u64 { + self.bytes.len() as u64 + } + fn read_at(&self, offset: u64, out: &mut [u8]) -> Result { + let at = offset as usize; + if at >= self.stop { + return Ok(0); + } + let n = out.len().min(3).min(self.stop - at); + out[..n].copy_from_slice(&self.bytes[at..at + n]); + Ok(n) + } + } + let memory = CleanupMemory::new(4096).unwrap(); + let bytes = header(); + assert!(scan_native_header43( + &Short { + bytes: &bytes, + stop: 49 + }, + &memory + ) + .is_ok()); + for stop in 0..49 { + assert_eq!( + scan_native_header43( + &Short { + bytes: &bytes, + stop + }, + &memory + ), + Err(NativeUnknown::Truncated) + ); + } + } + fn put64(bytes: &mut Vec, value: u64) { + bytes.extend(value.to_le_bytes()); + } + fn put32(bytes: &mut Vec, value: u32) { + bytes.extend(value.to_le_bytes()); + } + fn put_text(bytes: &mut Vec, text: &str) { + put64(bytes, text.len() as u64); + bytes.extend(text.as_bytes()); + } + fn null_literal(bytes: &mut Vec, logical: u8, physical: u8) { + bytes.push(70); + put_text(bytes, ""); + put_text(bytes, "NULL"); + put64(bytes, 0); + bytes.extend([logical, physical, 0, 1]); + put32(bytes, 0); + match physical { + 0 => {} + 1 | 9 => bytes.push(0), + 7 => bytes.extend([0x7f; 4]), + 2 | 6 | 11 => bytes.extend([0x7f; 8]), + 14 => bytes.extend([0x7f; 16]), + 20 | 21 => put_text(bytes, ""), + _ => unreachable!(), + } + } + fn catalog_fixture() -> Vec { + let mut bytes = Vec::new(); + put64(&mut bytes, PROFILE_TABLES as u64); + put64(&mut bytes, PROFILE_TABLES as u64); + for id in 0..PROFILE_TABLES as u64 { + bytes.push(0); + put_text(&mut bytes, &format!("Table{id}")); + put64(&mut bytes, id); + bytes.push(0); + put_text(&mut bytes, ""); + put32(&mut bytes, 1); + put32(&mut bytes, 1); + put64(&mut bytes, 1); + put32(&mut bytes, 0); + put_text(&mut bytes, "id"); + bytes.extend([50, 20, 0]); + null_literal(&mut bytes, 50, 20); + put64(&mut bytes, 1); + put32(&mut bytes, 0); + put32(&mut bytes, 0); + put_text(&mut bytes, "id"); + put64(&mut bytes, 0); + put_text(&mut bytes, ""); + bytes.push(0); + } + for set in 1..10 { + if set == 3 { + put64(&mut bytes, 1); + put64(&mut bytes, 1); + bytes.push(41); + put_text(&mut bytes, "JSON"); + put64(&mut bytes, 0); + bytes.push(0); + bytes.extend([60, 21, 0]); + } else { + put64(&mut bytes, 0); + put64(&mut bytes, 0); + } + } + bytes + } + fn wrap_catalog(catalog: &[u8], metadata: &[u8]) -> Vec { + let cp = (catalog.len() as u64).div_ceil(PAGE_BYTES) as u32; + let mp = (metadata.len() as u64).div_ceil(PAGE_BYTES) as u32; + let mut bytes = vec![0; (1 + cp + mp) as usize * PAGE_BYTES as usize]; + bytes[..4].copy_from_slice(b"LBUG"); + bytes[4..12].copy_from_slice(&43u64.to_le_bytes()); + for (at, value) in [ + (12, 1), + (16, cp), + (20, if mp == 0 { u32::MAX } else { 1 + cp }), + (24, mp), + (45, 1 + cp + mp), + ] { + bytes[at..at + 4].copy_from_slice(&value.to_le_bytes()); + } + bytes[44] = 2; + bytes[PAGE_BYTES as usize..PAGE_BYTES as usize + catalog.len()].copy_from_slice(catalog); + if mp > 0 { + let at = (1 + cp) as usize * PAGE_BYTES as usize; + bytes[at..at + metadata.len()].copy_from_slice(metadata); + } + bytes + } + #[test] + fn dc_native_catalog_typed_nulls_builtin_json_and_explicit_source_floor() { + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let catalog = catalog_fixture(); + let bytes = wrap_catalog(&catalog, &[]); + let _source_floor = memory.reserve(bytes.capacity()).unwrap(); + let baseline = memory.used(); + let mut properties = 0; + let facts = trace_native_catalog43(bytes.as_slice(), &memory, &mut |event| { + if let NativeCatalogEvent::Property { default, .. } = event { + assert_eq!(default, NativeDefaultFacts::Null); + properties += 1; + } + Ok(()) + }) + .unwrap(); + assert_eq!(facts.tables, 112); + assert_eq!(properties, 112); + assert_eq!(memory.used(), baseline); + for (logical, physical) in [ + (0, 0), + (22, 1), + (23, 2), + (27, 6), + (28, 7), + (30, 9), + (32, 11), + (42, 14), + (50, 20), + (60, 21), + ] { + let mut raw = Vec::new(); + null_literal(&mut raw, logical, physical); + let mut reader = CatalogCursor::new(raw.as_slice(), 0, raw.len() as u64).unwrap(); + assert_eq!( + reader.literal(&memory, (logical, physical)).unwrap(), + NativeDefaultFacts::Null + ); + assert_eq!(reader.at, raw.len() as u64); + } + let mut wrong = Vec::new(); + null_literal(&mut wrong, 50, 20); + let mut reader = CatalogCursor::new(wrong.as_slice(), 0, wrong.len() as u64).unwrap(); + assert!(reader.literal(&memory, (23, 2)).is_err()); + } + #[test] + fn dc_native_catalog_rejects_nonbuiltin_type_duplicate_table_and_bad_suffix() { + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let catalog = catalog_fixture(); + let bytes = wrap_catalog(&catalog, &[]); + let mut name = None; + trace_native_catalog43(bytes.as_slice(), &memory, &mut |event| { + if let NativeCatalogEvent::Table { + id: 1, name: span, .. + } = event + { + name = Some(span) + } + Ok(()) + }) + .unwrap(); + let name = name.unwrap(); + let id_offset = name.end as usize; + let mut duplicate = bytes.clone(); + duplicate[id_offset..id_offset + 8].copy_from_slice(&0u64.to_le_bytes()); + assert!(trace_native_catalog43(duplicate.as_slice(), &memory, &mut |_| Ok(())).is_err()); + let json_at = bytes.windows(4).position(|v| v == b"JSON").unwrap(); + let mut custom = bytes.clone(); + custom[json_at..json_at + 4].copy_from_slice(b"JUNK"); + assert!(trace_native_catalog43(custom.as_slice(), &memory, &mut |_| Ok(())).is_err()); + let mut suffix = bytes.clone(); + let end = PAGE_BYTES as usize + catalog.len(); + suffix[end - 8..end].copy_from_slice(&1u64.to_le_bytes()); + assert!(trace_native_catalog43(suffix.as_slice(), &memory, &mut |_| Ok(())).is_err()); + } + fn metadata_descriptor(bytes: &mut Vec, values: u64, tag: u8) { + put32(bytes, u32::MAX); + put32(bytes, 0); + put64(bytes, values); + bytes.extend([0; 32]); + bytes.push(tag); + } + fn primitive_chunk(bytes: &mut Vec, logical: u8, physical: u8, values: u64) { + bytes.extend([logical, physical, 0]); + metadata_descriptor(bytes, values, 3); + bytes.extend([1, 0]); + } + #[test] + fn dc_native_metadata_empty_dictionary_and_implicit_visible_version_tail() { + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let mut metadata = Vec::new(); + put64(&mut metadata, 112); + for table in 0..112 { + put64(&mut metadata, table); + put64(&mut metadata, if table == 0 { 1 } else { 0 }); + if table == 0 { + put64(&mut metadata, 0); + metadata.extend([1, 0, 1]); + put64(&mut metadata, 1); + metadata.push(1); + put64(&mut metadata, 1); + metadata.extend([50, 20, 0]); + metadata_descriptor(&mut metadata, 4097, 0); + metadata.extend([1, 1]); + metadata_descriptor(&mut metadata, 4097, 3); + primitive_chunk(&mut metadata, 28, 7, 4097); + primitive_chunk(&mut metadata, 27, 6, 1); + primitive_chunk(&mut metadata, 30, 9, 0); + put64(&mut metadata, 0); + metadata.push(1); + put64(&mut metadata, 1); + metadata.extend([1, 2, 1]); + put64(&mut metadata, 1); + } + put64(&mut metadata, if table == 0 { 4097 } else { 0 }); + put64(&mut metadata, 1); + metadata.push(0); + put64(&mut metadata, 0); + } + let bytes = wrap_catalog(&catalog_fixture(), &metadata); + let mut group_rows = 0; + let facts = trace_native_metadata43(bytes.as_slice(), &memory, &mut |event| { + if let NativeMetadataEvent::Group { rows, .. } = event { + group_rows += rows; + } + Ok(()) + }) + .unwrap(); + assert_eq!(facts.tables, 112); + assert_eq!(group_rows, 4097); + assert_eq!(facts.chunks, 1); + let truncated = &bytes[..bytes.len() - PAGE_BYTES as usize]; + assert!(trace_native_metadata43(truncated, &memory, &mut |_| Ok(())).is_err()); + } + #[test] + #[ignore = "requires separately generated synthetic public-producer fixture directory"] + fn dc_native_actual_producer187_catalog_metadata_checkpoint() { + use super::super::maintenance_validation::{BorrowedFileSource, ByteSource}; + use std::collections::BTreeMap; + let root = std::path::PathBuf::from( + std::env::var_os("DC_S3_NATIVE_FIXTURES") + .expect("set explicit synthetic fixture directory"), + ); + let oracle: serde_json::Value = + serde_json::from_slice(&std::fs::read(root.join("oracle-results.json")).unwrap()) + .unwrap(); + for case in oracle["cases"].as_array().unwrap() { + let name = case["fixture"].as_str().unwrap(); + assert!(name.starts_with("synthetic_native_")); + let path = root + .join(name) + .join(".codebaseGraph") + .join(format!("{name}_graph.ldb")); + let mut file = std::fs::File::open(path).unwrap(); + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let source = BorrowedFileSource::new(&mut file, &memory).unwrap(); + let mut tables = BTreeMap::new(); + let table_name = |span: super::super::maintenance_validation::Span| { + let mut bytes = vec![0; (span.end - span.start) as usize]; + let mut at = 0; + while at < bytes.len() { + let n = source + .read_at(span.start + at as u64, &mut bytes[at..]) + .unwrap(); + assert!(n > 0); + at += n; + } + String::from_utf8(bytes).unwrap() + }; + let catalog = trace_native_catalog43(&source, &memory, &mut |event| { + if let NativeCatalogEvent::Table { id, name, node } = event { + tables.insert(id, (table_name(name), node)); + } + Ok(()) + }) + .unwrap(); + let mut expected = BTreeMap::new(); + for row in case["native_oracles"].as_array().unwrap().last().unwrap()["rows"] + .as_array() + .unwrap() + { + let id = row[0] + .as_str() + .unwrap() + .strip_prefix("UInt64(") + .unwrap() + .strip_suffix(')') + .unwrap() + .parse::() + .unwrap(); + let native_name = row[1] + .as_str() + .unwrap() + .strip_prefix("String(") + .unwrap() + .strip_suffix(')') + .unwrap(); + let name = serde_json::from_str::(native_name).unwrap(); + expected.insert(id, (name, row[2].as_str().unwrap() == "String(\"NODE\")")); + } + assert_eq!(tables, expected); + assert_eq!(catalog.tables, 112); + assert_eq!(catalog.indexes, 20); + let mut rows = 0; + let metadata = trace_native_metadata43(&source, &memory, &mut |event| { + if let NativeMetadataEvent::Group { rows: n, .. } = event { + rows += n; + } + Ok(()) + }) + .unwrap(); + assert_eq!(metadata.tables, 62); + assert_eq!(metadata.indexes, 20); + let response: serde_json::Value = serde_json::from_slice( + &std::fs::read(root.join(name).join("response.json")).unwrap(), + ) + .unwrap(); + assert_eq!( + rows, + response["node_rows"].as_u64().unwrap() + response["edge_rows"].as_u64().unwrap() + ); + } + } +} + +#[cfg(test)] +mod selected_tests { + use super::super::maintenance_validation::{key_stream_hash, KeyEncoding, Span}; + use super::*; + fn meta( + page: u32, + pages: u32, + values: u64, + min: u64, + max: u64, + compression: u8, + ) -> NativePrimitiveMetadata { + let mut lo = [0; 16]; + let mut hi = [0; 16]; + lo[..8].copy_from_slice(&min.to_le_bytes()); + hi[..8].copy_from_slice(&max.to_le_bytes()); + NativePrimitiveMetadata { + page, + pages, + values, + min: lo, + max: hi, + compression, + } + } + fn packed( + width: u8, + offset: u64, + count: usize, + ) -> (Vec, NativePrimitiveMetadata, Vec) { + let per = 32768 / usize::from(width); + let pages = count.div_ceil(per); + let mut bytes = vec![0; (pages + 1) * 4096]; + let mask = if width == 64 { + u64::MAX + } else { + (1u64 << width) - 1 + }; + let mut expected = Vec::new(); + for row in 0..count { + let encoded = (row as u64).wrapping_mul(0x123456789abcdef) & mask; + let value = encoded.checked_add(offset).unwrap(); + expected.push(value); + let bit = (row % per) * usize::from(width); + let base = (1 + row / per) * 4096; + for k in 0..usize::from(width) { + if encoded & (1u64 << k) != 0 { + bytes[base + (bit + k) / 8] |= 1 << ((bit + k) % 8); + } + } + } + ( + bytes, + meta(1, pages as u32, count as u64, offset, mask + offset, 1), + expected, + ) + } + #[test] + fn dc_native_scalar_unsigned_every_width_page_word_and_frame_crossing() { + for width in 1..=64 { + for offset in [0, if width < 63 { 1u64 << 63 } else { 0 }] { + let count = 32768 / width as usize + 39; + let (bytes, m, expected) = packed(width, offset, count); + let mut scratch = NativeDataScratch::new(); + for (i, want) in expected.iter().enumerate() { + assert_eq!( + scalar(bytes.as_slice(), &mut scratch, m, 64, i as u64).unwrap(), + *want, + "width={width} row={i}" + ); + } + } + } + for bits in [8, 32, 64] { + let mut bytes = vec![0; 8192]; + let n = usize::from(bits) / 8; + let value = if bits == 64 { + u64::MAX + } else { + (1u64 << bits) - 1 + }; + bytes[4096..4096 + n].copy_from_slice(&value.to_le_bytes()[..n]); + let mut scratch = NativeDataScratch::new(); + assert_eq!( + scalar( + bytes.as_slice(), + &mut scratch, + meta(1, 1, 1, 0, 0, 0), + bits, + 0 + ) + .unwrap(), + value + ); + assert_eq!( + scalar( + bytes.as_slice(), + &mut scratch, + meta(INVALID_PAGE, 0, 1, value, value, 3), + bits, + 0 + ) + .unwrap(), + value + ); + } + } + #[test] + fn dc_native_scalar_null_mask_and_corrupt_ranges_are_explicit() { + let mut bytes = vec![0; 3 * 4096]; + bytes[4096] = 0b10000001; + bytes[8192] = 1; + let mut scratch = NativeDataScratch::new(); + let m = meta(1, 2, 32769, 0, 1, 2); + for row in [0, 7, 32768] { + assert_eq!( + scalar(bytes.as_slice(), &mut scratch, m, 1, row).unwrap(), + 1 + ); + } + assert_eq!(scalar(bytes.as_slice(), &mut scratch, m, 1, 1).unwrap(), 0); + for bad in [ + meta(0, 1, 1, 0, 1, 1), + meta(1, 0, 1, 0, 1, 1), + meta(1, 1, 1, 2, 1, 1), + meta(1, 1, 1, 0, 0, 1), + meta(1, 1, 1, 0, 1, 4), + ] { + assert!(scalar(bytes.as_slice(), &mut scratch, bad, 64, 0).is_err()); + } + assert!(scalar(bytes.as_slice(), &mut scratch, m, 1, m.values).is_err()); + let mut bad = meta(1, 1, 1, 0, 1, 1); + bad.max[8] = 1; + assert!(scalar(bytes.as_slice(), &mut scratch, bad, 64, 0).is_err()); + } + fn locator(bytes: &[u8]) -> (Vec, NativeStringLocator) { + let pages = bytes.len().div_ceil(4096); + let mut source = vec![0; (pages + 1) * 4096]; + source[4096..4096 + bytes.len()].copy_from_slice(bytes); + ( + source, + NativeStringLocator { + source: 0, + compression: 0, + bits: 8, + reserved: [0; 5], + page: 1, + pages: pages as u32, + values: bytes.len() as u64, + min: 0, + max: 0, + start: 0, + length: bytes.len() as u64, + }, + ) + } + #[test] + fn dc_native_logical_long_utf8_metadata_empty_and_late_errors() { + let memory = CleanupMemory::new(100_000).unwrap(); + let floor = memory.used(); + let text = format!("{}é🙂\0{}", "a".repeat(4095), "b".repeat(100_000)); + let (bytes, l) = locator(text.as_bytes()); + { + let source = NativeStringSource::new(bytes.as_slice(), l, &memory).unwrap(); + assert_eq!( + key_stream_hash( + &source, + Span { + start: 0, + end: l.length + }, + KeyEncoding::NativeUtf8, + 8, + &memory + ) + .unwrap(), + key_stream_hash( + text.as_bytes(), + Span { + start: 0, + end: text.len() as u64 + }, + KeyEncoding::NativeUtf8, + 8, + &memory + ) + .unwrap() + ); + } + assert_eq!(memory.used(), floor); + let empty = NativeStringLocator { + page: INVALID_PAGE, + pages: 0, + values: 0, + start: 0, + length: 0, + ..l + }; + let source = NativeStringSource::new([].as_slice(), empty, &memory).unwrap(); + assert!(validate_logical_utf8(&source, &memory).is_ok()); + drop(source); + let (mut bad, l) = locator(text.as_bytes()); + bad[4096 + text.len() - 1] = 0xff; + let source = NativeStringSource::new(bad.as_slice(), l, &memory).unwrap(); + assert!(validate_logical_utf8(&source, &memory).is_err()); + drop(source); + let json = br#"{"canonical_key":"p\u0061th","extra":{"x":[1,null]}}"#; + let (bytes, l) = locator(json); + let source = NativeStringSource::new(bytes.as_slice(), l, &memory).unwrap(); + let span = metadata_canonical_key(&source, &memory).unwrap().unwrap(); + assert_eq!( + super::super::maintenance_validation::compare_native_json( + b"path".as_slice(), + Span { start: 0, end: 4 }, + &source, + span, + &memory + ) + .unwrap(), + std::cmp::Ordering::Equal + ); + let (bytes, l) = locator(br#"{"canonical_key":"a","canonical_key":"b"}"#); + let source = NativeStringSource::new(bytes.as_slice(), l, &memory).unwrap(); + assert!(metadata_canonical_key(&source, &memory).is_err()); + let small = CleanupMemory::new(64).unwrap(); + assert!(NativeStringSource::new(bytes.as_slice(), l, &small).is_err()); + } + #[test] + fn dc_native_visibility_deleted_early_vector_and_implicit_tail() { + let mut bytes = vec![1]; + bytes.extend(1u64.to_le_bytes()); + bytes.extend([1, 2, 1]); + bytes.extend(u64::MAX.to_le_bytes()); + for i in 0..2048 { + bytes.extend(if i == 4 { 42u64 } else { u64::MAX }.to_le_bytes()); + } + let mut version = VersionCursor::new(bytes.as_slice(), 0, bytes.len() as u64).unwrap(); + for row in 0..4097 { + assert_eq!(version.visible(row).unwrap(), row != 4); + } + bytes.truncate(20); + let mut version = VersionCursor::new(bytes.as_slice(), 0, bytes.len() as u64).unwrap(); + assert!(version.visible(0).is_err()); + } + #[test] + fn dc_native_scan_cancellation_precedes_any_source_read() { + struct NoRead; + impl ByteSource for NoRead { + fn len(&self) -> u64 { + 0 + } + fn read_at(&self, _: u64, _: &mut [u8]) -> Result { + panic!("cancelled source must not read") + } + } + let memory = CleanupMemory::new(100_000).unwrap(); + let mut emits = 0; + assert_eq!( + scan_native_selected43( + &NoRead, + &memory, + &mut || Err(NativeUnknown::Source(ValidationIssue::Io( + std::io::ErrorKind::Interrupted + ))), + &mut |_| { + emits += 1; + Ok(()) + } + ), + Err(NativeUnknown::Source(ValidationIssue::Io( + std::io::ErrorKind::Interrupted + ))) + ); + assert_eq!(emits, 0); + } +} + +#[cfg(test)] +mod selected_oracle_tests { + use super::super::maintenance_validation::{compare_native_json, BorrowedFileSource, Span}; + use super::*; + #[test] + #[ignore = "requires separately generated actual public-producer187 synthetic fixtures"] + fn dc_native_actual_producer187_selected_strings_and_support_rows() { + let root = std::path::PathBuf::from( + std::env::var_os("DC_S3_NATIVE_FIXTURES") + .expect("explicit synthetic fixture directory"), + ); + let oracle: serde_json::Value = + serde_json::from_slice(&std::fs::read(root.join("oracle-results.json")).unwrap()) + .unwrap(); + let string = |value: &serde_json::Value| -> String { + let s = value.as_str().unwrap(); + serde_json::from_str( + s.strip_prefix("String(") + .unwrap() + .strip_suffix(')') + .unwrap(), + ) + .unwrap() + }; + for case in oracle["cases"].as_array().unwrap() { + let name = case["fixture"].as_str().unwrap(); + assert!(name.starts_with("synthetic_native_")); + let mut expected = std::collections::BTreeMap::new(); + for (index, query) in case["native_oracles"].as_array().unwrap()[..4] + .iter() + .enumerate() + { + for row in query["rows"].as_array().unwrap() { + let id = string(&row[0]); + let fields = match index { + 0 => [ + id.clone(), + string(&row[1]), + "repository".into(), + string(&row[2]), + string(&row[1]), + ], + 1 => [ + id.clone(), + string(&row[1]), + "sourceroot".into(), + string(&row[2]), + string(&row[2]), + ], + 2 => [ + id.clone(), + "file".into(), + string(&row[1]), + string(&row[2]), + string(&row[1]), + ], + 3 => { + let kind = string(&row[1]); + let source = string(&row[2]); + let target = string(&row[3]); + [ + id.clone(), + kind.clone(), + source.clone(), + target.clone(), + format!("Contains|{source}|{target}|{kind}"), + ] + } + _ => unreachable!(), + }; + assert!(expected.insert(id, fields).is_none()); + } + } + let path = root + .join(name) + .join(".codebaseGraph") + .join(format!("{name}_graph.ldb")); + let mut file = std::fs::File::open(path).unwrap(); + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let source = BorrowedFileSource::new(&mut file, &memory).unwrap(); + let mut actual = 0; + scan_native_selected43(&source, &memory, &mut || Ok(()), &mut |row| { + let mut fields = Vec::new(); + for locator in row.fields[..4].iter() { + let l = locator.unwrap(); + let s = NativeStringSource::new(&source, l, &memory)?; + let mut bytes = vec![0; s.len() as usize]; + s.read_at(0, &mut bytes)?; + fields.push(String::from_utf8(bytes).unwrap()); + } + let want = expected.remove(&fields[0]).unwrap(); + assert_eq!(&fields[..], &want[..4]); + let metadata = NativeStringSource::new(&source, row.fields[4].unwrap(), &memory)?; + assert_eq!( + compare_native_json( + want[4].as_bytes(), + Span { + start: 0, + end: want[4].len() as u64 + }, + &metadata, + row.canonical_key.unwrap(), + &memory + )?, + std::cmp::Ordering::Equal + ); + actual += 1; + Ok(()) + }) + .unwrap(); + assert_eq!(actual, 7); + assert!(expected.is_empty()); + assert!(memory.peak() < 64 * 1024); + eprintln!( + "{name}: exact7selected rows+5fields+canonical metadata; requestedpeak={}", + memory.peak() + ); + // A changed incarnation/cancellation invalidates the entire pass, + // including rows already observed by the provisional callback. + let mut checks = 0; + let result = scan_native_selected43( + &source, + &memory, + &mut || { + checks += 1; + if checks > 3 { + Err(NativeUnknown::Source(ValidationIssue::Io( + std::io::ErrorKind::Interrupted, + ))) + } else { + Ok(()) + } + }, + &mut |_| Ok(()), + ); + assert!(result.is_err()); + let epoch = std::cell::Cell::new(0); + let mut provisional = 0; + let changed = scan_native_selected43( + &source, + &memory, + &mut || { + if epoch.get() == 0 { + Ok(()) + } else { + Err(NativeUnknown::SourceChanged) + } + }, + &mut |_| { + provisional += 1; + epoch.set(1); + Ok(()) + }, + ); + assert_eq!(changed, Err(NativeUnknown::SourceChanged)); + assert_eq!(provisional, 1); + } + } +} + +#[cfg(test)] +mod dictionary_tests { + use super::*; + fn m(page: u32, values: u64, value: u64, compression: u8) -> NativePrimitiveMetadata { + let mut min = [0; 16]; + min[..8].copy_from_slice(&value.to_le_bytes()); + NativePrimitiveMetadata { + page, + pages: if page == INVALID_PAGE { 0 } else { 1 }, + values, + min, + max: min, + compression, + } + } + fn put(out: &mut Vec, m: NativePrimitiveMetadata) { + out.extend(m.page.to_le_bytes()); + out.extend(m.pages.to_le_bytes()); + out.extend(m.values.to_le_bytes()); + out.extend(m.min); + out.extend(m.max); + out.push(m.compression); + } + fn child(out: &mut Vec, logical: u8, physical: u8, m: NativePrimitiveMetadata) { + out.extend([logical, physical, 0]); + put(out, m); + out.extend([1, 0]); + } + fn segment(out: &mut Vec, page: u32, len: u64, null: bool, index: u64) { + out.extend([50, 20, 0]); + put(out, m(INVALID_PAGE, 1, 0, 0)); + out.extend([1, 1]); + put(out, m(INVALID_PAGE, 1, u64::from(null), 3)); + child(out, 28, 7, m(INVALID_PAGE, 1, index, 3)); + child(out, 27, 6, m(INVALID_PAGE, 1, 0, 3)); + child(out, 30, 9, m(page, len, 0, 0)); + } + #[test] + fn dc_native_dictionary_segment_advance_retained_locator_null_empty_and_invalid_index() { + let memory = CleanupMemory::new(100_000).unwrap(); + let mut metadata = vec![1]; + metadata.extend(4u64.to_le_bytes()); + segment(&mut metadata, 1, 3, false, 0); + segment(&mut metadata, 2, 3, false, 0); + segment(&mut metadata, INVALID_PAGE, 0, false, 0); + segment(&mut metadata, INVALID_PAGE, 0, true, 99); + let mut bytes = vec![0; 30 * 4096]; + bytes[..metadata.len()].copy_from_slice(&metadata); + bytes[4096..4099].copy_from_slice(b"one"); + bytes[8192..8195].copy_from_slice(b"two"); + let header = NativeHeader43Facts { + catalog: NativePageRange { + start: 20, + count: 1, + }, + metadata: NativePageRange { + start: 21, + count: 1, + }, + database_id: [0; 16], + data_file_pages: 30, + available_pages: 30, + }; + let mut cursor = ColumnCursor::new( + bytes.as_slice(), + super::super::maintenance_validation::Span { + start: 0, + end: metadata.len() as u64, + }, + ) + .unwrap(); + let mut scratch = NativeDataScratch::new(); + let retained = cursor.value(0, header, &mut scratch).unwrap().unwrap(); + let second = cursor.value(1, header, &mut scratch).unwrap().unwrap(); + for (l, text) in [(retained, b"one"), (second, b"two")] { + let source = NativeStringSource::new(bytes.as_slice(), l, &memory).unwrap(); + let mut actual = [0; 3]; + source.read_at(0, &mut actual).unwrap(); + assert_eq!(&actual, text); + } + assert_eq!( + cursor + .value(2, header, &mut scratch) + .unwrap() + .unwrap() + .length, + 0 + ); + assert_eq!(cursor.value(3, header, &mut scratch).unwrap(), None); + let mut metadata = vec![1]; + metadata.extend(1u64.to_le_bytes()); + segment(&mut metadata, 1, 3, false, 2); + let mut cursor = ColumnCursor::new( + metadata.as_slice(), + super::super::maintenance_validation::Span { + start: 0, + end: metadata.len() as u64, + }, + ) + .unwrap(); + assert!(cursor.value(0, header, &mut scratch).is_err()); + } + #[test] + fn dc_native_metadata_preserves_terminal_source_cancellation_instead_of_retry() { + struct Cancel; + impl ByteSource for Cancel { + fn len(&self) -> u64 { + 5000 + } + fn read_at(&self, _: u64, _: &mut [u8]) -> Result { + Err(ValidationIssue::Io(std::io::ErrorKind::Interrupted)) + } + } + let memory = CleanupMemory::new(100_000).unwrap(); + assert_eq!( + metadata_canonical_key(&Cancel, &memory), + Err(NativeUnknown::Source(ValidationIssue::Io( + std::io::ErrorKind::Interrupted + ))) + ); + } +} diff --git a/src/storage/maintenance_origin.rs b/src/storage/maintenance_origin.rs new file mode 100644 index 00000000..392b1ef8 --- /dev/null +++ b/src/storage/maintenance_origin.rs @@ -0,0 +1,1461 @@ +//! Actual producer origin for one artifact payload leaf. Origin is independent +//! of loader usability, reachability, migration and deletion authority. +use super::locks::{self, FileIdentity, FixedControlKind, FixedFileStamp, RootedDirectory}; +use super::maintenance_budget::CleanupMemory; +use super::maintenance_controls::{self, PublisherBinding, RootPublicationLease}; +use crate::error::NativeError; +use serde::Deserialize; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; + +pub(crate) const ORIGIN_NAME: &[u8] = b".entry-origin"; +pub(crate) const PAYLOAD_NAME: &[u8] = b"partition.json"; +const FRAME: usize = 512; +const MAX_BYTES: usize = 4 * FRAME; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum OriginPhase { + Bound, + Writing, + Complete, + Published, +} +impl OriginPhase { + fn number(self) -> u8 { + match self { + Self::Bound => 0, + Self::Writing => 1, + Self::Complete => 2, + Self::Published => 3, + } + } +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct OriginScope { + pub(crate) root: FileIdentity, + pub(crate) coordination: FileIdentity, + pub(crate) cohort: FileIdentity, + pub(crate) publication_binding: FileIdentity, + pub(crate) selector: FileIdentity, + pub(crate) incarnation: [u8; 32], +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct OriginFacts { + pub(crate) scope: OriginScope, + pub(crate) key: [u8; 32], + pub(crate) operation_nonce: [u8; 32], + pub(crate) directory: FileIdentity, + pub(crate) payload: FileIdentity, + pub(crate) receipt: FileIdentity, + pub(crate) directory_created: bool, + pub(crate) payload_created: bool, + pub(crate) phase: OriginPhase, + pub(crate) byte_len: u64, + pub(crate) payload_sha256: [u8; 32], + pub(crate) payload_nonce: [u8; 32], + pub(crate) payload_stamp: Option, + pub(crate) torn_tail: bool, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +// Fixed inline protocol/lease state: boxing would add a separately accounted allocation. +#[allow(clippy::large_enum_variant)] +pub(crate) enum OriginObservation { + Missing, + Unknown(&'static str), + Observed(OriginFacts), +} +fn invalid(reason: &str) -> NativeError { + NativeError::InvalidInput(reason.into()) +} +fn stamp(file: &File) -> Result { + FixedFileStamp::capture(file, FixedControlKind::File) + .map_err(|_| invalid("artifact_origin_file_changed_or_aliased")) +} +fn recheck(s: FixedFileStamp, file: &File) -> Result<(), NativeError> { + s.recheck(file) + .map_err(|_| invalid("artifact_origin_source_changed")) +} +fn identity(bytes: &mut [u8], id: FileIdentity) { + let (a, b) = id.fixed_words(); + bytes[..8].copy_from_slice(&a.to_le_bytes()); + bytes[8..16].copy_from_slice(&b.to_le_bytes()); +} +fn read_identity(bytes: &[u8]) -> FileIdentity { + FileIdentity::from_fixed_words( + u64::from_le_bytes(bytes[..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + ) +} +fn encode(f: OriginFacts) -> [u8; FRAME] { + let mut b = [0; FRAME]; + b[..8].copy_from_slice(b"CGORIG01"); + b[8..16].copy_from_slice(&2u64.to_le_bytes()); + b[16..24].copy_from_slice(&1u64.to_le_bytes()); + b[24] = f.phase.number(); + b[25] = u8::from(f.directory_created) | (u8::from(f.payload_created) << 1); + b[32..64].copy_from_slice(&f.scope.incarnation); + b[64..96].copy_from_slice(&f.key); + b[96..128].copy_from_slice(&f.operation_nonce); + for (offset, id) in [ + (128, f.scope.root), + (144, f.scope.coordination), + (160, f.scope.cohort), + (176, f.scope.publication_binding), + (192, f.scope.selector), + (208, f.directory), + (224, f.payload), + (240, f.receipt), + ] { + identity(&mut b[offset..offset + 16], id); + } + b[256..264].copy_from_slice(&f.byte_len.to_le_bytes()); + b[264..296].copy_from_slice(&f.payload_sha256); + if let Some(stamp) = f.payload_stamp { + stamp.encode_checkpoint((&mut b[296..376]).try_into().unwrap()); + } + b[376..408].copy_from_slice(&f.payload_nonce); + let digest = Sha256::digest(&b[..480]); + b[480..].copy_from_slice(&digest); + b +} +fn decode(b: &[u8; FRAME]) -> Option { + if &b[..8] != b"CGORIG01" + || u64::from_le_bytes(b[8..16].try_into().ok()?) != 2 + || u64::from_le_bytes(b[16..24].try_into().ok()?) != 1 + || b[24] > 3 + || b[25] > 3 + || b[26..32].iter().chain(b[408..480].iter()).any(|v| *v != 0) + || Sha256::digest(&b[..480]).as_slice() != &b[480..] + { + return None; + } + let phase = [ + OriginPhase::Bound, + OriginPhase::Writing, + OriginPhase::Complete, + OriginPhase::Published, + ][usize::from(b[24])]; + let byte_len = u64::from_le_bytes(b[256..264].try_into().ok()?); + let payload_sha256 = b[264..296].try_into().ok()?; + let payload_nonce: [u8; 32] = b[376..408].try_into().ok()?; + if payload_nonce == [0; 32] { + return None; + } + let payload_stamp = if phase.number() < 2 { + if b[296..376].iter().any(|b| *b != 0) { + return None; + } + None + } else { + if b[320] != 0 { + return None; + } + let stamp = FixedFileStamp::decode_checkpoint(b[296..376].try_into().ok()?).ok()?; + if stamp.identity() != read_identity(&b[224..240]) || stamp.len() != byte_len { + return None; + } + Some(stamp) + }; + if phase.number() < 2 && (byte_len != 0 || payload_sha256 != [0; 32]) { + return None; + } + if b[96..128].iter().all(|v| *v == 0) || b[32..64].iter().all(|v| *v == 0) { + return None; + } + Some(OriginFacts { + scope: OriginScope { + root: read_identity(&b[128..144]), + coordination: read_identity(&b[144..160]), + cohort: read_identity(&b[160..176]), + publication_binding: read_identity(&b[176..192]), + selector: read_identity(&b[192..208]), + incarnation: b[32..64].try_into().ok()?, + }, + key: b[64..96].try_into().ok()?, + operation_nonce: b[96..128].try_into().ok()?, + directory: read_identity(&b[208..224]), + payload: read_identity(&b[224..240]), + receipt: read_identity(&b[240..256]), + directory_created: b[25] & 1 != 0, + payload_created: b[25] & 2 != 0, + phase, + byte_len, + payload_sha256, + payload_nonce, + payload_stamp, + torn_tail: false, + }) +} +fn scope(lease: &RootPublicationLease<'_, '_>) -> OriginScope { + OriginScope { + root: lease.binding().root, + coordination: lease.binding().coordination, + cohort: lease.root_cohort_identity(), + publication_binding: lease.publication_binding_identity(), + selector: lease.binding().selector, + incarnation: lease.binding().incarnation, + } +} + +/// Created exclusively by the retained actual producer publication lease. Its +/// constructor never adopts an existing receipt and its log is at most2048B. +pub(crate) struct OriginWriter { + file: File, + facts: OriginFacts, + usable: bool, +} +/// A consumed creator's continuation, never reconstructed from an observed +/// receipt. It retains no child file handle across a Windows directory move. +pub(crate) struct OriginPublication { + facts: OriginFacts, +} +impl OriginPublication { + pub(crate) fn published( + self, + lease: &RootPublicationLease<'_, '_>, + directory: &RootedDirectory, + ) -> Result<(), NativeError> { + if directory.identity() != self.facts.directory { + return Err(invalid("artifact_origin_publication_changed")); + } + let mut file = directory.open_existing(ORIGIN_NAME)?; + let before = stamp(&file)?; + let frames = u64::from(self.facts.phase.number()) + 1; + if before.identity() != self.facts.receipt || before.len() != frames * FRAME as u64 { + return Err(invalid("artifact_origin_receipt_changed")); + } + file.seek(SeekFrom::Start((frames - 1) * FRAME as u64))?; + let mut last = [0; FRAME]; + file.read_exact(&mut last)?; + recheck(before, &file)?; + if last != encode(self.facts) { + return Err(invalid("artifact_origin_receipt_changed")); + } + file.seek(SeekFrom::End(0))?; + OriginWriter { + file, + facts: self.facts, + usable: true, + } + .published(lease, directory) + } +} +impl OriginWriter { + pub(crate) fn into_publication(self) -> Option { + (self.usable && self.facts.phase == OriginPhase::Complete) + .then_some(OriginPublication { facts: self.facts }) + } + pub(crate) fn create( + lease: &RootPublicationLease<'_, '_>, + directory: &RootedDirectory, + payload: &File, + key: [u8; 32], + directory_created: bool, + payload_created: bool, + ) -> Result { + if !lease.revalidate_existing()? { + return Err(invalid("artifact_origin_scope_changed")); + } + let payload_stamp = stamp(payload)?; + let named = directory.open_readonly_file(PAYLOAD_NAME)?; + if locks::file_identity(&named)? != payload_stamp.identity() { + return Err(invalid("artifact_origin_payload_changed")); + } + if payload_created && payload_stamp.len() != 0 { + return Err(invalid("artifact_origin_created_payload_not_empty")); + } + let mut operation_nonce = [0; 32]; + getrandom::fill(&mut operation_nonce) + .map_err(|_| invalid("artifact_origin_nonce_unavailable"))?; + if operation_nonce == [0; 32] { + return Err(invalid("artifact_origin_zero_nonce")); + } + let payload_nonce = match locks::created_file_nonce(payload)? { + Some(nonce) if !payload_created && nonce != [0; 32] => nonce, + Some(_) => return Err(invalid("artifact_origin_existing_creation_marker")), + None => { + // Existing byte-equal payload attestation may add only this + // immutable marker. Flags remain false; it claims no creation. + let writable = directory.open_existing(PAYLOAD_NAME)?; + payload_stamp + .recheck(&writable) + .map_err(|_| invalid("artifact_origin_attestation_source_changed"))?; + locks::set_created_file_nonce(&writable, &operation_nonce)?; + if locks::created_file_nonce(payload)? != Some(operation_nonce) { + return Err(invalid("artifact_origin_marker_readback_changed")); + } + operation_nonce + } + }; + let file = directory.create_new(ORIGIN_NAME)?; + // Only this exclusive creator may bind the physical receipt birth to + // the operation recorded in its frames. Never repair existing files. + let receipt_stamped = locks::set_created_file_nonce(&file, &operation_nonce).is_ok() + && locks::created_file_nonce(&file).ok().flatten() == Some(operation_nonce); + let receipt = locks::file_identity(&file)?; + let facts = OriginFacts { + scope: scope(lease), + key, + operation_nonce, + directory: directory.identity(), + payload: payload_stamp.identity(), + receipt, + directory_created, + payload_created, + phase: OriginPhase::Bound, + byte_len: 0, + payload_sha256: [0; 32], + payload_nonce, + payload_stamp: None, + torn_tail: false, + }; + let mut writer = Self { + file, + facts, + usable: receipt_stamped, + }; + if !writer.usable + || writer + .file + .write_all(&encode(facts)) + .and_then(|_| writer.file.sync_all()) + .and_then(|_| directory.sync_namespace()) + .is_err() + { + // Retain exact creation identity even if optional persistence failed. + // The primary producer may safely carry only its own created leaf; + // later observation treats this unbound/torn record as unknown. + writer.usable = false; + } + Ok(writer) + } + pub(crate) fn receipt_identity(&self) -> FileIdentity { + self.facts.receipt + } + pub(crate) fn writing(&mut self) -> Result<(), NativeError> { + self.append(OriginPhase::Writing, 0, [0; 32], None) + } + pub(crate) fn complete( + &mut self, + directory: &RootedDirectory, + payload: &File, + len: u64, + digest: [u8; 32], + ) -> Result<(), NativeError> { + if !self.usable || self.facts.phase != OriginPhase::Writing { + return Err(invalid("artifact_origin_incomplete_writer")); + } + let before = stamp(payload)?; + let (actual_len, actual_digest) = hash_payload(payload)?; + recheck(before, payload)?; + if locks::created_file_nonce(payload)? != Some(self.facts.payload_nonce) { + return Err(invalid("artifact_origin_payload_nonce_changed")); + } + if directory.identity() != self.facts.directory + || locks::file_identity(&directory.open_readonly_file(PAYLOAD_NAME)?)? + != self.facts.payload + || locks::file_identity(payload)? != self.facts.payload + || actual_len != len + || actual_digest != digest + { + return Err(invalid("artifact_origin_written_bytes_differ")); + } + directory.sync_namespace()?; + self.append(OriginPhase::Complete, len, digest, Some(before)) + } + pub(crate) fn published( + &mut self, + lease: &RootPublicationLease<'_, '_>, + directory: &RootedDirectory, + ) -> Result<(), NativeError> { + if !lease.revalidate_existing()? + || scope(lease) != self.facts.scope + || directory.identity() != self.facts.directory + || locks::file_identity(&directory.open_readonly_file(PAYLOAD_NAME)?)? + != self.facts.payload + || locks::file_identity(&directory.open_readonly_file(ORIGIN_NAME)?)? + != self.facts.receipt + { + return Err(invalid("artifact_origin_publication_changed")); + } + let payload = directory.open_readonly_file(PAYLOAD_NAME)?; + let before = stamp(&payload)?; + if locks::created_file_nonce(&payload)? != Some(self.facts.payload_nonce) { + return Err(invalid("artifact_origin_publication_nonce_changed")); + } + if self.facts.payload_stamp != Some(before) { + let (len, digest) = hash_payload(&payload)?; + recheck(before, &payload)?; + if len != self.facts.byte_len || digest != self.facts.payload_sha256 { + return Err(invalid("artifact_origin_publication_bytes_changed")); + } + } + self.append( + OriginPhase::Published, + self.facts.byte_len, + self.facts.payload_sha256, + Some(before), + ) + } + fn append( + &mut self, + phase: OriginPhase, + len: u64, + digest: [u8; 32], + payload_stamp: Option, + ) -> Result<(), NativeError> { + if !self.usable + || phase.number() != self.facts.phase.number() + 1 + || self.file.metadata()?.len() != u64::from(phase.number()) * FRAME as u64 + { + return Err(invalid("artifact_origin_nonmonotone_phase")); + } + self.usable = false; + let facts = OriginFacts { + phase, + byte_len: len, + payload_sha256: digest, + payload_stamp, + ..self.facts + }; + self.file.write_all(&encode(facts))?; + self.file.sync_all()?; + self.facts = facts; + self.usable = true; + Ok(()) + } +} +/// Hash actual stable file bytes. Producer callers are outside janitor budget; +/// observation callers precharge this fixed buffer/state in the shared account. +pub(crate) fn hash_payload(file: &File) -> Result<(u64, [u8; 32]), NativeError> { + hash_payload_checked(file, &|| false) +} +fn hash_payload_checked( + file: &File, + cancel: &dyn Fn() -> bool, +) -> Result<(u64, [u8; 32]), NativeError> { + let before = stamp(file)?; + let mut cursor = file; + cursor.seek(SeekFrom::Start(0))?; + let mut bytes = [0; 8192]; + let mut hash = Sha256::new(); + let mut len = 0u64; + loop { + if cancel() { + return Err(std::io::Error::from(std::io::ErrorKind::Interrupted).into()); + } + let n = cursor.read(&mut bytes)?; + if n == 0 { + break; + } + len = len + .checked_add(n as u64) + .ok_or_else(|| invalid("artifact_origin_length_overflow"))?; + hash.update(&bytes[..n]); + } + recheck(before, file)?; + if len != before.len() { + return Err(invalid("artifact_origin_length_changed")); + } + Ok((len, hash.finalize().into())) +} +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Cohort { + version: u32, + root: CohortIdentity, + coordination: CohortIdentity, + coordination_incarnation: [u8; 32], + origin: CohortOrigin, +} +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct CohortIdentity { + volume: u64, + index: u64, +} +impl CohortIdentity { + fn physical(&self) -> FileIdentity { + FileIdentity::from_fixed_words(self.volume, self.index) + } +} +#[derive(Deserialize)] +#[serde(rename_all = "snake_case")] +enum CohortOrigin { + GuardedCreation, + ControlledMigration, +} +fn read_exact(file: &File) -> Result<[u8; N], NativeError> { + let before = stamp(file)?; + if before.len() != N as u64 { + return Err(invalid("artifact_origin_control_size")); + } + let mut cursor = file; + cursor.seek(SeekFrom::Start(0))?; + let mut bytes = [0; N]; + cursor.read_exact(&mut bytes)?; + recheck(before, file)?; + Ok(bytes) +} +fn observe_scope(root: &RootedDirectory) -> Result, NativeError> { + let coordination = root.open_readonly_file(b".use.lock")?; + let coordination_stamp = stamp(&coordination)?; + let Some(incarnation) = locks::read_coordination_incarnation(&coordination)? else { + return Ok(None); + }; + let cohort = root.open_readonly_file(b".use-cohort.json")?; + let cohort_stamp = stamp(&cohort)?; + if cohort_stamp.len() > 1024 { + return Ok(None); + } + let mut bytes = [0; 1024]; + let mut cursor = &cohort; + cursor.read_exact(&mut bytes[..cohort_stamp.len() as usize])?; + let Ok(record) = serde_json::from_slice::(&bytes[..cohort_stamp.len() as usize]) else { + return Ok(None); + }; + let _ = record.origin; + if record.version != 2 + || record.root.physical() != root.identity() + || record.coordination.physical() != coordination_stamp.identity() + || record.coordination_incarnation != incarnation + { + return Ok(None); + } + let binding_file = root.open_readonly_file(maintenance_controls::BINDING_NAME)?; + let binding = PublisherBinding::decode(&read_exact::<{ maintenance_controls::BINDING_BYTES }>( + &binding_file, + )?) + .map_err(|_| invalid("artifact_origin_publisher_binding_unknown"))?; + if binding.root != root.identity() + || binding.coordination != coordination_stamp.identity() + || binding.incarnation != incarnation + { + return Ok(None); + } + if locks::file_identity(&root.open_readonly_file(maintenance_controls::SELECTOR_NAME)?)? + != binding.selector + { + return Ok(None); + } + recheck(coordination_stamp, &coordination)?; + recheck(cohort_stamp, &cohort)?; + Ok(Some(OriginScope { + root: root.identity(), + coordination: coordination_stamp.identity(), + cohort: cohort_stamp.identity(), + publication_binding: locks::file_identity(&binding_file)?, + selector: binding.selector, + incarnation, + })) +} +/// Existing-only fixed root/cohort/binding facts. No producer capability, +/// reference completeness, lifecycle or mutation authority is returned. +pub(crate) fn observe_artifact_root_scope( + root: &RootedDirectory, + memory: &CleanupMemory, +) -> Result, NativeError> { + let _floor = memory + .reserve( + 8192 + std::mem::size_of::() * 2 + + std::mem::size_of::() * 8, + ) + .map_err(|_| invalid("artifact_scope_memory_limit"))?; + observe_scope(root) +} +/// Noncreating fixed-size observation. An incomplete reused receipt NEVER +/// establishes creation of the existing payload. Complete facts require actual +/// unchanged byte integrity. The caller still proves references and authority. +#[cfg(test)] +pub(crate) fn observe_artifact_origin( + root: &RootedDirectory, + directory: &RootedDirectory, + payload: &File, + key: [u8; 32], + memory: &CleanupMemory, +) -> Result { + observe_origin(root, directory, payload, key, memory, true, &|| false) +} +/// Read-only producer-restoration evidence for a retained, originally created +/// key directory whose payload is absent. This does not verify old payload +/// completion, loader usability, references, or any deletion authority. +pub(crate) fn observe_published_origin_without_payload( + lease: &RootPublicationLease<'_, '_>, + root: &RootedDirectory, + directory: &RootedDirectory, + key: [u8; 32], +) -> Result { + if !lease.revalidate_existing()? || observe_scope(root)? != Some(scope(lease)) { + return Ok(false); + } + match directory.open_readonly_file(PAYLOAD_NAME) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} + _ => return Ok(false), + } + let file = match directory.open_readonly_file(ORIGIN_NAME) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false), + Err(error) => return Err(error.into()), + }; + let before = stamp(&file)?; + if before.len() != MAX_BYTES as u64 { + return Ok(false); + } + let bytes = read_exact::(&file)?; + let mut previous: Option = None; + for (index, image) in bytes.chunks_exact(FRAME).enumerate() { + let Some(facts) = decode(image.try_into().unwrap()) else { + return Ok(false); + }; + if usize::from(facts.phase.number()) != index + || facts.scope != scope(lease) + || facts.key != key + || facts.directory != directory.identity() + || facts.receipt != before.identity() + || !facts.directory_created + || !facts.payload_created + || locks::created_file_nonce(&file)? != Some(facts.operation_nonce) + { + return Ok(false); + } + if let Some(old) = previous { + let mut comparable = facts; + comparable.phase = old.phase; + comparable.byte_len = old.byte_len; + comparable.payload_sha256 = old.payload_sha256; + comparable.payload_stamp = old.payload_stamp; + if comparable != old + || (index == 3 + && (facts.byte_len != old.byte_len + || facts.payload_sha256 != old.payload_sha256 + || facts.payload_stamp != old.payload_stamp)) + { + return Ok(false); + } + } + previous = Some(facts); + } + let facts = previous.unwrap(); + recheck(before, &file)?; + if locks::file_identity(&directory.open_readonly_file(ORIGIN_NAME)?)? != before.identity() + || locks::created_file_nonce(&file)? != Some(facts.operation_nonce) + || !lease.revalidate_existing()? + || observe_scope(root)? != Some(scope(lease)) + { + return Ok(false); + } + Ok(matches!(directory.open_readonly_file(PAYLOAD_NAME), + Err(error) if error.kind() == std::io::ErrorKind::NotFound)) +} + +/// Facts for a journal-bound detached payload. The caller must independently +/// prove the exact private address and incarnation from its durable journal. +/// This function provides no publication, reference or deletion authority. +pub(crate) fn observe_detached_artifact_origin( + root: &RootedDirectory, + directory: &RootedDirectory, + payload: &File, + key: [u8; 32], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + observe_origin(root, directory, payload, key, memory, false, cancel) +} +pub(crate) fn observe_artifact_origin_checked( + root: &RootedDirectory, + directory: &RootedDirectory, + payload: &File, + key: [u8; 32], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + observe_origin(root, directory, payload, key, memory, true, cancel) +} +fn observe_origin( + root: &RootedDirectory, + directory: &RootedDirectory, + payload: &File, + key: [u8; 32], + memory: &CleanupMemory, + require_named: bool, + cancel: &dyn Fn() -> bool, +) -> Result { + let floor = std::mem::size_of::() * 5 + + MAX_BYTES + + FRAME + + 1024 + + 8192 + + std::mem::size_of::() * 8 + + std::mem::size_of::() * 8 + + std::mem::size_of::() * 4 + + 2048; + let _floor = memory + .reserve(floor) + .map_err(|_| invalid("artifact_origin_memory_limit"))?; + let scope = match observe_scope(root) { + Ok(Some(scope)) => scope, + _ => return Ok(OriginObservation::Unknown("root_origin_scope_unverified")), + }; + let file = match directory.open_readonly_file(ORIGIN_NAME) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + return Ok(OriginObservation::Missing) + } + Err(error) => return Err(error.into()), + }; + let before = stamp(&file)?; + if before.len() < FRAME as u64 || before.len() > MAX_BYTES as u64 { + return Ok(OriginObservation::Unknown( + "origin_size_or_unbound_creation", + )); + } + let mut bytes = [0; MAX_BYTES]; + let len = before.len() as usize; + let mut cursor = &file; + cursor.read_exact(&mut bytes[..len])?; + let full = len / FRAME; + let mut latest: Option = None; + for i in 0..full { + let image: &[u8; FRAME] = bytes[i * FRAME..(i + 1) * FRAME].try_into().unwrap(); + let Some(facts) = decode(image) else { + return Ok(OriginObservation::Unknown("origin_frame_invalid")); + }; + if usize::from(facts.phase.number()) != i + || facts.scope != scope + || facts.key != key + || facts.directory != directory.identity() + || facts.payload != locks::file_identity(payload)? + || facts.receipt != before.identity() + { + return Ok(OriginObservation::Unknown("origin_binding_changed")); + } + if let Some(old) = latest { + let mut comparable = facts; + comparable.phase = old.phase; + comparable.byte_len = old.byte_len; + comparable.payload_sha256 = old.payload_sha256; + comparable.payload_stamp = old.payload_stamp; + if comparable != old { + return Ok(OriginObservation::Unknown("origin_operation_changed")); + } + if i == 3 + && (facts.byte_len != old.byte_len || facts.payload_sha256 != old.payload_sha256) + { + return Ok(OriginObservation::Unknown("origin_completion_changed")); + } + } + latest = Some(facts); + } + let mut facts = latest.unwrap(); + if locks::created_file_nonce(payload)? != Some(facts.payload_nonce) { + return Ok(OriginObservation::Unknown( + "origin_payload_incarnation_changed", + )); + } + let tail = len % FRAME; + if tail != 0 { + if full >= 4 { + return Ok(OriginObservation::Unknown("origin_extra_bytes")); + } + let mut next = facts; + next.phase = [ + OriginPhase::Bound, + OriginPhase::Writing, + OriginPhase::Complete, + OriginPhase::Published, + ][full]; + if full == 2 { + let (n, d) = hash_payload_checked(payload, cancel)?; + next.byte_len = n; + next.payload_sha256 = d; + next.payload_stamp = Some(stamp(payload)?); + } + if bytes[full * FRAME..len] != encode(next)[..tail] { + return Ok(OriginObservation::Unknown("origin_torn_unknown_bytes")); + } + facts.torn_tail = true; + } + if facts.phase.number() >= 2 { + let (len, digest) = hash_payload_checked(payload, cancel)?; + if len != facts.byte_len || digest != facts.payload_sha256 { + return Ok(OriginObservation::Unknown( + "origin_payload_integrity_changed", + )); + } + } + recheck(before, &file)?; + if locks::file_identity(&directory.open_readonly_file(ORIGIN_NAME)?)? != facts.receipt + || (require_named + && locks::file_identity(&directory.open_readonly_file(PAYLOAD_NAME)?)? != facts.payload) + { + return Ok(OriginObservation::Unknown( + "origin_named_incarnation_changed", + )); + } + if observe_scope(root)? != Some(scope) { + return Ok(OriginObservation::Unknown("origin_scope_changed")); + } + Ok(OriginObservation::Observed(facts)) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn dc_origin_actual_creation_frames_interrupted_publication_and_torn_prefix() { + let path = std::env::temp_dir().join(format!( + "dc-origin-real-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + use super::super::{ + layout::{create_fresh_reference_root, ManagedLayout}, + locks::{open_locked, LockMode}, + maintenance_ownership::{initialize_fresh_reference_domain, ReferenceDomain}, + }; + std::fs::create_dir_all(&path).unwrap(); + let path = std::fs::canonicalize(path).unwrap(); + let layout = ManagedLayout::new(path.join("storage")); + let fresh = create_fresh_reference_root(layout.storage_root()) + .unwrap() + .unwrap(); + layout.ensure_roots().unwrap(); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + // Consume this non-owning proof before releasing or checking its lender. + #[allow(clippy::drop_non_drop)] + drop(writer); + let mutation = super::super::maintenance_ownership::ReferenceMutation::begin_producing( + &layout.artifacts_root(), + ) + .unwrap(); + let mut capability = mutation + .artifact_root_producer_capability() + .unwrap() + .unwrap(); + let lease = maintenance_controls::try_begin_root_publication(&mut capability) + .unwrap() + .unwrap(); + let root = lease.directory(); + let directory = root.create_directory(b"actual-entry").unwrap(); + let mut payload = directory.create_new(PAYLOAD_NAME).unwrap(); + let key = [17; 32]; + let mut writer = + OriginWriter::create(&lease, &directory, &payload, key, true, true).unwrap(); + let memory = CleanupMemory::new(128 * 1024).unwrap(); + let observe = |payload: &File| { + observe_artifact_origin(root, &directory, payload, key, &memory).unwrap() + }; + assert!(matches!( + observe(&payload), + OriginObservation::Observed(OriginFacts { + phase: OriginPhase::Bound, + payload_created: true, + .. + }) + )); + writer.writing().unwrap(); + payload + .write_all(b"actual accepted producer bytes") + .unwrap(); + payload.sync_all().unwrap(); + let (len, digest) = hash_payload(&payload).unwrap(); + writer.complete(&directory, &payload, len, digest).unwrap(); + assert!(matches!( + observe(&payload), + OriginObservation::Observed(OriginFacts { + phase: OriginPhase::Complete, + .. + }) + )); + let next = OriginFacts { + phase: OriginPhase::Published, + ..writer.facts + }; + writer.file.write_all(&encode(next)[..31]).unwrap(); + writer.file.sync_all().unwrap(); + assert!(matches!( + observe(&payload), + OriginObservation::Observed(OriginFacts { + phase: OriginPhase::Complete, + torn_tail: true, + .. + }) + )); + writer.file.write_all(b"bad trailing bytes").unwrap(); + writer.file.sync_all().unwrap(); + assert!(matches!(observe(&payload), OriginObservation::Unknown(_))); + writer.file.set_len(3 * FRAME as u64).unwrap(); + writer.file.seek(SeekFrom::End(0)).unwrap(); + writer.published(&lease, &directory).unwrap(); + assert!(matches!( + observe(&payload), + OriginObservation::Observed(OriginFacts { + phase: OriginPhase::Published, + .. + }) + )); + payload.seek(SeekFrom::Start(0)).unwrap(); + payload.write_all(b"X").unwrap(); + payload.sync_all().unwrap(); + assert_eq!( + observe(&payload), + OriginObservation::Unknown("origin_payload_integrity_changed") + ); + // Consume this non-owning proof before releasing or checking its lender. + #[allow(clippy::drop_non_drop)] + drop(writer); + drop(payload); + drop(directory); + drop(lease); + // Consume this non-owning proof before releasing or checking its lender. + #[allow(clippy::drop_non_drop)] + drop(capability); + drop(mutation); + std::fs::remove_dir_all(path).unwrap(); + } + #[test] + fn dc_origin_frames_fail_closed_on_wrong_key_scope_identity_and_extra_bytes() { + let id = FileIdentity::from_fixed_words(1, 2); + let facts = OriginFacts { + scope: OriginScope { + root: id, + coordination: id, + cohort: id, + publication_binding: id, + selector: id, + incarnation: [1; 32], + }, + key: [2; 32], + operation_nonce: [3; 32], + directory: id, + payload: id, + receipt: id, + directory_created: true, + payload_created: true, + phase: OriginPhase::Bound, + byte_len: 0, + payload_sha256: [0; 32], + payload_nonce: [4; 32], + payload_stamp: None, + torn_tail: false, + }; + let image = encode(facts); + assert_eq!(decode(&image), Some(facts)); + for offset in [ + 0, 8, 16, 24, 25, 32, 64, 96, 128, 208, 224, 240, 264, 480, 511, + ] { + let mut corrupt = image; + corrupt[offset] ^= 0x80; + assert_eq!(decode(&corrupt), None); + } + } +} + +pub(crate) const WORKSPACE_ORIGIN_FRAME_BYTES: usize = 1024; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum WorkspaceOriginPhase { + Bound, + Complete, +} +/// Decoder facts only; callers separately match every field against an actual +/// admitted workspace scope, output nonce, parent and receipt incarnation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct WorkspaceOriginFacts { + pub(crate) directory: bool, + pub(crate) control: bool, + pub(crate) fifo: bool, + pub(crate) intended_name: [u8; 255], + pub(crate) intended_name_len: u8, + pub(crate) binding: maintenance_controls::WorkspaceOutputBinding, + pub(crate) receipt: FileIdentity, + pub(crate) phase: WorkspaceOriginPhase, + pub(crate) byte_len: u64, + pub(crate) payload_sha256: [u8; 32], +} +fn workspace_frame(f: WorkspaceOriginFacts) -> [u8; WORKSPACE_ORIGIN_FRAME_BYTES] { + let mut bytes = [0; WORKSPACE_ORIGIN_FRAME_BYTES]; + bytes[..8].copy_from_slice(b"CGWOUT01"); + bytes[8] = u8::from(f.phase == WorkspaceOriginPhase::Complete); + bytes[9] = f.binding.name_len; + bytes[10] = if f.fifo { + 3 + } else if f.control { + 2 + } else { + u8::from(f.directory) + }; + bytes[16..48].copy_from_slice(&f.binding.incarnation); + bytes[48..80].copy_from_slice(&f.binding.operation); + for (index, id) in [ + f.binding.root, + f.binding.coordination, + f.binding.registration, + f.binding.publication_binding, + f.binding.registry, + f.binding.parent, + f.binding.output, + f.receipt, + ] + .iter() + .enumerate() + { + let at = 80 + index * 16; + identity(&mut bytes[at..at + 16], *id); + } + bytes[208..216].copy_from_slice(&f.byte_len.to_le_bytes()); + bytes[216..248].copy_from_slice(&f.payload_sha256); + bytes[248..503].copy_from_slice(&f.binding.name); + bytes[503] = f.intended_name_len; + bytes[504..759].copy_from_slice(&f.intended_name); + let hash = Sha256::digest(&bytes[..992]); + bytes[992..].copy_from_slice(&hash); + bytes +} +fn workspace_fact(bytes: &[u8; WORKSPACE_ORIGIN_FRAME_BYTES]) -> Option { + let len = usize::from(bytes[9]); + if &bytes[..8] != b"CGWOUT01" + || bytes[8] > 1 + || len == 0 + || bytes[10] > 3 + || matches!(&bytes[248..248 + len], b"." | b"..") + || (bytes[10] == 3 && &bytes[248..248 + len] != b"writers.fifo") + || bytes[11..16] + .iter() + .chain(bytes[759..992].iter()) + .any(|b| *b != 0) + || bytes[248 + len..503].iter().any(|b| *b != 0) + || bytes[248..248 + len] + .iter() + .any(|b| *b == 0 || *b == b'/' || *b == b'\\') + || bytes[992..] != Sha256::digest(&bytes[..992])[..] + || bytes[16..48].iter().all(|b| *b == 0) + || bytes[48..80].iter().all(|b| *b == 0) + { + return None; + } + let intended_len = bytes[503] as usize; + if (bytes[10] != 2 && bytes[503..759].iter().any(|b| *b != 0)) + || (bytes[10] == 2 + && (intended_len == 0 + || matches!(&bytes[504..504 + intended_len], b"." | b"..") + || bytes[504 + intended_len..759].iter().any(|b| *b != 0) + || bytes[504..504 + intended_len] + .iter() + .any(|b| matches!(*b, 0 | b'/' | b'\\')))) + { + return None; + } + let phase = if bytes[8] == 0 { + WorkspaceOriginPhase::Bound + } else { + WorkspaceOriginPhase::Complete + }; + let byte_len = u64::from_le_bytes(bytes[208..216].try_into().ok()?); + let payload_sha256 = bytes[216..248].try_into().ok()?; + if (phase == WorkspaceOriginPhase::Bound && (byte_len != 0 || payload_sha256 != [0; 32])) + || (bytes[10] != 0 && phase != WorkspaceOriginPhase::Bound) + { + return None; + } + Some(WorkspaceOriginFacts { + directory: bytes[10] == 1, + control: bytes[10] == 2, + fifo: bytes[10] == 3, + intended_name: bytes[504..759].try_into().ok()?, + intended_name_len: bytes[503], + binding: maintenance_controls::WorkspaceOutputBinding { + root: read_identity(&bytes[80..96]), + coordination: read_identity(&bytes[96..112]), + registration: read_identity(&bytes[112..128]), + publication_binding: read_identity(&bytes[128..144]), + registry: read_identity(&bytes[144..160]), + parent: read_identity(&bytes[160..176]), + output: read_identity(&bytes[176..192]), + incarnation: bytes[16..48].try_into().ok()?, + operation: bytes[48..80].try_into().ok()?, + name: bytes[248..503].try_into().ok()?, + name_len: bytes[9], + }, + receipt: read_identity(&bytes[192..208]), + phase, + byte_len, + payload_sha256, + }) +} +fn workspace_bound( + binding: maintenance_controls::WorkspaceOutputBinding, + receipt: FileIdentity, +) -> WorkspaceOriginFacts { + WorkspaceOriginFacts { + directory: false, + control: false, + fifo: false, + intended_name: [0; 255], + intended_name_len: 0, + binding, + receipt, + phase: WorkspaceOriginPhase::Bound, + byte_len: 0, + payload_sha256: [0; 32], + } +} +/// Called by a real O_EXCL producer token BEFORE any payload bytes are written. +/// Failure is optional tracking failure, never permission to adopt a collision. +pub(crate) fn record_created_workspace_output( + output: &maintenance_controls::WorkspaceOutputFile, +) -> Result<(), NativeError> { + let mut receipt = output.create_origin()?; + let bound = workspace_frame(workspace_bound(output.origin_binding(), receipt.identity())); + receipt.write_all(&bound)?; + receipt.sync_all()?; + receipt.sync_parent()?; + receipt.seek(SeekFrom::Start(0))?; + let mut read = [0; WORKSPACE_ORIGIN_FRAME_BYTES]; + receipt.read_exact(&mut read)?; + if read != bound || receipt.len()? != WORKSPACE_ORIGIN_FRAME_BYTES as u64 { + return Err(invalid("workspace_creation_origin_readback_changed")); + } + Ok(()) +} +/// An actual control creation binds only its temporary name and the producer's +/// single intended atomic-publication name, never arbitrary renamed aliases. +pub(crate) fn record_created_workspace_control( + output: &maintenance_controls::WorkspaceCreatedControl, +) -> Result<(), NativeError> { + let intended = output.intended_name(); + if intended.is_empty() || intended.len() > 255 { + return Err(invalid("workspace_control_name_invalid")); + } + let mut receipt = output.create_origin()?; + let mut facts = workspace_bound(output.origin_binding(), receipt.identity()); + facts.control = true; + facts.intended_name_len = intended.len() as u8; + facts.intended_name[..intended.len()].copy_from_slice(intended); + let frame = workspace_frame(facts); + receipt.write_all(&frame)?; + receipt.sync_all()?; + receipt.sync_parent()?; + receipt.seek(SeekFrom::Start(0))?; + let mut actual = [0; 1024]; + receipt.read_exact(&mut actual)?; + if actual != frame || receipt.len()? != 1024 { + return Err(invalid("workspace_control_origin_readback_changed")); + } + Ok(()) +} +/// Creation-only FIFO proof. The producer holds the actual FIFO and its +/// registry pin; neither a name nor a decoded frame can construct this token. +#[cfg(unix)] +pub(crate) fn record_created_workspace_fifo( + output: &maintenance_controls::WorkspaceCreatedFifo, +) -> Result<(), NativeError> { + let mut receipt = output.create_origin()?; + let mut facts = workspace_bound(output.origin_binding(), receipt.identity()); + facts.fifo = true; + let frame = workspace_frame(facts); + receipt.write_all(&frame)?; + receipt.sync_all()?; + receipt.sync_parent()?; + receipt.seek(SeekFrom::Start(0))?; + let mut actual = [0; WORKSPACE_ORIGIN_FRAME_BYTES]; + receipt.read_exact(&mut actual)?; + if actual != frame || receipt.len()? != WORKSPACE_ORIGIN_FRAME_BYTES as u64 { + return Err(invalid("workspace_fifo_origin_readback_changed")); + } + Ok(()) +} +/// Creation fact for one genuine new directory. It never certifies ownership +/// of children; removal still requires matching incarnation and atomic-empty. +pub(crate) fn record_created_workspace_directory( + output: &maintenance_controls::WorkspaceCreatedDirectory, +) -> Result<(), NativeError> { + let mut receipt = output.create_origin()?; + let mut facts = workspace_bound(output.origin_binding(), receipt.identity()); + facts.directory = true; + let bound = workspace_frame(facts); + receipt.write_all(&bound)?; + receipt.sync_all()?; + receipt.sync_parent()?; + receipt.seek(SeekFrom::Start(0))?; + let mut read = [0; WORKSPACE_ORIGIN_FRAME_BYTES]; + receipt.read_exact(&mut read)?; + if read != bound || receipt.len()? != WORKSPACE_ORIGIN_FRAME_BYTES as u64 { + return Err(invalid("workspace_directory_origin_readback_changed")); + } + Ok(()) +} +/// The completed token retains the genuine creation, writer and reference +/// mutation through this call. A completed legacy/open-existing file cannot +/// construct the token. Only matching own partial completion prefixes resume. +pub(crate) fn record_completed_workspace_output( + output: &maintenance_controls::WorkspaceCompletedOutput, +) -> Result<(), NativeError> { + let mut receipt = match output.open_origin() { + Ok(receipt) => receipt, + Err(NativeError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { + let mut receipt = output.create_origin()?; + let bound = + workspace_frame(workspace_bound(output.origin_binding(), receipt.identity())); + receipt.write_all(&bound)?; + receipt.sync_all()?; + receipt.sync_parent()?; + receipt + } + Err(error) => return Err(error), + }; + let bound = workspace_bound(output.origin_binding(), receipt.identity()); + let bound_bytes = workspace_frame(bound); + let mut complete = bound; + complete.phase = WorkspaceOriginPhase::Complete; + complete.byte_len = output.len(); + complete.payload_sha256 = output.digest(); + let complete_bytes = workspace_frame(complete); + let len = receipt.len()?; + if !(WORKSPACE_ORIGIN_FRAME_BYTES as u64..=2 * WORKSPACE_ORIGIN_FRAME_BYTES as u64) + .contains(&len) + { + return Err(invalid("workspace_origin_unknown_size_preserved")); + } + let len = len as usize; + let mut read = [0; 2 * WORKSPACE_ORIGIN_FRAME_BYTES]; + receipt.seek(SeekFrom::Start(0))?; + receipt.read_exact(&mut read[..len])?; + let tail = len - WORKSPACE_ORIGIN_FRAME_BYTES; + if read[..WORKSPACE_ORIGIN_FRAME_BYTES] != bound_bytes + || read[WORKSPACE_ORIGIN_FRAME_BYTES..len] != complete_bytes[..tail] + { + return Err(invalid("workspace_origin_unknown_bytes_preserved")); + } + if tail != WORKSPACE_ORIGIN_FRAME_BYTES { + receipt.seek(SeekFrom::Start(WORKSPACE_ORIGIN_FRAME_BYTES as u64))?; + receipt.write_all(&complete_bytes)?; + receipt.sync_all()?; + receipt.sync_parent()?; + } + receipt.seek(SeekFrom::Start(WORKSPACE_ORIGIN_FRAME_BYTES as u64))?; + receipt.read_exact(&mut read[..WORKSPACE_ORIGIN_FRAME_BYTES])?; + if read[..WORKSPACE_ORIGIN_FRAME_BYTES] != complete_bytes + || receipt.len()? != 2 * WORKSPACE_ORIGIN_FRAME_BYTES as u64 + { + return Err(invalid("workspace_completion_origin_readback_changed")); + } + Ok(()) +} +/// Strict fixed facts codec. A partial Complete prefix is accepted only after +/// the caller supplied the actual stable payload bytes' length and digest. +pub(crate) fn decode_workspace_output_origin( + bytes: &[u8], + receipt: FileIdentity, + actual: Option<(u64, [u8; 32])>, + memory: &CleanupMemory, +) -> Result { + let _floor = memory + .reserve( + 3 * std::mem::size_of::() + + 2 * WORKSPACE_ORIGIN_FRAME_BYTES + + 512, + ) + .map_err(|_| invalid("workspace_origin_memory_limit"))?; + if !(WORKSPACE_ORIGIN_FRAME_BYTES..=2 * WORKSPACE_ORIGIN_FRAME_BYTES).contains(&bytes.len()) { + return Err(invalid("workspace_origin_unknown_size")); + } + let bound = workspace_fact(bytes[..WORKSPACE_ORIGIN_FRAME_BYTES].try_into().unwrap()) + .ok_or_else(|| invalid("workspace_origin_unknown_bound"))?; + if bound.phase != WorkspaceOriginPhase::Bound || bound.receipt != receipt { + return Err(invalid("workspace_origin_binding_changed")); + } + let tail = &bytes[WORKSPACE_ORIGIN_FRAME_BYTES..]; + if (bound.directory || bound.control || bound.fifo) && !tail.is_empty() { + return Err(invalid("workspace_directory_unexpected_completion")); + } + if tail.is_empty() { + return Ok(bound); + } + if tail.len() == WORKSPACE_ORIGIN_FRAME_BYTES { + let complete = workspace_fact(tail.try_into().unwrap()) + .ok_or_else(|| invalid("workspace_origin_unknown_completion"))?; + let mut comparable = complete; + comparable.phase = WorkspaceOriginPhase::Bound; + comparable.byte_len = 0; + comparable.payload_sha256 = [0; 32]; + if complete.phase != WorkspaceOriginPhase::Complete || comparable != bound { + return Err(invalid("workspace_origin_operation_changed")); + } + let (len, hash) = actual.ok_or_else(|| invalid("workspace_origin_integrity_required"))?; + if len != complete.byte_len || hash != complete.payload_sha256 { + return Err(invalid("workspace_origin_payload_changed")); + } + return Ok(complete); + } + let (len, hash) = actual.ok_or_else(|| invalid("workspace_origin_unverified_partial"))?; + let mut complete = bound; + complete.phase = WorkspaceOriginPhase::Complete; + complete.byte_len = len; + complete.payload_sha256 = hash; + if tail != &workspace_frame(complete)[..tail.len()] { + return Err(invalid("workspace_origin_unknown_partial")); + } + Ok(bound) +} + +#[cfg(test)] +mod workspace_origin_tests { + use super::*; + fn bound() -> WorkspaceOriginFacts { + let id = FileIdentity::from_fixed_words(1, 2); + let mut name = [0; 255]; + name[..11].copy_from_slice(b"nodes-0.csv"); + workspace_bound( + maintenance_controls::WorkspaceOutputBinding { + root: id, + coordination: id, + registration: id, + publication_binding: id, + registry: id, + parent: id, + output: id, + incarnation: [3; 32], + operation: [4; 32], + name, + name_len: 11, + }, + id, + ) + } + #[test] + fn workspace_control_binds_only_two_exact_names_and_never_complete() { + let memory = CleanupMemory::new(32768).unwrap(); + let mut fact = bound(); + fact.control = true; + fact.intended_name[..12].copy_from_slice(b"journal.json"); + fact.intended_name_len = 12; + let mut frame = workspace_frame(fact); + assert_eq!( + decode_workspace_output_origin(&frame, fact.receipt, None, &memory).unwrap(), + fact + ); + let mut completed = fact; + completed.phase = WorkspaceOriginPhase::Complete; + assert!(workspace_fact(&workspace_frame(completed)).is_none()); + frame[504] = b'/'; + let digest = Sha256::digest(&frame[..992]); + frame[992..].copy_from_slice(&digest); + assert!(workspace_fact(&frame).is_none()); + let mut ordinary = workspace_frame(bound()); + ordinary[503] = 1; + ordinary[504] = b'x'; + let digest = Sha256::digest(&ordinary[..992]); + ordinary[992..].copy_from_slice(&digest); + assert!(workspace_fact(&ordinary).is_none()); + let mut bytes = [0; 1025]; + bytes[..1024].copy_from_slice(&workspace_frame(fact)); + assert!( + decode_workspace_output_origin(&bytes, fact.receipt, Some((0, [0; 32])), &memory) + .is_err() + ); + } + #[test] + fn workspace_fifo_proof_is_exact_creation_only_and_rejects_other_names() { + let memory = CleanupMemory::new(32768).unwrap(); + let mut fact = bound(); + fact.fifo = true; + fact.binding.name = [0; 255]; + fact.binding.name[..12].copy_from_slice(b"writers.fifo"); + fact.binding.name_len = 12; + let frame = workspace_frame(fact); + assert_eq!( + decode_workspace_output_origin(&frame, fact.receipt, None, &memory).unwrap(), + fact + ); + for name in [b"reader.fifo".as_slice(), b"..", b"."] { + let mut foreign = fact; + foreign.binding.name = [0; 255]; + foreign.binding.name[..name.len()].copy_from_slice(name); + foreign.binding.name_len = name.len() as u8; + assert!(workspace_fact(&workspace_frame(foreign)).is_none()); + } + let mut completed = fact; + completed.phase = WorkspaceOriginPhase::Complete; + assert!(workspace_fact(&workspace_frame(completed)).is_none()); + let mut tail = [0; 1025]; + tail[..1024].copy_from_slice(&frame); + assert!(decode_workspace_output_origin(&tail, fact.receipt, None, &memory).is_err()); + let mut intended = fact; + intended.intended_name_len = 1; + intended.intended_name[0] = b'x'; + assert!(workspace_fact(&workspace_frame(intended)).is_none()); + } + #[test] + fn workspace_frames_bind_exact_creation_and_completed_bytes() { + let memory = CleanupMemory::new(32768).unwrap(); + let bound = bound(); + let mut bytes = [0; 2048]; + bytes[..1024].copy_from_slice(&workspace_frame(bound)); + assert_eq!( + decode_workspace_output_origin(&bytes[..1024], bound.receipt, None, &memory).unwrap(), + bound + ); + let mut complete = bound; + complete.phase = WorkspaceOriginPhase::Complete; + complete.byte_len = 7; + complete.payload_sha256 = [8; 32]; + bytes[1024..].copy_from_slice(&workspace_frame(complete)); + assert!(decode_workspace_output_origin(&bytes, bound.receipt, None, &memory).is_err()); + assert_eq!( + decode_workspace_output_origin(&bytes, bound.receipt, Some((7, [8; 32])), &memory) + .unwrap(), + complete + ); + assert!( + decode_workspace_output_origin(&bytes, bound.receipt, Some((8, [8; 32])), &memory) + .is_err() + ); + for offset in [16, 80, 176, 192, 208, 248, 1024 + 48, 2047] { + let mut corrupt = bytes; + corrupt[offset] ^= 1; + assert!(decode_workspace_output_origin( + &corrupt, + bound.receipt, + Some((7, [8; 32])), + &memory + ) + .is_err()); + } + } + #[test] + fn workspace_matching_partial_completion_is_creation_only() { + let memory = CleanupMemory::new(32768).unwrap(); + let bound = bound(); + let mut complete = bound; + complete.phase = WorkspaceOriginPhase::Complete; + complete.byte_len = 10; + complete.payload_sha256 = [9; 32]; + let mut bytes = [0; 2048]; + bytes[..1024].copy_from_slice(&workspace_frame(bound)); + bytes[1024..].copy_from_slice(&workspace_frame(complete)); + for tail in [1, 96, 217, 1023] { + assert!(decode_workspace_output_origin( + &bytes[..1024 + tail], + bound.receipt, + None, + &memory + ) + .is_err()); + assert_eq!( + decode_workspace_output_origin( + &bytes[..1024 + tail], + bound.receipt, + Some((10, [9; 32])), + &memory + ) + .unwrap(), + bound + ); + } + } +} diff --git a/src/storage/maintenance_ownership.rs b/src/storage/maintenance_ownership.rs new file mode 100644 index 00000000..cd734055 --- /dev/null +++ b/src/storage/maintenance_ownership.rs @@ -0,0 +1,6304 @@ +//! Ownership capabilities for maintenance. Inventory observations are never +//! capabilities; every capability below retains the actual OS locks. +use super::atomic::write_json_atomically; +#[cfg(test)] +use super::containment::{probe_descriptor, BuildDescriptorV2}; +use super::inventory::real_path; +#[cfg(test)] +use super::inventory::{bound_roles_overlap, default_direct, observe_binding, ObservedBinding}; +#[cfg(test)] +use super::inventory::{same_physical_path, DirectPair, MaintenanceTarget}; +use super::layout::{DirectLayout, ManagedLayout}; +use super::locks::{ + open_locked, open_shared_coordination, path_identity, try_open_existing_locked, + try_open_locked_in_existing_parent, FileIdentity, LockMode, LockedFile, WriterLease, +}; +use crate::error::NativeError; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::fs::{self, OpenOptions}; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +// This record is invalidation evidence, never deletion authority. Every read +// and write has a fixed byte bound; no JSON manifest/configuration is read here. +const REFERENCE_EPOCH_SIZE: usize = 2048; +const REFERENCE_EPOCH_MAGIC: &[u8; 8] = b"CGRFE001"; +const REFERENCE_EPOCH_FILE: &str = ".reference-epoch"; +const REFERENCE_ACTIVITY_FILE: &str = ".reference-activity.lock"; +const REFERENCE_SERIAL_FILE: &str = ".reference-epoch.lock"; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +struct ReferenceScopeRecord { + kind: u8, + destination: FileIdentity, + writer: FileIdentity, + address: [u8; 32], + reference_roots: [Option; 2], +} + +/// Observational scope, not authority or a coverage-creation permit. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ReferenceDomain { + artifact_root: PathBuf, + artifact_identity: Option, + use_identity: Option, + destination_root: PathBuf, + writer_path: PathBuf, + raw_address: Option<[PathBuf; 2]>, + reference_roots: [Option; 2], + scope: ReferenceScopeRecord, +} +impl ReferenceDomain { + pub(crate) fn managed(layout: &ManagedLayout) -> Result { + Self::observe( + layout.artifacts_root(), + layout.storage_root().to_owned(), + layout.writer_lock_path(), + [Some(layout.generations_root()), Some(layout.runs_root())], + 1, + ) + } + pub(crate) fn direct(layout: &DirectLayout) -> Result { + let mut domain = Self::observe( + layout.artifact_root_path(), + layout + .db_path() + .parent() + .ok_or_else(|| invalid("reference_destination_parent_missing"))? + .to_owned(), + layout.writer_lock_path(), + [None, None], + 2, + )?; + let addresses = [ + layout.db_path().to_owned(), + layout.manifest_path().to_owned(), + ]; + let mut hash = Sha256::new(); + hash.update(addresses[0].as_os_str().as_encoded_bytes()); + hash.update([0]); + hash.update(addresses[1].as_os_str().as_encoded_bytes()); + domain.scope.address = hash.finalize().into(); + domain.raw_address = Some(addresses); + Ok(domain) + } + fn observe( + artifact_root: PathBuf, + destination_root: PathBuf, + writer_path: PathBuf, + reference_roots: [Option; 2], + kind: u8, + ) -> Result { + let artifact_root = absolute(&artifact_root)?; + let destination_root = absolute(&destination_root)?; + let writer_path = absolute(&writer_path)?; + let reference_roots = [ + reference_roots[0].as_deref().map(absolute).transpose()?, + reference_roots[1].as_deref().map(absolute).transpose()?, + ]; + let scope = ReferenceScopeRecord { + kind, + destination: directory_identity(&destination_root)?, + writer: path_identity(&writer_path)?, + address: Sha256::digest(writer_path.as_os_str().as_encoded_bytes()).into(), + reference_roots: [ + reference_roots[0] + .as_deref() + .map(directory_identity) + .transpose()?, + reference_roots[1] + .as_deref() + .map(directory_identity) + .transpose()?, + ], + }; + let artifact_identity = match fs::symlink_metadata(&artifact_root) { + Ok(_) => Some(directory_identity(&artifact_root)?), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => None, + Err(error) => return Err(error.into()), + }; + let use_identity = optional_resource_identity(&artifact_root.join(".use.lock"))?; + Ok(Self { + artifact_root, + artifact_identity, + use_identity, + destination_root, + writer_path, + raw_address: None, + reference_roots, + scope, + }) + } + pub(crate) fn revalidate(&self) -> Result { + let current_root = match fs::symlink_metadata(&self.artifact_root) { + Ok(_) => Some(directory_identity(&self.artifact_root)?), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => None, + Err(error) => return Err(error.into()), + }; + let address = if let Some(paths) = &self.raw_address { + let mut hash = Sha256::new(); + hash.update(paths[0].as_os_str().as_encoded_bytes()); + hash.update([0]); + hash.update(paths[1].as_os_str().as_encoded_bytes()); + <[u8; 32]>::from(hash.finalize()) + } else { + Sha256::digest(self.writer_path.as_os_str().as_encoded_bytes()).into() + }; + Ok(address == self.scope.address + && current_root == self.artifact_identity + && optional_resource_identity(&self.artifact_root.join(".use.lock"))? + == self.use_identity + && directory_identity(&self.destination_root)? == self.scope.destination + && path_identity(&self.writer_path)? == self.scope.writer + && self.reference_roots[0] + .as_deref() + .map(directory_identity) + .transpose()? + == self.scope.reference_roots[0] + && self.reference_roots[1] + .as_deref() + .map(directory_identity) + .transpose()? + == self.scope.reference_roots[1]) + } + pub(crate) fn allows_runs_root(&self, runs_root: &Path) -> Result { + Ok(self.scope.kind != 1 + || self.scope.reference_roots[1] == Some(directory_identity(runs_root)?)) + } + pub(crate) fn same_scope(&self, other: &Self) -> bool { + self.scope == other.scope + && self.artifact_identity == other.artifact_identity + && self.use_identity == other.use_identity + } + pub(crate) fn artifact_root(&self) -> &Path { + &self.artifact_root + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +struct ReferenceEpochRecord { + version: u32, + root: FileIdentity, + use_lock: FileIdentity, + use_incarnation: [u8; 32], + cohort: FileIdentity, + scope: ReferenceScopeRecord, + incarnation: [u8; 32], + activity: FileIdentity, + serial: FileIdentity, + counter: u64, + covered: bool, +} +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct ReferenceEpoch { + file: FileIdentity, + record: ReferenceEpochRecord, +} +impl ReferenceEpoch { + pub(crate) fn counter(&self) -> u64 { + self.record.counter + } +} +#[derive(Debug)] +// Fixed inline protocol/lease state: boxing would add a separately accounted allocation. +#[allow(clippy::large_enum_variant)] +pub(crate) enum EpochObservation { + Verified(ReferenceEpoch), + Pending(PendingEpochReason), +} + +#[cfg(test)] +type PendingEpochReason = String; +#[cfg(not(test))] +type PendingEpochReason = (); +fn pending_epoch(_reason: &str) -> EpochObservation { + #[cfg(test)] + { + EpochObservation::Pending(_reason.into()) + } + #[cfg(not(test))] + { + EpochObservation::Pending(()) + } +} + +fn encode_reference_epoch( + record: &ReferenceEpochRecord, +) -> Result<[u8; REFERENCE_EPOCH_SIZE], NativeError> { + let payload = serde_json::to_vec(record)?; + if payload.len() > REFERENCE_EPOCH_SIZE - 44 { + return Err(invalid("reference_epoch_record_oversized")); + } + let mut bytes = [0; REFERENCE_EPOCH_SIZE]; + bytes[..8].copy_from_slice(REFERENCE_EPOCH_MAGIC); + bytes[8..12].copy_from_slice(&(payload.len() as u32).to_le_bytes()); + bytes[12..12 + payload.len()].copy_from_slice(&payload); + let checksum = Sha256::digest(&bytes[..REFERENCE_EPOCH_SIZE - 32]); + bytes[REFERENCE_EPOCH_SIZE - 32..].copy_from_slice(&checksum); + Ok(bytes) +} +fn read_reference_epoch(root: &Path) -> Result { + let path = root.join(REFERENCE_EPOCH_FILE); + reject_file_alias(&path)?; + let identity = path_identity(&path)?; + let mut file = fs::File::open(&path)?; + if file.metadata()?.len() != REFERENCE_EPOCH_SIZE as u64 + || super::locks::file_identity(&file)? != identity + { + return Err(invalid("reference_epoch_record_invalid")); + } + let mut bytes = [0; REFERENCE_EPOCH_SIZE]; + file.read_exact(&mut bytes)?; + let len = u32::from_le_bytes( + bytes[8..12] + .try_into() + .map_err(|_| invalid("reference_epoch_length_invalid"))?, + ) as usize; + if &bytes[..8] != REFERENCE_EPOCH_MAGIC + || len > REFERENCE_EPOCH_SIZE - 44 + || Sha256::digest(&bytes[..REFERENCE_EPOCH_SIZE - 32]).as_slice() + != &bytes[REFERENCE_EPOCH_SIZE - 32..] + || path_identity(&path)? != identity + { + return Err(invalid("reference_epoch_record_invalid")); + } + let record: ReferenceEpochRecord = serde_json::from_slice(&bytes[12..12 + len])?; + if record.version != 2 { + return Err(invalid("reference_epoch_version_unknown")); + } + Ok(ReferenceEpoch { + file: identity, + record, + }) +} +fn epoch_root_matches( + root: &Path, + record: &ReferenceEpochRecord, + held_root_use: Option<&LockedFile>, +) -> Result { + for name in [ + ".use.lock", + ".use-cohort.json", + REFERENCE_ACTIVITY_FILE, + REFERENCE_SERIAL_FILE, + ] { + reject_file_alias(&root.join(name))?; + } + let cohort_path = root.join(".use-cohort.json"); + reject_file_alias(&cohort_path)?; + let mut cohort_file = fs::File::open(&cohort_path)?; + let len = cohort_file.metadata()?.len(); + if len > 1024 || super::locks::file_identity(&cohort_file)? != record.cohort { + return Ok(false); + } + let mut bytes = [0; 1024]; + cohort_file.read_exact(&mut bytes[..len as usize])?; + let cohort: ArtifactCohortRecord = serde_json::from_slice(&bytes[..len as usize])?; + let actual_incarnation = if let Some(held) = held_root_use { + if held.identity()? != record.use_lock || !held.is_current()? { + return Ok(false); + } + held.coordination_incarnation()? + } else { + super::locks::read_coordination_incarnation(&fs::File::open(root.join(".use.lock"))?)? + }; + if cohort.version != 2 + || cohort.root != record.root + || cohort.coordination != record.use_lock + || cohort.coordination_incarnation != record.use_incarnation + || actual_incarnation != Some(record.use_incarnation) + || path_identity(&cohort_path)? != record.cohort + { + return Ok(false); + } + Ok(record.root == directory_identity(root)? + && record.use_lock == path_identity(&root.join(".use.lock"))? + && record.activity == path_identity(&root.join(REFERENCE_ACTIVITY_FILE))? + && record.serial == path_identity(&root.join(REFERENCE_SERIAL_FILE))?) +} + +/// Borrows actual Exclusive root ownership throughout genuine domain creation. +/// Fixed identities are captured before invoking other creation factories; no +/// factory needs to reopen the exclusively locked coordination header. +pub(crate) struct RootCreationAuthority<'a> { + lease: &'a ArtifactUseLease, + nonce: [u8; 32], + cohort: FileIdentity, + binding: FileIdentity, +} +impl RootCreationAuthority<'_> { + pub(crate) fn root(&self) -> &Path { + &self.lease.root + } + pub(crate) fn root_identity(&self) -> FileIdentity { + self.lease.identity + } + pub(crate) fn coordination_identity(&self) -> Result { + self.lease.lock.identity() + } + pub(crate) fn incarnation(&self) -> Result<[u8; 32], NativeError> { + Ok(self.nonce) + } + pub(crate) fn cohort_identity(&self) -> Result { + Ok(self.cohort) + } + pub(crate) fn publication_binding_identity(&self) -> Result { + Ok(self.binding) + } +} + +/// Mutating entry points call this BEFORE any coordinator/worker/state lock +/// can create a missing Managed root. Only actual create_dir success supplies +/// birth authority; observing an existing directory never enrolls it. +pub(crate) fn initialize_fresh_managed_domain(layout: &ManagedLayout) -> Result { + let Some(fresh) = super::layout::create_fresh_reference_root(layout.storage_root())? else { + return Ok(false); + }; + layout.ensure_roots()?; + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive)?; + let domain = ReferenceDomain::managed(layout)?; + // Cleanup controls remain optional for primary creation. Partial files are + // finite Unknown and are never repaired/adopted on a later entry. + Ok(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap_or(false)) +} + +/// Exact first mutating Direct boundary. Existing destinations never acquire +/// coverage from a later fresh cache or from observing an empty directory. +pub(crate) fn initialize_fresh_direct_domain(layout: &DirectLayout) -> Result { + let Some(parent) = layout.db_path().parent() else { + return Ok(false); + }; + if layout.manifest_path().parent() != Some(parent) { + return Ok(false); + } + let Some(fresh) = super::layout::create_fresh_reference_root(parent)? else { + return Ok(false); + }; + ArtifactUseLease::ensure_root(&layout.artifact_root_path())?; + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive)?; + let domain = ReferenceDomain::direct(layout)?; + Ok(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap_or(false)) +} + +/// Seed only from a complete, newly-created reference domain and its actual +/// exclusive writer. Ordinary observation/admission and old-domain producers +/// have no creation API. A fresh cache beside an old domain is insufficient. +pub(crate) fn initialize_fresh_reference_domain( + domain: &ReferenceDomain, + fresh: &super::layout::FreshReferenceRoot, + writer: &WriterLease, +) -> Result { + if fresh.identity() != domain.scope.destination + || path_identity(fresh.path())? != fresh.identity() + || !domain.revalidate()? + || !writer.verifies(&domain.writer_path, LockMode::Exclusive)? + { + return Err(invalid("reference_domain_creation_unverified")); + } + let Some(root_use) = ArtifactUseLease::try_exclusive(&domain.artifact_root)? else { + return Ok(false); + }; + if fs::symlink_metadata(domain.artifact_root.join(REFERENCE_EPOCH_FILE)).is_ok() { + return Ok(false); + } + let Some(use_incarnation) = root_use.lock.coordination_incarnation()? else { + return Ok(false); + }; + { + // Every supported startup uses this same stable inode, including an + // idle destination which has never needed a helper process. Admission + // remains noncreating; birth supplies the missing coordination file. + let destination = super::locks::RootedDirectory::open(fresh.path(), fresh.identity())?; + let worker = if let Some(pair) = &domain.raw_address { + DirectLayout::new(&pair[0], &pair[1]).worker_lock_path() + } else { + domain.destination_root.join("worker.lock") + }; + let worker_name = worker + .file_name() + .ok_or_else(|| invalid("worker_coordination_name_missing"))? + .as_encoded_bytes(); + match destination.create_new(worker_name) { + Ok(file) => file.sync_all()?, + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => { + destination.open_readonly_file(worker_name)?; + } + Err(error) => return Err(error.into()), + } + destination.sync_namespace()?; + let creation = RootCreationAuthority { + lease: &root_use, + nonce: use_incarnation, + cohort: match path_identity(&domain.artifact_root.join(".use-cohort.json")) { + Ok(id) => id, + Err(_) => return Ok(false), + }, + binding: match path_identity(&domain.artifact_root.join(".publication.binding")) { + Ok(id) => id, + Err(_) => return Ok(false), + }, + }; + if super::maintenance_workspace_gate::initialize_fresh_workspace_gate( + fresh, writer, &creation, + ) + .is_err() + { + return Ok(false); + } + if domain.scope.kind == 1 + && super::maintenance_generation_gate::initialize_fresh_generation_gate( + fresh, writer, &creation, + ) + .is_err() + { + // Optional cleanup activation never adds a primary publication + // requirement. No covered epoch is published after failed birth. + return Ok(false); + } + } + let mut incarnation = [0; 32]; + getrandom::fill(&mut incarnation) + .map_err(|_| invalid("reference_epoch_entropy_unavailable"))?; + let activity = open_locked( + domain.artifact_root.join(REFERENCE_ACTIVITY_FILE), + LockMode::Exclusive, + )?; + let serial = open_locked( + domain.artifact_root.join(REFERENCE_SERIAL_FILE), + LockMode::Exclusive, + )?; + let record = ReferenceEpochRecord { + version: 2, + root: root_use.identity, + use_lock: root_use.lock.identity()?, + use_incarnation, + cohort: path_identity(&domain.artifact_root.join(".use-cohort.json"))?, + scope: domain.scope.clone(), + incarnation, + activity: activity.identity()?, + serial: serial.identity()?, + counter: 0, + covered: true, + }; + let mut file = OpenOptions::new() + .write(true) + .create_new(true) + .open(domain.artifact_root.join(REFERENCE_EPOCH_FILE))?; + file.write_all(&encode_reference_epoch(&record)?)?; + file.sync_all()?; + super::atomic::sync_dir(&domain.artifact_root)?; + Ok(true) +} + +/// Noncreating fixed-record observation. No authority survives this call and +/// no epoch is captured in the middle of a supported producer's activity. +pub(crate) fn observe_reference_epoch( + domain: &ReferenceDomain, +) -> Result { + let root = &domain.artifact_root; + let activity = + match try_open_existing_locked(&root.join(REFERENCE_ACTIVITY_FILE), LockMode::Exclusive) { + Ok(Some(guard)) => guard, + Ok(None) => return Ok(pending_epoch("reference_activity_in_use")), + Err(_) => return Ok(pending_epoch("reference_epoch_missing_or_unverifiable")), + }; + let epoch = match read_reference_epoch(root) { + Ok(epoch) => epoch, + Err(_) => return Ok(pending_epoch("reference_epoch_missing_or_unverifiable")), + }; + let verified = (|| -> Result { + Ok(activity.is_current()? + && domain.revalidate()? + && epoch_root_matches(root, &epoch.record, None)? + && epoch.record.scope == domain.scope + && epoch.record.covered) + })(); + if !matches!(verified, Ok(true)) { + return Ok(pending_epoch("reference_domain_unverified")); + } + Ok(EpochObservation::Verified(epoch)) +} + +// Private foundation only. Borrowed paths/bytes keep their owning Work and +// reservations OFF the authority stack. This is not PreparedMaintenance, +// current config/reference completeness, Ready admission or deletion authority. +pub(super) struct CanonicalEpochPaths<'a> { + pub(super) root: &'a Path, + pub(super) destination: &'a Path, + pub(super) writer: &'a Path, + pub(super) root_use: &'a Path, + pub(super) cohort: &'a Path, + pub(super) epoch: &'a Path, + pub(super) activity: &'a Path, + pub(super) serial: &'a Path, + pub(super) reference_roots: [Option<&'a Path>; 2], +} +pub(super) struct CanonicalEpochFrame<'a> { + pub(super) paths: CanonicalEpochPaths<'a>, + pub(super) expected: &'a ReferenceEpoch, + pub(super) bytes: &'a [u8], + pub(super) cohort: super::locks::FixedFileStamp, + pub(super) coordination_header: [u8; super::locks::COORDINATION_HEADER_BYTES], +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct CanonicalEpochAdvance { + pub(crate) before: u64, + pub(crate) after: u64, +} + +fn encode_fixed_epoch( + record: &ReferenceEpochRecord, + bytes: &mut [u8], +) -> Result<(), super::locks::FixedControlError> { + use super::locks::{FixedControlError as Error, FixedControlProblem as Problem}; + if bytes.len() != REFERENCE_EPOCH_SIZE { + return Err(Error::Invalid(Problem::BufferTooSmall)); + } + bytes.fill(0); + let len = { + let mut cursor = std::io::Cursor::new(&mut bytes[12..REFERENCE_EPOCH_SIZE - 32]); + serde_json::to_writer(&mut cursor, record) + .map_err(|_| Error::Invalid(Problem::InvalidRecord))?; + cursor.position() as usize + }; + bytes[..8].copy_from_slice(REFERENCE_EPOCH_MAGIC); + bytes[8..12].copy_from_slice(&(len as u32).to_le_bytes()); + let checksum = Sha256::digest(&bytes[..REFERENCE_EPOCH_SIZE - 32]); + bytes[REFERENCE_EPOCH_SIZE - 32..].copy_from_slice(&checksum); + Ok(()) +} + +/// Pure bounded preparation, no locks or writes. The caller reserved both +/// buffers in the shared account. Fixed producer codecs use a slice writer; +/// no arbitrary JSON is decoded and no expected-E bytes are relabeled later. +pub(super) fn prepare_canonical_epoch_frame<'a>( + paths: CanonicalEpochPaths<'a>, + expected: &'a ReferenceEpoch, + prepared: &'a mut super::maintenance_budget::ReservedBytes, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, +) -> Result, super::locks::FixedControlError> { + use super::locks::{ + open_fixed_control, read_fixed_control, FixedControlError as Error, + FixedControlKind as Kind, FixedControlProblem as Problem, FixedFileStamp, + }; + if !expected.record.covered || expected.record.version != 2 { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + let next = expected + .record + .counter + .checked_add(1) + .ok_or(Error::Invalid(Problem::EpochExhausted))?; + if prepared.as_slice().len() < 2 * REFERENCE_EPOCH_SIZE + || read.as_slice().len() < REFERENCE_EPOCH_SIZE + { + return Err(Error::Invalid(Problem::BufferTooSmall)); + } + let mut cohort_file = open_fixed_control(paths.cohort, Kind::File, false, scratch)?; + let cohort = FixedFileStamp::capture(&cohort_file, Kind::File)?; + if cohort.identity() != expected.record.cohort || cohort.len() > 1024 { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + let len = read_fixed_control(&mut cohort_file, &cohort, read)?; + // These are the exact two compact encodings of the current producer + // contract. A different JSON spelling remains unknown, never an implicit + // controlled migration or a reason to repair the record. + let mut recognized = false; + for origin in [ + CohortOrigin::GuardedCreation, + CohortOrigin::ControlledMigration, + ] { + let record = ArtifactCohortRecord { + version: 2, + root: expected.record.root, + coordination: expected.record.use_lock, + coordination_incarnation: expected.record.use_incarnation, + origin, + }; + let mut cursor = std::io::Cursor::new(&mut prepared.as_mut_slice()[..REFERENCE_EPOCH_SIZE]); + serde_json::to_writer(&mut cursor, &record) + .map_err(|_| Error::Invalid(Problem::InvalidRecord))?; + let encoded_len = cursor.position() as usize; + if encoded_len == len && prepared.as_slice()[..len] == read.as_slice()[..len] { + recognized = true; + break; + } + } + if !recognized { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + let current = open_fixed_control(paths.cohort, Kind::File, false, scratch)?; + cohort.recheck(¤t)?; + encode_fixed_epoch( + &expected.record, + &mut prepared.as_mut_slice()[..REFERENCE_EPOCH_SIZE], + )?; + let mut updated = expected.record.clone(); + updated.counter = next; + encode_fixed_epoch( + &updated, + &mut prepared.as_mut_slice()[REFERENCE_EPOCH_SIZE..2 * REFERENCE_EPOCH_SIZE], + )?; + Ok(CanonicalEpochFrame { + paths, + expected, + bytes: &prepared.as_slice()[..2 * REFERENCE_EPOCH_SIZE], + cohort, + coordination_header: super::locks::encode_coordination_incarnation( + expected.record.use_incarnation, + ), + }) +} + +fn fixed_epoch_open( + path: &Path, + kind: super::locks::FixedControlKind, + write: bool, + scratch: &mut super::locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, +) -> Result { + grant.take_entry()?; + let file = super::locks::open_fixed_control(path, kind, write, scratch)?; + grant.check_time()?; + Ok(file) +} + +/// Exact private canonical E -> E+1 step, retaining owned exclusive activity +/// and serial handles through a BORROWED callback. No full Work/closure owner +/// moves in or drops on error/panic; callers retain their writer/root guards. +/// No production caller exists until full bounded admission is reviewed. +#[cfg(test)] +fn with_canonical_epoch_advance( + frame: &CanonicalEpochFrame<'_>, + writer: &WriterLease, + root_use: &ArtifactUseLease, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, + apply: &mut impl FnMut(CanonicalEpochAdvance) -> Result<(), super::locks::FixedControlError>, +) -> Result { + if root_use.identity != frame.expected.record.root || root_use.reference_activity.is_some() { + return Err(super::locks::FixedControlError::Invalid( + super::locks::FixedControlProblem::GuardMismatch, + )); + } + with_canonical_epoch_handles( + frame, + EpochLock::Legacy(writer), + EpochLock::Legacy(&root_use.lock), + read, + scratch, + grant, + 0, + &mut |_, _| Ok(()), + &mut |advance, _| apply(advance), + ) +} + +pub(super) enum EpochLock<'a> { + #[cfg(test)] + Legacy(&'a LockedFile), + Fixed(&'a super::locks::FixedLockedFile), +} +impl EpochLock<'_> { + fn matches_fixed_current( + &self, + stamp: &super::locks::FixedFileStamp, + mode: LockMode, + ) -> Result { + match self { + #[cfg(test)] + Self::Legacy(lock) => lock.matches_fixed_current(stamp, mode), + Self::Fixed(lock) => lock.matches_fixed_current(stamp, mode), + } + } + fn matches_fixed_header( + &self, + header: &[u8; super::locks::COORDINATION_HEADER_BYTES], + ) -> Result { + match self { + #[cfg(test)] + Self::Legacy(lock) => lock.matches_fixed_header(header), + Self::Fixed(lock) => lock.matches_fixed_header(header), + } + } +} +// Keep independent borrowed authorities and caller-owned scratch explicit. +#[allow(clippy::too_many_arguments)] +pub(super) fn with_canonical_epoch_handles( + frame: &CanonicalEpochFrame<'_>, + writer: EpochLock<'_>, + root_use: EpochLock<'_>, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, + effect_entries: usize, + validate: &mut impl FnMut( + &mut super::locks::FixedControlScratch, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError>, + apply: &mut impl FnMut( + CanonicalEpochAdvance, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError>, +) -> Result { + use super::locks::{ + read_fixed_control, FixedControlError as Error, FixedControlKind as Kind, + FixedControlProblem as Problem, FixedFileStamp, FixedLockedFile, + }; + let expected = &frame.expected.record; + grant.check()?; + if read.as_slice().len() < REFERENCE_EPOCH_SIZE || frame.bytes.len() != 2 * REFERENCE_EPOCH_SIZE + { + return Err(Error::Invalid(Problem::BufferTooSmall)); + } + // Check only fixed identities/modes here; generic revalidate/claim helpers + // allocate paths, decode configuration or scan registries and are excluded. + let writer_current = fixed_epoch_open(frame.paths.writer, Kind::File, false, scratch, grant)?; + let writer_stamp = FixedFileStamp::capture(&writer_current, Kind::File)?; + if writer_stamp.identity() != expected.scope.writer + || !writer.matches_fixed_current(&writer_stamp, LockMode::Exclusive)? + { + return Err(Error::Invalid(Problem::GuardMismatch)); + } + let use_current = fixed_epoch_open(frame.paths.root_use, Kind::File, false, scratch, grant)?; + let use_stamp = FixedFileStamp::capture(&use_current, Kind::File)?; + if use_stamp.identity() != expected.use_lock + || !root_use.matches_fixed_current(&use_stamp, LockMode::Exclusive)? + { + return Err(Error::Invalid(Problem::GuardMismatch)); + } + if !root_use.matches_fixed_header(&frame.coordination_header)? { + return Err(Error::Invalid(Problem::Changed)); + } + let activity_file = fixed_epoch_open(frame.paths.activity, Kind::File, true, scratch, grant)?; + let activity = FixedLockedFile::try_lock(activity_file, LockMode::Exclusive)? + .ok_or(Error::Invalid(Problem::Busy))?; + if FixedFileStamp::capture(activity.file(), Kind::File)?.identity() != expected.activity { + return Err(Error::Invalid(Problem::Changed)); + } + let serial_file = fixed_epoch_open(frame.paths.serial, Kind::File, true, scratch, grant)?; + let serial = FixedLockedFile::try_lock(serial_file, LockMode::Exclusive)? + .ok_or(Error::Invalid(Problem::Busy))?; + if FixedFileStamp::capture(serial.file(), Kind::File)?.identity() != expected.serial { + return Err(Error::Invalid(Problem::Changed)); + } + // No shared activity has been acquired by this operation; no observer tries + // to upgrade its own shared guard. Both real controls remain owned below. + for (path, identity) in [ + (frame.paths.root, expected.root), + (frame.paths.destination, expected.scope.destination), + ] { + let file = fixed_epoch_open(path, Kind::Directory, false, scratch, grant)?; + if FixedFileStamp::capture(&file, Kind::Directory)?.identity() != identity { + return Err(Error::Invalid(Problem::Changed)); + } + } + for (path, identity) in frame + .paths + .reference_roots + .iter() + .zip(expected.scope.reference_roots) + { + match (path, identity) { + (Some(path), Some(identity)) => { + let file = fixed_epoch_open(path, Kind::Directory, false, scratch, grant)?; + if FixedFileStamp::capture(&file, Kind::Directory)?.identity() != identity { + return Err(Error::Invalid(Problem::Changed)); + } + } + (None, None) => (), + _ => return Err(Error::Invalid(Problem::InvalidRecord)), + } + } + let cohort = fixed_epoch_open(frame.paths.cohort, Kind::File, false, scratch, grant)?; + frame.cohort.recheck(&cohort)?; + let mut epoch_file = fixed_epoch_open(frame.paths.epoch, Kind::File, true, scratch, grant)?; + let epoch_stamp = FixedFileStamp::capture(&epoch_file, Kind::File)?; + if epoch_stamp.identity() != frame.expected.file + || epoch_stamp.len() != REFERENCE_EPOCH_SIZE as u64 + { + return Err(Error::Invalid(Problem::Changed)); + } + read_fixed_control(&mut epoch_file, &epoch_stamp, read)?; + if read.as_slice()[..REFERENCE_EPOCH_SIZE] != frame.bytes[..REFERENCE_EPOCH_SIZE] { + return Err(Error::Invalid(Problem::EpochMismatch)); + } + // Recheck the actual control addresses, including lock inode replacement, + // AFTER taking their locks and BEFORE changing the captured epoch E. + for (path, identity) in [ + (frame.paths.writer, writer_stamp.identity()), + (frame.paths.root_use, use_stamp.identity()), + (frame.paths.activity, expected.activity), + (frame.paths.serial, expected.serial), + ] { + let current = fixed_epoch_open(path, Kind::File, false, scratch, grant)?; + if FixedFileStamp::capture(¤t, Kind::File)?.identity() != identity { + return Err(Error::Invalid(Problem::Changed)); + } + } + let current_root = fixed_epoch_open(frame.paths.root, Kind::Directory, false, scratch, grant)?; + if FixedFileStamp::capture(¤t_root, Kind::Directory)?.identity() != expected.root { + return Err(Error::Invalid(Problem::Changed)); + } + // Reserve the epoch file effect before starting it. Encoding and checksum + // were already prepared outside authority; no decode/hash/state lock here. + validate(scratch, grant)?; + grant.take_entries(effect_entries.checked_add(2).ok_or(Error::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?)?; // complete effect reservation before E changes + grant.check_time()?; + epoch_file.seek(SeekFrom::Start(0))?; + epoch_file.write_all(&frame.bytes[REFERENCE_EPOCH_SIZE..])?; + grant.check_time()?; + epoch_file.sync_all()?; + grant.check_time()?; + // This independent entry was already reserved with the complete effect. + let mut current = + super::locks::open_fixed_control(frame.paths.epoch, Kind::File, false, scratch)?; + let written_stamp = FixedFileStamp::capture(¤t, Kind::File)?; + if written_stamp.identity() != frame.expected.file + || written_stamp.len() != REFERENCE_EPOCH_SIZE as u64 + { + return Err(Error::Invalid(Problem::Changed)); + } + read_fixed_control(&mut current, &written_stamp, read)?; + if read.as_slice()[..REFERENCE_EPOCH_SIZE] != frame.bytes[REFERENCE_EPOCH_SIZE..] { + return Err(Error::Invalid(Problem::Changed)); + } + grant.check_time()?; + let advance = CanonicalEpochAdvance { + before: expected.counter, + after: expected + .counter + .checked_add(1) + .ok_or(Error::Invalid(Problem::EpochExhausted))?, + }; + apply(advance, grant)?; + grant.check_time()?; + // Explicit uses document retention through callback; RAII also covers all + // preceding errors and callback unwind. No caller-owned guard is released. + drop(serial); + drop(activity); + Ok(advance) +} + +#[derive(Debug)] +struct ReferenceActivity { + guard: Option, + #[cfg(test)] + reason: Option, + producer_binding: Option, +} + +#[derive(Debug, Clone, Copy)] +struct ArtifactRootProducerBinding { + root: FileIdentity, + coordination: FileIdentity, + incarnation: [u8; 32], + cohort: FileIdentity, +} +impl ReferenceActivity { + fn untracked(_reason: &str) -> Self { + Self { + guard: None, + #[cfg(test)] + reason: Some(_reason.into()), + producer_binding: None, + } + } + fn begin( + root: &Path, + expected: Option<&ReferenceDomain>, + nonblocking: bool, + held_root_use: &LockedFile, + ) -> Result, NativeError> { + // Never create missing epoch/control proof or silently revive coverage. + match fs::symlink_metadata(root.join(REFERENCE_EPOCH_FILE)) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + return Ok(Some(Self::untracked("reference_epoch_missing"))) + } + Err(error) => return Err(error.into()), + Ok(_) => (), + } + let activity = if nonblocking { + match try_open_existing_locked(&root.join(REFERENCE_ACTIVITY_FILE), LockMode::Shared) { + Ok(Some(guard)) => guard, + Ok(None) => return Ok(None), + Err(error) => return Err(error), + } + } else { + // Existing-only shared handle; no lock creation can repair proof. + super::locks::open_existing_shared_coordination(&root.join(REFERENCE_ACTIVITY_FILE))? + }; + let serial = if nonblocking { + match try_open_existing_locked(&root.join(REFERENCE_SERIAL_FILE), LockMode::Exclusive) { + Ok(Some(guard)) => guard, + Ok(None) => return Ok(None), + Err(error) => return Err(error), + } + } else { + super::locks::open_existing_exclusive(&root.join(REFERENCE_SERIAL_FILE))? + }; + let mut epoch = read_reference_epoch(root)?; + if !activity.is_current()? + || !serial.is_current()? + || !epoch_root_matches(root, &epoch.record, Some(held_root_use))? + { + return Err(invalid("reference_epoch_incarnation_changed")); + } + let mismatch = match expected { + Some(domain) => !domain.revalidate()? || epoch.record.scope != domain.scope, + None => false, + }; + if !epoch.record.covered { + return Ok(Some(Self { + guard: Some(activity), + #[cfg(test)] + reason: Some("reference_coverage_unverified".into()), + producer_binding: None, + })); + } + epoch.record.counter = epoch + .record + .counter + .checked_add(1) + .ok_or_else(|| invalid("reference_epoch_exhausted"))?; + if mismatch { + epoch.record.covered = false; + } + let path = root.join(REFERENCE_EPOCH_FILE); + let mut file = OpenOptions::new().read(true).write(true).open(&path)?; + if super::locks::file_identity(&file)? != epoch.file { + return Err(invalid("reference_epoch_identity_changed")); + } + file.seek(SeekFrom::Start(0))?; + file.write_all(&encode_reference_epoch(&epoch.record)?)?; + file.sync_all()?; + if path_identity(&path)? != epoch.file || !activity.is_current()? || !serial.is_current()? { + return Err(invalid("reference_epoch_identity_changed")); + } + Ok(Some(Self { + guard: Some(activity), + #[cfg(test)] + reason: mismatch.then(|| "reference_domain_mismatch".into()), + producer_binding: (!mismatch).then_some(ArtifactRootProducerBinding { + root: epoch.record.root, + coordination: epoch.record.use_lock, + incarnation: epoch.record.use_incarnation, + cohort: epoch.record.cohort, + }), + })) + } +} + +/// Scope-bound producer activity. Missing old coverage permits ordinary work +/// only as untracked; it can never authorize canonical artifact collection. +#[derive(Debug)] +pub(crate) struct ReferenceMutation<'a> { + _root_use: Option, + activity: ReferenceActivity, + borrowed_root_use: Option<&'a ArtifactUseLease>, +} +impl ReferenceMutation<'static> { + /// Producing paths may create a previously absent artifact root. Acquire + /// and retain its real use guard BEFORE any data effect, so a concurrent + /// domain initializer cannot bless an in-flight untracked producer. + pub(crate) fn begin_producing(root: &Path) -> Result { + let producer = ArtifactUseLease::acquire_shared(root)?; + Ok(Self { + _root_use: Some(producer.lock), + activity: producer + .reference_activity + .unwrap_or_else(|| ReferenceActivity::untracked("reference_epoch_missing")), + borrowed_root_use: None, + }) + } + + pub(crate) fn begin_existing(root: &Path) -> Result { + Self::begin(root, None, false)?.ok_or_else(|| invalid("reference_activity_in_use")) + } + pub(crate) fn begin_in_domain(domain: &ReferenceDomain) -> Result { + Self::begin(&domain.artifact_root, Some(domain), false)? + .ok_or_else(|| invalid("reference_activity_in_use")) + } + pub(crate) fn try_begin_in_domain( + domain: &ReferenceDomain, + ) -> Result, NativeError> { + Self::begin(&domain.artifact_root, Some(domain), true) + } + fn begin( + root: &Path, + expected: Option<&ReferenceDomain>, + nonblocking: bool, + ) -> Result, NativeError> { + let root = absolute(root)?; + let root_use = if nonblocking { + match try_open_existing_locked(&root.join(".use.lock"), LockMode::Shared) { + Ok(Some(guard)) => guard, + Ok(None) => return Ok(None), + Err(error) => { + if !matches!(fs::symlink_metadata(root.join(REFERENCE_EPOCH_FILE)), Err(ref probe) if probe.kind() == std::io::ErrorKind::NotFound) + { + return Err(error); + } + return Ok(Some(Self { + _root_use: None, + activity: ReferenceActivity::untracked("reference_root_unverifiable"), + borrowed_root_use: None, + })); + } + } + } else { + match super::locks::open_existing_shared_coordination(&root.join(".use.lock")) { + Ok(guard) => guard, + Err(error) => { + if !matches!(fs::symlink_metadata(root.join(REFERENCE_EPOCH_FILE)), Err(ref probe) if probe.kind() == std::io::ErrorKind::NotFound) + { + return Err(error); + } + return Ok(Some(Self { + _root_use: None, + activity: ReferenceActivity::untracked("reference_root_unverifiable"), + borrowed_root_use: None, + })); + } + } + }; + let Some(activity) = ReferenceActivity::begin(&root, expected, nonblocking, &root_use)? + else { + return Ok(None); + }; + Ok(Some(Self { + _root_use: Some(root_use), + activity, + borrowed_root_use: None, + })) + } +} +impl Drop for ReferenceMutation<'_> { + fn drop(&mut self) { + drop(self.activity.guard.take()); + drop(self._root_use.take()); + } +} + +/// Same-root producer provenance, never cleanup/destination authority. Fields +/// cannot be reconstructed from a bool, path, serialized record or root name. +/// Both real guards are borrowed from the live mutation; no ownership escapes. +#[derive(Debug)] +pub(crate) struct ArtifactRootProducerCapability<'a> { + root: &'a Path, + root_use: &'a LockedFile, + activity: &'a LockedFile, + binding: ArtifactRootProducerBinding, + _not_send_sync: std::marker::PhantomData>, +} +impl ArtifactRootProducerCapability<'_> { + pub(crate) fn root(&self) -> &Path { + self.root + } + pub(crate) fn root_identity(&self) -> FileIdentity { + self.binding.root + } + pub(crate) fn coordination_identity(&self) -> FileIdentity { + self.binding.coordination + } + pub(crate) fn incarnation(&self) -> [u8; 32] { + self.binding.incarnation + } + pub(crate) fn cohort_identity(&self) -> FileIdentity { + self.binding.cohort + } + + /// Existing-only checks. A failed optional attestation must not be promoted + /// into a mandatory primary publication/read error by its consumer. + pub(crate) fn revalidate(&self) -> Result { + if directory_identity(self.root)? != self.binding.root + || !self + .root_use + .verifies(&self.root.join(".use.lock"), self.root_use.mode())? + || self.root_use.identity()? != self.binding.coordination + || self.root_use.coordination_incarnation()? != Some(self.binding.incarnation) + || !self + .activity + .verifies(&self.root.join(REFERENCE_ACTIVITY_FILE), LockMode::Shared)? + { + return Ok(false); + } + let path = self.root.join(".use-cohort.json"); + if !real_path(&path) { + return Ok(false); + } + reject_file_alias(&path)?; + let file = fs::File::open(&path)?; + if super::locks::file_identity(&file)? != self.binding.cohort + || path_identity(&path)? != self.binding.cohort + { + return Ok(false); + } + let stamp = + super::locks::FixedFileStamp::capture(&file, super::locks::FixedControlKind::File) + .map_err(|_| invalid("artifact_producer_cohort_changed"))?; + let len = file.metadata()?.len(); + if len > 1024 { + return Ok(false); + } + let mut bytes = [0; 1024]; + (&file).read_exact(&mut bytes[..len as usize])?; + let Ok(record) = serde_json::from_slice::(&bytes[..len as usize]) + else { + return Ok(false); + }; + stamp + .recheck(&file) + .map_err(|_| invalid("artifact_producer_cohort_changed"))?; + Ok(record.version == 2 + && record.root == self.binding.root + && record.coordination == self.binding.coordination + && record.coordination_incarnation == self.binding.incarnation + && file.metadata()?.len() == len + && path_identity(&path)? == self.binding.cohort + && self.root_use.is_current()? + && self.activity.is_current()?) + } +} + +impl ReferenceMutation<'_> { + pub(crate) fn artifact_root_producer_capability( + &self, + ) -> Result>, NativeError> { + let Some(binding) = self.activity.producer_binding else { + return Ok(None); + }; + let Some(activity) = self.activity.guard.as_ref() else { + return Ok(None); + }; + let root_use = self + ._root_use + .as_ref() + .or_else(|| self.borrowed_root_use.map(|lease| &lease.lock)); + let Some(root_use) = root_use else { + return Ok(None); + }; + let Some(root) = root_use.path().parent() else { + return Ok(None); + }; + let capability = ArtifactRootProducerCapability { + root, + root_use, + activity, + binding, + _not_send_sync: std::marker::PhantomData, + }; + if !capability.revalidate()? { + return Ok(None); + } + Ok(Some(capability)) + } + + #[cfg(test)] + pub(crate) fn tracked(&self) -> bool { + self.activity.reason.is_none() + } + #[cfg(test)] + pub(crate) fn pending_reason(&self) -> Option<&str> { + self.activity.reason.as_deref() + } +} + +#[derive(Debug)] +#[cfg(test)] +// Fixed inline protocol/lease state: boxing would add a separately accounted allocation. +#[allow(clippy::large_enum_variant)] +pub(crate) enum MaintenanceAdmission<'a> { + Acquired(MaintenanceLease<'a>), + Deferred(String), + Unknown(String), +} + +#[derive(Debug)] +#[cfg(test)] +enum DestinationWriter<'a> { + Owned(WriterLease), + Borrowed(&'a WriterLease), +} +#[cfg(test)] +impl DestinationWriter<'_> { + fn lease(&self) -> &WriterLease { + match self { + Self::Owned(lease) => lease, + Self::Borrowed(lease) => lease, + } + } +} + +/// A current-cohort lease can be acquired before legacy migration is approved. +/// `cohort_verified` establishes destination-wide controlled migration; +/// `artifact_cohort_verified` establishes root-only producer provenance. S3 owns +/// confirmation policy and persistence, using `cohort_record` under this lease. +#[derive(Debug)] +#[cfg(test)] +pub(crate) struct MaintenanceLease<'a> { + writer: DestinationWriter<'a>, + writer_path: PathBuf, + state_path: Option, + artifact: ArtifactUseLease, + _database: Vec, + _workers: Vec, + _phases: Vec, + target: MaintenanceTarget, + binding: ObservedBinding, +} +#[cfg(test)] +impl MaintenanceLease<'_> { + /// Invalidation only. A future prepared transition must validate its old + /// epoch under this authority BEFORE calling; this is not a deletion seal. + pub(crate) fn begin_reference_mutation(&self) -> Result, NativeError> { + self.artifact.revalidate()?; + let activity = + ReferenceActivity::begin(&self.artifact.root, None, false, &self.artifact.lock)? + .ok_or_else(|| invalid("reference_activity_in_use"))?; + Ok(ReferenceMutation { + _root_use: None, + activity, + borrowed_root_use: Some(&self.artifact), + }) + } + + pub(crate) fn target(&self) -> &MaintenanceTarget { + &self.target + } + + pub(crate) fn revalidate(&self) -> Result<(), NativeError> { + if !self + .writer + .lease() + .verifies(&self.writer_path, LockMode::Exclusive)? + { + return Err(invalid("writer_identity_changed")); + } + self.artifact.revalidate()?; + if observe_binding(&self.target).map_err(invalid)? != self.binding + || bound_roles_overlap(&self.target, &self.binding) + { + return Err(invalid("destination_binding_changed")); + } + for guard in self._workers.iter().chain(&self._phases) { + if !guard.is_current()? { + return Err(invalid("worker_or_phase_identity_changed")); + } + } + for guard in &self._database { + guard.revalidate()?; + } + Ok(()) + } + + /// Root provenance does not establish destination-wide legacy quiescence. + pub(crate) fn artifact_cohort_verified(&self) -> Result { + self.revalidate()?; + self.artifact.cohort_verified() + } + + #[cfg(test)] + pub(crate) fn cohort_verified(&self) -> Result { + self.revalidate()?; + if self.artifact.lock.coordination_incarnation()?.is_none() { + return Ok(false); + } + let expected = self.cohort_record()?; + let path = self.cohort_record_path(); + if !real_path(&path) { + return Ok(false); + } + reject_file_alias(&path)?; + let identity = path_identity(&path)?; + let file = fs::File::open(&path)?; + if super::locks::file_identity(&file)? != identity { + return Ok(false); + } + let observed: DestinationCohortRecord = match serde_json::from_reader(&file) { + Ok(record) => record, + Err(_) => return Ok(false), + }; + Ok(path_identity(&path)? == identity && observed == expected) + } + + /// Short metadata-only state transition. The caller must prepare validation + /// and traversal outside this closure. Contention never waits under writer. + pub(crate) fn with_state_lock( + &self, + transition: impl FnOnce() -> Result, + ) -> Result, NativeError> { + self.revalidate()?; + let _state = if let Some(path) = &self.state_path { + let Some(lease) = try_open_locked_in_existing_parent(path, LockMode::Exclusive)? else { + return Ok(None); + }; + Some(lease) + } else { + None + }; + transition().map(Some) + } + + /// Data for S3's durable, explicit controlled-migration record. Returning + /// this value does not write it or authorize legacy retirement by itself. + pub(crate) fn cohort_record(&self) -> Result { + self.revalidate()?; + let pair = self + .target + .direct_pair + .clone() + .or(self.binding.current_direct.clone()); + let (database, manifest) = match &pair { + Some(pair) => ( + optional_resource_identity(&pair.database)?, + optional_resource_identity(&pair.manifest)?, + ), + None => (None, None), + }; + Ok(DestinationCohortRecord { + version: 2, + writer_path: self.writer_path.clone(), + repository: path_identity(&self.target.repository_root)?, + destination_root: directory_identity( + self.writer_path + .parent() + .ok_or_else(|| invalid("destination_parent_missing"))?, + )?, + writer: self.writer.lease().identity()?, + artifact_root: self.artifact.identity, + artifact_coordination: self.artifact.lock.identity()?, + artifact_incarnation: self + .artifact + .lock + .coordination_incarnation()? + .ok_or_else(|| invalid("artifact_coordination_incarnation_unknown"))?, + direct_pair: pair, + database, + manifest, + resource_use: self + ._database + .iter() + .map(DatabaseUseLease::exclusive_proof) + .collect::>()?, + origin: DestinationCohortOrigin::ControlledMigration, + }) + } + + /// S3 may persist this data only after proving fresh creation and durable + /// coherent ACTIVE publication. It grants no operator-confirmation status + /// and covers only these exact files/generation, never legacy siblings. + pub(crate) fn publication_cohort_record( + &self, + database: &DatabaseUseLease, + manifest: &DatabaseUseLease, + ) -> Result { + self.revalidate()?; + let database = database.exclusive_proof()?; + let manifest = manifest.exclusive_proof()?; + if database.file.is_none() || manifest.file.is_none() { + return Err(invalid("publication_files_missing")); + } + let pair = self + .target + .direct_pair + .as_ref() + .or(self.binding.current_direct.as_ref()); + let scope = if let Some(pair) = pair { + if database.resource != absolute(&pair.database)? + || manifest.resource != absolute(&pair.manifest)? + { + return Err(invalid("publication_wrong_direct_destination")); + } + PublicationScope::Direct(pair.clone()) + } else { + let generation = database + .resource + .parent() + .ok_or_else(|| invalid("publication_generation_missing"))?; + let generations = self.binding.storage.join("generations"); + if database.resource != generation.join("graph.ldb") + || manifest.resource != generation.join("manifest.json") + || !real_path(generation) + || !same_physical_path( + generation + .parent() + .ok_or_else(|| invalid("generation_parent_missing"))?, + &generations, + ) + { + return Err(invalid("publication_wrong_managed_generation")); + } + let name = generation + .file_name() + .and_then(|name| name.to_str()) + .and_then(|name| name.strip_prefix("gen-")) + .ok_or_else(|| invalid("publication_generation_name_invalid"))?; + super::layout::validate_generation_id(name)?; + PublicationScope::ManagedGeneration { + root: generation.to_owned(), + identity: directory_identity(generation)?, + } + }; + Ok(PublicationCohortRecord { + version: 2, + origin: PublicationCohortOrigin::GuardedPublication, + repository: path_identity(&self.target.repository_root)?, + writer_path: self.writer_path.clone(), + writer: self.writer.lease().identity()?, + artifact_root: self.artifact.identity, + artifact_coordination: self.artifact.lock.identity()?, + artifact_incarnation: self.artifact.lock.coordination_incarnation()?, + scope, + database, + manifest, + }) + } + + pub(crate) fn direct_publication_cohort_record( + &self, + ) -> Result { + let database = self + ._database + .first() + .ok_or_else(|| invalid("not_a_direct_destination"))?; + let manifest = self + ._database + .get(1) + .ok_or_else(|| invalid("not_a_direct_destination"))?; + self.publication_cohort_record(database, manifest) + } + + pub(crate) fn publication_cohort_verified( + &self, + record: &PublicationCohortRecord, + database: &DatabaseUseLease, + manifest: &DatabaseUseLease, + ) -> Result { + Ok(self + .verified_publication_cohort(record, database, manifest)? + .is_some()) + } + + /// Informational records remain available for untracked roots. Cleanup + /// origin/admission must instead receive this typed, actually nonce-bound + /// capability; neither v1 nor None==None equality establishes ownership. + pub(crate) fn verified_publication_cohort<'a>( + &'a self, + record: &'a PublicationCohortRecord, + database: &DatabaseUseLease, + manifest: &DatabaseUseLease, + ) -> Result>, NativeError> { + if record.version != 2 + || record.artifact_incarnation.is_none() + || !self.artifact.cohort_verified()? + { + return Ok(None); + } + let current = self.publication_cohort_record(database, manifest)?; + if current.artifact_incarnation.is_none() || *record != current { + return Ok(None); + } + Ok(Some(VerifiedPublicationCohort { + _record: record, + _artifact: &self.artifact, + })) + } + + pub(crate) fn direct_publication_cohort_verified( + &self, + record: &PublicationCohortRecord, + ) -> Result { + let database = self + ._database + .first() + .ok_or_else(|| invalid("not_a_direct_destination"))?; + let manifest = self + ._database + .get(1) + .ok_or_else(|| invalid("not_a_direct_destination"))?; + self.publication_cohort_verified(record, database, manifest) + } + + pub(crate) fn cohort_record_path(&self) -> PathBuf { + self.writer_path.with_extension("maintenance-cohort.json") + } +} + +#[derive(Debug)] +#[cfg(test)] +pub(crate) struct VerifiedPublicationCohort<'a> { + _record: &'a PublicationCohortRecord, + _artifact: &'a ArtifactUseLease, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg(test)] +pub(crate) struct PublicationCohortRecord { + version: u32, + origin: PublicationCohortOrigin, + repository: FileIdentity, + writer_path: PathBuf, + writer: FileIdentity, + artifact_root: FileIdentity, + artifact_coordination: FileIdentity, + #[serde(default)] + artifact_incarnation: Option<[u8; 32]>, + scope: PublicationScope, + database: DatabaseUseProof, + manifest: DatabaseUseProof, +} +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +#[cfg(test)] +enum PublicationCohortOrigin { + GuardedPublication, +} +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +#[cfg(test)] +enum PublicationScope { + Direct(DirectPair), + ManagedGeneration { + root: PathBuf, + identity: FileIdentity, + }, +} + +/// S3 persists this record only after its explicit operator-controlled protocol. +/// A normal producer never emits destination-wide migration authority. Exact +/// Direct file identities invalidate the record after replacement/publication; +/// S3 owns the new publication's ACTIVE incarnation and subsequent eligibility. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg(test)] +pub(crate) struct DestinationCohortRecord { + version: u32, + writer_path: PathBuf, + repository: FileIdentity, + destination_root: FileIdentity, + writer: FileIdentity, + artifact_root: FileIdentity, + artifact_coordination: FileIdentity, + artifact_incarnation: [u8; 32], + direct_pair: Option, + database: Option, + manifest: Option, + resource_use: Vec, + origin: DestinationCohortOrigin, +} +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +#[cfg(test)] +enum DestinationCohortOrigin { + ControlledMigration, +} + +fn optional_resource_identity(path: &Path) -> Result, NativeError> { + match fs::symlink_metadata(path) { + Ok(_) => { + reject_file_alias(path)?; + path_identity(path).map(Some) + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(error) => Err(error.into()), + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +enum CohortOrigin { + GuardedCreation, + ControlledMigration, +} + +/// Exact physical root and stable lock identity, produced only under upgraded +/// producer ownership or explicit operator-controlled migration. No PID/time +/// or mere record presence supplies proof. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ArtifactCohortRecord { + version: u32, + root: FileIdentity, + coordination: FileIdentity, + coordination_incarnation: [u8; 32], + origin: CohortOrigin, +} + +/// Creation-only evidence. This type has no public constructor and is minted +/// only by the genuine fresh-root branch while retaining actual Exclusive use. +pub(crate) struct FreshPublicationRoot<'a> { + anchor: &'a super::locks::RootedDirectory, + root_use: &'a LockedFile, + incarnation: [u8; 32], +} +impl FreshPublicationRoot<'_> { + pub(crate) fn anchor(&self) -> &super::locks::RootedDirectory { + self.anchor + } + pub(crate) fn incarnation(&self) -> [u8; 32] { + self.incarnation + } + pub(crate) fn coordination_identity(&self) -> std::io::Result { + if self.root_use.mode() != LockMode::Exclusive { + return Err(std::io::ErrorKind::InvalidInput.into()); + } + self.root_use.identity().map_err(|error| match error { + NativeError::Io(error) => error, + _ => std::io::ErrorKind::InvalidInput.into(), + }) + } +} + +#[derive(Debug)] +pub(crate) struct ArtifactUseLease { + root: PathBuf, + identity: FileIdentity, + lock: LockedFile, + reference_activity: Option, + legacy_source: Option, +} +impl ArtifactUseLease { + pub(crate) fn ensure_root(root: &Path) -> Result<(), NativeError> { + let root = absolute(root)?; + fs::create_dir_all( + root.parent() + .ok_or_else(|| invalid("artifact_parent_missing"))?, + )?; + match fs::create_dir(&root) { + Ok(()) => { + let identity = directory_identity(&root)?; + if let Some(lock) = + try_open_locked_in_existing_parent(root.join(".use.lock"), LockMode::Exclusive)? + { + let lease = Self { + root, + identity, + lock, + reference_activity: None, + legacy_source: None, + }; + lease.write_guarded_creation_record()?; + } + } + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => { + directory_identity(&root)?; + } + Err(error) => return Err(error.into()), + } + Ok(()) + } + + fn write_guarded_creation_record(&self) -> Result<(), NativeError> { + let mut incarnation = [0; 32]; + getrandom::fill(&mut incarnation) + .map_err(|_| invalid("artifact_coordination_entropy_unavailable"))?; + self.revalidate()?; + // Failure to prepare optional provenance cannot introduce a new primary + // write requirement. No header/cohort is published if anchoring fails. + let anchor = match super::locks::RootedDirectory::open(&self.root, self.identity) { + Ok(anchor) => anchor, + Err(_) => return Ok(()), + }; + let named_use = anchor.open_existing(b".use.lock")?; + if super::locks::file_identity(&named_use)? != self.lock.identity()? { + return Err(invalid("artifact_coordination_incarnation_changed")); + } + drop(named_use); + self.lock.initialize_creation_incarnation(incarnation)?; + { + let fresh = FreshPublicationRoot { + anchor: &anchor, + root_use: &self.lock, + incarnation, + }; + let mut scratch = super::maintenance_controls::PublicationFactoryScratch::new(); + // Partial controls remain finite Unknown. Primary publication must + // use S3's approved safe lanes without inventing origin attestation. + let _ = + super::maintenance_controls::initialize_publication_controls(&fresh, &mut scratch); + } + let record = self.record(CohortOrigin::GuardedCreation)?; + let mut file = anchor.create_new(b".use-cohort.json")?; + serde_json::to_writer(&mut file, &record)?; + file.sync_all()?; + #[cfg(not(windows))] + anchor.sync_namespace()?; + #[cfg(windows)] + { + // The existing primary cohort policy never fails an otherwise + // successful write for unsupported Windows directory flushing. + // Publisher attestation independently requires both actual flushes. + let _ = anchor.sync_namespace(); + } + self.revalidate()?; + Ok(()) + } + + pub(crate) fn acquire_shared(root: &Path) -> Result { + let root = absolute(root)?; + let parent = root + .parent() + .ok_or_else(|| invalid("artifact_parent_missing"))?; + fs::create_dir_all(parent)?; + let created = match fs::create_dir(&root) { + Ok(()) => true, + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => false, + Err(e) => return Err(e.into()), + }; + let identity = directory_identity(&root)?; + if fs::symlink_metadata(root.join(REFERENCE_EPOCH_FILE)).is_ok() + && fs::symlink_metadata(root.join(".use.lock")).is_err() + { + return Err(invalid("reference_use_lock_missing")); + } + let lock = open_locked( + root.join(".use.lock"), + if created { + LockMode::Exclusive + } else { + LockMode::Shared + }, + )?; + let mut lease = Self { + root, + identity, + lock, + reference_activity: None, + legacy_source: None, + }; + lease.revalidate()?; + // Only the process that created this incarnation can establish guarded + // creation. Existing unproven roots remain legacy until explicit S3 + // confirmation. A create_new write cannot replace an existing proof. + if created { + lease.write_guarded_creation_record()?; + let incarnation = lease.lock.coordination_incarnation()?; + lease.lock = lease.lock.share_after_creation()?; + lease.revalidate()?; + // Check the actual held header after any Windows downgrade gap. + if lease.lock.coordination_incarnation()? != incarnation { + return Err(invalid("artifact_coordination_incarnation_changed")); + } + } + lease.legacy_source = + super::maintenance_legacy_guard::before_artifact_producer(&lease.root)?; + lease.reference_activity = ReferenceActivity::begin(&lease.root, None, false, &lease.lock)?; + Ok(lease) + } + + pub(crate) fn bind_reference_domain( + &mut self, + domain: &ReferenceDomain, + ) -> Result<(), NativeError> { + self.revalidate()?; + if domain.artifact_identity != Some(self.identity) { + return Err(invalid("reference_artifact_domain_mismatch")); + } + self.reference_activity = + ReferenceActivity::begin(&self.root, Some(domain), false, &self.lock)?; + Ok(()) + } + pub(crate) fn acquire_in_domain(domain: &ReferenceDomain) -> Result { + let mut lease = Self::acquire_shared(&domain.artifact_root)?; + lease.bind_reference_domain(domain)?; + Ok(lease) + } + + /// Cache-consumer guard: existing-only readonly Shared ownership, with no + /// creation, reference epoch increment, optional cohort decode or repair. + /// Mere use never establishes collection ownership for a caller-owned root. + pub(crate) fn try_existing_shared_read(root: &Path) -> Result, NativeError> { + let root = absolute(root)?; + let identity = match directory_identity(&root) { + Ok(identity) => identity, + Err(NativeError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { + return Ok(None) + } + Err(error) => return Err(error), + }; + let lock = + match super::locks::try_open_existing_shared_coordination(&root.join(".use.lock")) { + Ok(Some(lock)) => lock, + Ok(None) => return Ok(None), + Err(NativeError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { + return Ok(None) + } + Err(error) => return Err(error), + }; + let lease = Self { + root, + identity, + lock, + reference_activity: None, + legacy_source: None, + }; + lease.revalidate()?; + Ok(Some(lease)) + } + + pub(crate) fn try_exclusive(root: &Path) -> Result, NativeError> { + let root = absolute(root)?; + let identity = directory_identity(&root)?; + let Some(lock) = + try_open_locked_in_existing_parent(root.join(".use.lock"), LockMode::Exclusive)? + else { + return Ok(None); + }; + let lease = Self { + root, + identity, + lock, + reference_activity: None, + legacy_source: None, + }; + lease.revalidate()?; + Ok(Some(lease)) + } + + fn revalidate(&self) -> Result<(), NativeError> { + if directory_identity(&self.root)? != self.identity + || !self + .lock + .verifies(&self.root.join(".use.lock"), self.lock_mode())? + { + return Err(invalid("artifact_root_incarnation_changed")); + } + Ok(()) + } + fn lock_mode(&self) -> LockMode { + self.lock.mode() + } + fn record(&self, origin: CohortOrigin) -> Result { + self.revalidate()?; + Ok(ArtifactCohortRecord { + version: 2, + root: self.identity, + coordination: self.lock.identity()?, + coordination_incarnation: self + .lock + .coordination_incarnation()? + .ok_or_else(|| invalid("artifact_coordination_incarnation_unknown"))?, + origin, + }) + } + #[cfg(test)] + fn cohort_verified(&self) -> Result { + self.revalidate()?; + let path = self.root.join(".use-cohort.json"); + if !real_path(&path) { + return Ok(false); + } + reject_file_alias(&path)?; + let identity = path_identity(&path)?; + let file = fs::File::open(&path)?; + if super::locks::file_identity(&file)? != identity { + return Ok(false); + } + let record: ArtifactCohortRecord = match serde_json::from_reader(&file) { + Ok(record) => record, + Err(_) => return Ok(false), + }; + Ok(path_identity(&path)? == identity + && record.version == 2 + && record.root == self.identity + && record.coordination == self.lock.identity()? + && self.lock.coordination_incarnation()? == Some(record.coordination_incarnation)) + } + pub(crate) fn into_lock(self) -> LockedFile { + let mut lock = self.lock; + if let Some(activity) = self.reference_activity { + if let Some(guard) = activity.guard { + lock.retain(guard); + } + } + lock + } +} + +/// Physical-parent family ownership covers all equivalent filename spellings, +/// including absent-to-created resources. Basename guards remain secondary; +/// filesystem case folding/normalization is never emulated as authority. Raw +/// Direct pair-derived writer/journal keys stay unchanged. +#[derive(Debug)] +pub(crate) struct DatabaseUseLease { + resource: PathBuf, + parent: PathBuf, + parent_identity: FileIdentity, + family: Arc, + lock: Arc, + parent_pin: fs::File, + legacy_source: Option, +} +impl DatabaseUseLease { + pub(crate) fn acquire_shared(database: &Path) -> Result { + let startup = super::maintenance_generation_gate::before_native_open(database)?; + let legacy_source = super::maintenance_legacy_guard::before_native_open(database)?; + let (parent, path) = database_lock_path(database, true)?; + let parent_identity = directory_identity(&parent)?; + let parent_pin = + super::locks::RootedDirectory::open_readonly_root(&parent, parent_identity)? + .pin_handle()?; + let family = Arc::new(open_shared_coordination( + &parent.join(".database-use-family.lock"), + )?); + let lock = Arc::new(open_shared_coordination(&path)?); + let lease = Self { + resource: absolute(database)?, + parent, + parent_identity, + family, + lock, + parent_pin, + legacy_source, + }; + lease.revalidate()?; + super::maintenance_generation_gate::recheck_before_native(startup.as_ref(), database)?; + super::maintenance_legacy_guard::recheck_before_native( + lease.legacy_source.as_ref(), + database, + )?; + drop(startup); + Ok(lease) + } + + #[cfg(test)] + pub(crate) fn try_exclusive(database: &Path) -> Result, NativeError> { + Ok(Self::try_exclusive_many(&[database])?.and_then(|mut leases| leases.pop())) + } + + /// One acquisition group owns each physical parent and each basename guard + /// once. This is explicit group authority, never process-wide reentrancy. + /// All guards remain live until the final group member using them drops. + #[cfg(test)] + pub(crate) fn try_exclusive_many( + databases: &[&Path], + ) -> Result>, NativeError> { + let mut families: Vec<(FileIdentity, Arc)> = Vec::new(); + let mut resource_locks: Vec<(FileIdentity, PathBuf, Arc)> = Vec::new(); + let mut leases = Vec::new(); + for database in databases { + let (parent, path) = database_lock_path(database, false)?; + let parent_identity = directory_identity(&parent)?; + let parent_pin = + super::locks::RootedDirectory::open_readonly_root(&parent, parent_identity)? + .pin_handle()?; + let family = if let Some((_, guard)) = families + .iter() + .find(|(identity, _)| *identity == parent_identity) + { + Arc::clone(guard) + } else { + let Some(guard) = try_open_locked_in_existing_parent( + parent.join(".database-use-family.lock"), + LockMode::Exclusive, + )? + else { + return Ok(None); + }; + let guard = Arc::new(guard); + families.push((parent_identity, Arc::clone(&guard))); + guard + }; + let name = path + .file_name() + .ok_or_else(|| invalid("database_coordination_name_missing"))? + .to_owned(); + let lock = if let Some((_, _, guard)) = resource_locks + .iter() + .find(|(identity, key, _)| *identity == parent_identity && key.as_os_str() == name) + { + Arc::clone(guard) + } else { + let Some(guard) = try_open_locked_in_existing_parent(&path, LockMode::Exclusive)? + else { + return Ok(None); + }; + let guard = Arc::new(guard); + resource_locks.push((parent_identity, PathBuf::from(name), Arc::clone(&guard))); + guard + }; + let lease = Self { + resource: absolute(database)?, + parent, + parent_identity, + family, + lock, + parent_pin, + legacy_source: None, + }; + lease.revalidate()?; + leases.push(lease); + } + Ok(Some(leases)) + } + + fn revalidate(&self) -> Result<(), NativeError> { + let addressed_parent = fs::canonicalize( + self.resource + .parent() + .ok_or_else(|| invalid("database_parent_missing"))?, + )?; + if super::locks::file_identity(&self.parent_pin)? != self.parent_identity + || directory_identity(&self.parent)? != self.parent_identity + || directory_identity(&addressed_parent)? != self.parent_identity + || !self.family.is_current()? + || !self.lock.is_current()? + { + return Err(invalid("database_parent_incarnation_changed")); + } + Ok(()) + } + + /// Identity evidence, never a transferable deletion capability. S3 retains + /// this exclusive lease while validating/detaching the corresponding file. + #[cfg(test)] + pub(crate) fn exclusive_proof(&self) -> Result { + self.revalidate()?; + if self.family.mode() != LockMode::Exclusive || self.lock.mode() != LockMode::Exclusive { + return Err(invalid("database_use_not_exclusive")); + } + Ok(DatabaseUseProof { + resource: self.resource.clone(), + file: optional_resource_identity(&self.resource)?, + parent: self.parent_identity, + family_coordination_path: self.family.path().to_owned(), + family_coordination: self.family.identity()?, + coordination_path: self.lock.path().to_owned(), + coordination: self.lock.identity()?, + }) + } + + /// Producer conversion retains BOTH guards through the enclosing Direct + /// session. A member of a shared exclusive group cannot be extracted alone. + pub(crate) fn into_lock(self) -> Result { + let mut lock = + Arc::try_unwrap(self.lock).map_err(|_| invalid("database_use_group_still_shared"))?; + let family = + Arc::try_unwrap(self.family).map_err(|_| invalid("database_use_group_still_shared"))?; + lock.retain(family); + Ok(lock) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg(test)] +pub(crate) struct DatabaseUseProof { + resource: PathBuf, + file: Option, + parent: FileIdentity, + family_coordination_path: PathBuf, + family_coordination: FileIdentity, + coordination_path: PathBuf, + coordination: FileIdentity, +} + +pub(super) fn database_lock_path( + database: &Path, + create_parent: bool, +) -> Result<(PathBuf, PathBuf), NativeError> { + let database = absolute(database)?; + reject_file_alias(&database)?; + let parent = database + .parent() + .ok_or_else(|| invalid("database_parent_missing"))?; + if create_parent { + fs::create_dir_all(parent)?; + } + let parent = fs::canonicalize(parent)?; + let basename = database + .file_name() + .ok_or_else(|| invalid("database_name_missing"))? + .to_str() + .ok_or_else(|| invalid("database_name_not_utf8"))? + .to_lowercase(); + let hash = format!("{:x}", Sha256::digest(basename.as_bytes())); + let lock = parent.join(format!(".database-use-{hash}.lock")); + Ok((parent, lock)) +} +fn absolute(path: &Path) -> Result { + Ok(if path.is_absolute() { + path.to_owned() + } else { + std::env::current_dir()?.join(path) + }) +} +fn directory_identity(path: &Path) -> Result { + if !fs::symlink_metadata(path)?.is_dir() { + return Err(invalid("use_root_not_directory")); + } + path_identity(path) +} +fn reject_file_alias(path: &Path) -> Result<(), NativeError> { + let metadata = match fs::symlink_metadata(path) { + Ok(m) => m, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()), + Err(e) => return Err(e.into()), + }; + if metadata.file_type().is_symlink() || !metadata.is_file() { + return Err(invalid("resource_symlink_or_not_file")); + } + let file = fs::File::open(path)?; + if super::locks::file_link_count(&file)? != 1 + || super::locks::file_identity(&file)? != path_identity(path)? + { + return Err(invalid("resource_hardlink_or_identity_ambiguous")); + } + Ok(()) +} +fn invalid(reason: impl Into) -> NativeError { + NativeError::InvalidInput(reason.into()) +} + +/// Pure, noncreating observation held through the inventory scans. The caller +/// may release state.lock before traversal; writer/use ownership and repeated +/// tree observations remain necessary and uncertainty stays partial. +#[cfg(test)] +pub(crate) fn observe_artifact_cohort( + root: &Path, +) -> Result, NativeError> { + let root = absolute(root)?; + let identity = directory_identity(&root)?; + let Some(lock) = try_open_existing_locked(&root.join(".use.lock"), LockMode::Exclusive)? else { + return Ok(None); + }; + let lease = ArtifactUseLease { + root, + identity, + lock, + reference_activity: None, + legacy_source: None, + }; + if lease.cohort_verified()? { + Ok(Some(lease)) + } else { + Ok(None) + } +} + +#[cfg(test)] +pub(crate) fn claim_maintenance( + target: &MaintenanceTarget, +) -> Result, NativeError> { + claim(target, None) +} +#[cfg(test)] +pub(crate) fn claim_maintenance_with_owned<'a>( + target: &MaintenanceTarget, + writer: &'a WriterLease, +) -> Result, NativeError> { + claim(target, Some(writer)) +} + +#[cfg(test)] +fn claim<'a>( + target: &MaintenanceTarget, + owned: Option<&'a WriterLease>, +) -> Result, NativeError> { + let binding = match observe_binding(target) { + Ok(binding) => binding, + Err(reason) => return Ok(MaintenanceAdmission::Unknown(reason)), + }; + if bound_roles_overlap(target, &binding) { + return Ok(MaintenanceAdmission::Unknown( + "managed_direct_storage_role_overlap".into(), + )); + } + // Explicit Direct selection names its own destination. Otherwise managed + // storage takes precedence when configured; default-only installs use Direct. + let direct = target + .direct_pair + .clone() + .or(binding.current_direct.clone()); + let ( + writer_path, + worker_path, + worker_state, + workers_root, + phase_root, + state_path, + artifact_root, + ) = if let Some(pair) = &direct { + let layout = DirectLayout::new(&pair.database, &pair.manifest); + ( + layout.writer_lock_path(), + layout.worker_lock_path(), + layout.worker_state_path(), + layout.worker_workspace_root_path(), + layout.phase_workspace_root(), + None, + layout.artifact_root_path(), + ) + } else { + let layout = ManagedLayout::new(&binding.storage); + ( + layout.writer_lock_path(), + layout.worker_lock_path(), + layout.worker_state_path(), + binding.storage.join("workers"), + layout.phase_workspace_root(), + Some(layout.state_lock_path()), + layout.artifacts_root(), + ) + }; + if !real_path(writer_path.parent().unwrap_or_else(|| Path::new("."))) + || !real_path(&artifact_root) + { + return Ok(MaintenanceAdmission::Unknown( + "destination_missing_or_symlinked".into(), + )); + } + let writer = if let Some(writer) = owned { + if !writer.verifies(&writer_path, LockMode::Exclusive)? { + return Err(invalid("owned_writer_wrong_destination_or_mode")); + } + DestinationWriter::Borrowed(writer) + } else { + let Some(writer) = try_open_locked_in_existing_parent(&writer_path, LockMode::Exclusive)? + else { + return Ok(MaintenanceAdmission::Deferred("writer_in_use".into())); + }; + DestinationWriter::Owned(writer) + }; + let Some(worker) = try_open_locked_in_existing_parent(&worker_path, LockMode::Exclusive)? + else { + return Ok(MaintenanceAdmission::Deferred("worker_in_use".into())); + }; + let mut phases = Vec::new(); + for root in [&workers_root, &phase_root] { + if let Err(reason) = guard_registry(root, &mut phases) { + return Ok(if reason == "startup_in_use" { + MaintenanceAdmission::Deferred(reason) + } else { + MaintenanceAdmission::Unknown(reason) + }); + } + } + // Legacy v1 is not process authority; preserve its evidence until controlled + // migration. v2 can only be idle when the existing guardian proves emptiness. + let worker_metadata = match fs::symlink_metadata(&worker_state) { + Ok(metadata) => Some(metadata), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => None, + Err(_) => { + return Ok(MaintenanceAdmission::Unknown( + "worker_state_inspection_unknown".into(), + )) + } + }; + if let Some(metadata) = worker_metadata { + if !metadata.is_file() + || metadata.file_type().is_symlink() + || !real_path(&worker_state) + || reject_file_alias(&worker_state).is_err() + { + return Ok(MaintenanceAdmission::Unknown( + "worker_state_identity_unknown".into(), + )); + } + let value: serde_json::Value = match fs::File::open(&worker_state) + .map_err(NativeError::from) + .and_then(|f| serde_json::from_reader(f).map_err(NativeError::from)) + { + Ok(v) => v, + Err(_) => { + return Ok(MaintenanceAdmission::Unknown( + "worker_state_unreadable".into(), + )) + } + }; + if value.get("version").and_then(|v| v.as_u64()) == Some(2) { + let descriptor: BuildDescriptorV2 = match serde_json::from_value(value) { + Ok(d) => d, + Err(_) => { + return Ok(MaintenanceAdmission::Unknown( + "worker_descriptor_unknown".into(), + )) + } + }; + if !matches!(probe_descriptor(&descriptor), Ok(true)) { + return Ok(MaintenanceAdmission::Unknown( + "worker_containment_unknown".into(), + )); + } + } else { + return Ok(MaintenanceAdmission::Unknown( + "legacy_quiescence_required".into(), + )); + } + } + let mut databases = Vec::new(); + if let Some(pair) = &direct { + for path in [&pair.database, &pair.manifest] { + if !real_path(path.parent().unwrap_or_else(|| Path::new("."))) + || reject_file_alias(path).is_err() + { + return Ok(MaintenanceAdmission::Unknown( + "direct_resource_identity_unknown".into(), + )); + } + } + if resources_overlap(&pair.database, &pair.manifest) { + return Ok(MaintenanceAdmission::Unknown( + "direct_pair_resource_overlap".into(), + )); + } + if let Some(reason) = overlapping_pairs(target, pair)? { + return Ok(MaintenanceAdmission::Unknown(reason)); + } + let Some(group) = DatabaseUseLease::try_exclusive_many(&[&pair.database, &pair.manifest])? + else { + return Ok(MaintenanceAdmission::Deferred( + "database_parent_in_use".into(), + )); + }; + databases = group; + } + let Some(artifact) = ArtifactUseLease::try_exclusive(&artifact_root)? else { + return Ok(MaintenanceAdmission::Deferred( + "artifact_root_in_use".into(), + )); + }; + let lease = MaintenanceLease { + writer, + writer_path, + state_path, + artifact, + _database: databases, + _workers: vec![worker], + _phases: phases, + target: target.clone(), + binding, + }; + lease.revalidate()?; + Ok(MaintenanceAdmission::Acquired(lease)) +} + +/// Hold every existing lifecycle lease through detachment; no retirement or +/// deletion is performed here. Missing lease beside startup/launch state is +/// unknown, and existing v1/ambiguous guardian evidence is preserved unchanged. +#[cfg(test)] +fn guard_registry(root: &Path, guards: &mut Vec) -> Result<(), String> { + let metadata = match fs::symlink_metadata(root) { + Ok(m) => m, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()), + Err(e) => return Err(e.to_string()), + }; + if metadata.file_type().is_symlink() || !metadata.is_dir() { + return Err("phase_registry_identity_unknown".into()); + } + if root + .file_name() + .and_then(|n| n.to_str()) + .is_some_and(|n| n.starts_with(".lifecycle-")) + { + let lease = try_open_existing_locked(&root.join("lease"), LockMode::Exclusive) + .map_err(|e| format!("startup_lease_unknown: {e}"))? + .ok_or("startup_in_use")?; + if !super::containment::lifecycle_empty_with_owned(root, &lease)? { + return Err("phase_containment_unknown".into()); + } + guards.push(lease); + return Ok(()); + } + let mut has_files = false; + let mut has_lifecycle = false; + for entry in fs::read_dir(root).map_err(|e| e.to_string())? { + let entry = entry.map_err(|e| e.to_string())?; + let ty = entry.file_type().map_err(|e| e.to_string())?; + if ty.is_symlink() { + return Err("phase_registry_symlink".into()); + } + if ty.is_dir() { + has_lifecycle |= entry + .file_name() + .to_str() + .is_some_and(|name| name.starts_with(".lifecycle-")); + guard_registry(&entry.path(), guards)?; + } else { + has_files = true; + } + } + if has_files && !has_lifecycle { + return Err("phase_execution_evidence_without_lifecycle".into()); + } + Ok(()) +} + +#[derive(Debug, Serialize, Deserialize)] +struct PairRegistration { + database: PathBuf, + manifest: PathBuf, +} + +/// Stable registration records are evidence of possible overlap, never proof +/// of quiescence. Keep them after a session so an alternate pair cannot vanish +/// from the retirement decision as soon as its current reader exits. +pub(crate) fn register_direct_pair(layout: &DirectLayout) -> Result<(), NativeError> { + let first = layout.registration_path(); + let second = layout + .manifest_path() + .parent() + .unwrap_or_else(|| Path::new(".")) + .join( + first + .file_name() + .ok_or_else(|| invalid("registration_name_missing"))?, + ); + let record = PairRegistration { + database: absolute(layout.db_path())?, + manifest: absolute(layout.manifest_path())?, + }; + for path in [first, second] { + if path.exists() { + reject_file_alias(&path)?; + let existing: PairRegistration = serde_json::from_slice(&fs::read(&path)?)?; + if existing.database != record.database || existing.manifest != record.manifest { + return Err(invalid("direct_registration_conflict")); + } + } else { + write_json_atomically(&path, &record)?; + } + } + Ok(()) +} +#[cfg(test)] +fn overlapping_pairs( + target: &MaintenanceTarget, + pair: &DirectPair, +) -> Result, NativeError> { + let mut pairs = vec![default_direct(target)]; + for resource in [&pair.database, &pair.manifest] { + let parent = resource + .parent() + .ok_or_else(|| invalid("resource_parent_missing"))?; + for entry in fs::read_dir(parent)? { + let entry = entry?; + if entry + .file_name() + .to_string_lossy() + .starts_with(".direct-registration-") + { + if !real_path(&entry.path()) || reject_file_alias(&entry.path()).is_err() { + return Ok(Some("direct_registration_identity_unknown".into())); + } + let record: PairRegistration = + match serde_json::from_slice(&fs::read(entry.path())?) { + Ok(record) => record, + Err(_) => return Ok(Some("direct_registration_invalid".into())), + }; + pairs.push(DirectPair { + database: record.database, + manifest: record.manifest, + }); + } + } + } + for other in pairs { + if other == *pair { + continue; + } + if resources_overlap(&other.database, &pair.database) + || resources_overlap(&other.manifest, &pair.manifest) + || resources_overlap(&other.database, &pair.manifest) + || resources_overlap(&other.manifest, &pair.database) + { + return Ok(Some("direct_registered_pair_overlap".into())); + } + } + Ok(None) +} +#[cfg(test)] +fn resources_overlap(a: &Path, b: &Path) -> bool { + // Actual distinct physical files are positive evidence on a case-sensitive + // filesystem. The conservative coordination-key fallback is for absent or + // otherwise unproven identities, not a reason to conflate known files. + if let (Ok(a), Ok(b)) = (path_identity(a), path_identity(b)) { + return a == b; + } + same_physical_path(a, b) + || match (database_lock_path(a, false), database_lock_path(b, false)) { + (Ok((_, a)), Ok((_, b))) => a == b, + _ => true, + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::storage::direct::DirectStore; + use crate::storage::managed::ManagedStore; + use std::time::{Duration, Instant}; + + struct Fixture { + root: PathBuf, + target: MaintenanceTarget, + layout: ManagedLayout, + } + impl Fixture { + fn new() -> Self { + let root = std::env::temp_dir().join(format!( + "cleanup-s2-{}", + super::super::layout::managed_generation_id() + )); + fs::create_dir(&root).unwrap(); + let root = fs::canonicalize(root).unwrap(); + let state = root.join(".codebaseGraph"); + fs::create_dir(&state).unwrap(); + let layout = ManagedLayout::new(state.join("storage")); + layout.ensure_roots().unwrap(); + fs::write(state.join("config.json"), br#"{"schema_version":2}"#).unwrap(); + for path in [layout.writer_lock_path(), layout.state_lock_path()] { + drop(open_locked(path, LockMode::Exclusive).unwrap()); + } + let target = MaintenanceTarget { + repository_root: root.clone(), + state_root: state, + storage_root: None, + direct_pair: None, + config_path: None, + }; + Self { + root, + target, + layout, + } + } + fn direct(&self) -> (MaintenanceTarget, DirectLayout) { + let pair = default_direct(&self.target); + let layout = DirectLayout::new(&pair.database, &pair.manifest); + fs::write(&pair.database, b"db").unwrap(); + fs::write(&pair.manifest, br#"{"files":{}}"#).unwrap(); + drop(ArtifactUseLease::acquire_shared(&layout.artifact_root_path()).unwrap()); + let mut target = self.target.clone(); + target.direct_pair = Some(pair); + (target, layout) + } + } + impl Drop for Fixture { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.root); + } + } + fn acquired(admission: MaintenanceAdmission<'_>) -> MaintenanceLease<'_> { + match admission { + MaintenanceAdmission::Acquired(lease) => lease, + other => panic!("expected acquired: {other:?}"), + } + } + + fn tracked_domain(fixture: &Fixture, name: &str) -> (ManagedLayout, ReferenceDomain) { + let layout = ManagedLayout::new(fixture.root.join(name)); + let fresh = super::super::layout::create_fresh_reference_root(layout.storage_root()) + .unwrap() + .unwrap(); + layout.ensure_roots().unwrap(); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + drop(writer); + (layout, domain) + } + fn observed(domain: &ReferenceDomain) -> ReferenceEpoch { + match observe_reference_epoch(domain).unwrap() { + EpochObservation::Verified(epoch) => epoch, + other => panic!("expected verified epoch: {other:?}"), + } + } + + #[test] + fn dc_workspace_root_removal_restarts_after_partial_effects_and_failed_done() { + use crate::storage::maintenance_budget::{CleanupMemory, MutationGrant}; + use crate::storage::run_workspace::RunWorkspace; + use crate::storage::{ + maintenance_controls as control, maintenance_workspace_gate as removal, + }; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "root-removal-restart"); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let root = run.retire_for_maintenance().unwrap(); + // This unit isolates root-final transport after receipt collection. All + // these files were produced by this fixture; production drains them via + // the origin candidate/journal path, exercised by controller tests. + for entry in fs::read_dir(root.join(".workspace-origins")).unwrap() { + fs::remove_file(entry.unwrap().path()).unwrap(); + } + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let mut terminal = control::prepare_terminal_workspace(&root, &domain, &memory).unwrap(); + let mut initial = + removal::prepare_workspace_root_removal(&root, &domain, Some(&terminal), &memory) + .unwrap(); + let mut grant = MutationGrant::for_test(100, Duration::from_secs(2)); + assert_eq!( + removal::begin_workspace_root_removal(&mut terminal, &mut initial, &mut grant).unwrap(), + removal::WorkspaceRemovalProgress::Pending + ); + drop(initial); + drop(terminal); + let mut saw_actual_removal = false; + let mut failed_done = false; + for _ in 0..16 { + let mut next = removal::prepare_pending_workspace_root_removal(&domain, &memory) + .unwrap() + .expect("durable exact removing intent"); + let mut grant = MutationGrant::for_test(100, Duration::from_secs(2)); + let root_existed_before = root.exists(); + let result = removal::advance_workspace_root_removal( + &mut next, + &mut grant, + 0, + &mut |outcome, _| { + match outcome { + removal::WorkspaceRootOutcome::Removed(_) => assert!( + root_existed_before, + "actual-rmdir token fabricated after a prior invocation" + ), + removal::WorkspaceRootOutcome::RecoveredMissing(_) => assert!( + !root_existed_before, + "missing-root recovery claimed while root was present" + ), + } + assert!(!root.exists()); + failed_done = true; + Err(crate::storage::locks::FixedControlProblem::InvalidRecord.into()) + }, + ); + // Actual namespace removal and delivery of its durable Done event + // can occupy different grants. Observe the former independently. + saw_actual_removal |= root_existed_before && !root.exists(); + if failed_done { + assert!(result.is_err()); + break; + } + assert_eq!(result.unwrap(), removal::WorkspaceRemovalProgress::Pending); + } + assert!(saw_actual_removal); + assert!( + failed_done, + "first legitimate completion callback was not exercised" + ); + assert!(!root.exists()); + // Failed durable completion keeps Removing. Recovery cannot invent the + // prior invocation's actual-rmdir event; it reports RecoveredMissing. + let mut pending = removal::prepare_pending_workspace_root_removal(&domain, &memory) + .unwrap() + .unwrap(); + let mut called = false; + let mut grant = MutationGrant::for_test(100, Duration::from_secs(2)); + assert_eq!( + removal::advance_workspace_root_removal( + &mut pending, + &mut grant, + 0, + &mut |outcome, _| { + assert!(matches!( + outcome, + removal::WorkspaceRootOutcome::RecoveredMissing(_) + )); + called = true; + Ok(()) + } + ) + .unwrap(), + removal::WorkspaceRemovalProgress::Complete + ); + assert!(called); + assert!( + removal::prepare_pending_workspace_root_removal(&domain, &memory) + .unwrap() + .is_none() + ); + } + + #[test] + fn dc_workspace_root_removal_unknown_sibling_never_starts_intent() { + use crate::storage::maintenance_budget::CleanupMemory; + use crate::storage::run_workspace::RunWorkspace; + use crate::storage::{ + maintenance_controls as control, maintenance_workspace_gate as removal, + }; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "root-removal-unknown"); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let root = run.retire_for_maintenance().unwrap(); + for entry in fs::read_dir(root.join(".workspace-origins")).unwrap() { + fs::remove_file(entry.unwrap().path()).unwrap(); + } + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let terminal = control::prepare_terminal_workspace(&root, &domain, &memory).unwrap(); + fs::write(root.join("foreign-owned"), b"preserve").unwrap(); + assert!( + removal::prepare_workspace_root_removal(&root, &domain, Some(&terminal), &memory) + .is_err() + ); + assert_eq!(fs::read(root.join("foreign-owned")).unwrap(), b"preserve"); + assert!( + removal::prepare_pending_workspace_root_removal(&domain, &memory) + .unwrap() + .is_none() + ); + } + + #[test] + fn dc_s2_workspace_terminal_admission_preserves_local_source_with_unrelated_producer() { + use crate::storage::maintenance_budget::{CleanupMemory, MutationGrant}; + use crate::storage::maintenance_controls as control; + use crate::storage::run_workspace::RunWorkspace; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "terminal-source"); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let mut output = + control::WorkspaceOutputFile::create_new(&run.staging_root().join("owned.csv")) + .unwrap() + .expect("genuine registered output"); + output.write_all(b"a,b\n1,2\n").unwrap(); + let completed = output.finish().unwrap(); + assert_eq!(completed.len(), 8); + assert_eq!( + completed.digest(), + <[u8; 32]>::from(Sha256::digest(b"a,b\n1,2\n")) + ); + drop(completed); + let root = run.retire_for_maintenance().unwrap(); + assert!(root.exists()); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let mut prepared = control::prepare_terminal_workspace(&root, &domain, &memory).unwrap(); + let source = prepared.source_epoch(); + let unrelated = ReferenceMutation::begin_in_domain(&domain).unwrap(); + let mut called = false; + let mut grant = MutationGrant::for_test(100, Duration::from_secs(2)); + let advance = + control::with_terminal_workspace(&mut prepared, &mut grant, 0, &mut |lease, _| { + assert_eq!(lease.source_epoch(), source); + assert_eq!(lease.root_identity(), path_identity(&root).unwrap()); + called = true; + Ok(()) + }) + .unwrap(); + assert!(called); + assert_eq!(advance.after, advance.before + 1); + assert_eq!(prepared.source_epoch(), source); + drop(unrelated); + assert_eq!(observed(&domain).counter(), advance.after); + } + + #[test] + fn dc_s2_workspace_live_output_prevents_terminal_and_preserves_root() { + use crate::storage::maintenance_controls::WorkspaceOutputFile; + use crate::storage::run_workspace::RunWorkspace; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "terminal-live-output"); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let root = run.root().to_owned(); + let output = WorkspaceOutputFile::create_new(&run.staging_root().join("live.csv")) + .unwrap() + .unwrap(); + assert!(run.retire_for_maintenance().is_err()); + assert!(root.exists()); + drop(output); + assert!(root.exists()); + } + + #[test] + fn dc_s2_workspace_child_owns_startup_gate_and_late_start_refuses_closed() { + use crate::storage::maintenance_controls::WorkspaceStartupBinding; + use crate::storage::run_workspace::RunWorkspace; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "terminal-child-startup"); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let binding = WorkspaceStartupBinding::discover(run.root()) + .unwrap() + .unwrap(); + let child = binding.acquire().unwrap(); + child.recheck_building().unwrap(); + drop(child); + let root = run.retire_for_maintenance().unwrap(); + assert!(binding.acquire().is_err()); + assert!(root.exists()); + } + + #[test] + fn dc_s2_producer_capability_borrows_live_tracked_root_and_activity() { + let fixture = Fixture::new(); + let (_layout, domain) = tracked_domain(&fixture, "producer-capability"); + let mutation = ReferenceMutation::begin_in_domain(&domain).unwrap(); + let capability = mutation + .artifact_root_producer_capability() + .unwrap() + .unwrap(); + assert_eq!(capability.root(), domain.artifact_root()); + assert_eq!( + capability.root_identity(), + directory_identity(domain.artifact_root()).unwrap() + ); + assert_eq!( + capability.coordination_identity(), + path_identity(&domain.artifact_root().join(".use.lock")).unwrap() + ); + assert_eq!( + capability.incarnation(), + mutation.activity.producer_binding.unwrap().incarnation + ); + assert!(capability.revalidate().unwrap()); + assert!(try_open_existing_locked( + &domain.artifact_root().join(".use.lock"), + LockMode::Exclusive + ) + .unwrap() + .is_none()); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Pending(_) + )); + trait AmbiguousIfSend { + fn check() {} + } + impl AmbiguousIfSend<()> for T {} + impl AmbiguousIfSend for T {} + let _ = as AmbiguousIfSend<_>>::check; + trait AmbiguousIfSync { + fn check() {} + } + impl AmbiguousIfSync<()> for T {} + impl AmbiguousIfSync for T {} + let _ = as AmbiguousIfSync<_>>::check; + trait AmbiguousIfClone { + fn check() {} + } + impl AmbiguousIfClone<()> for T {} + impl AmbiguousIfClone for T {} + let _ = as AmbiguousIfClone<_>>::check; + // Consume this non-owning proof before releasing or checking its lender. + #[allow(clippy::drop_non_drop)] + drop(capability); + // Capability drop cannot release its lender's actual ownership. + assert!(try_open_existing_locked( + &domain.artifact_root().join(".use.lock"), + LockMode::Exclusive + ) + .unwrap() + .is_none()); + drop(mutation); + assert!(try_open_existing_locked( + &domain.artifact_root().join(".use.lock"), + LockMode::Exclusive + ) + .unwrap() + .is_some()); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Verified(_) + )); + } + + #[test] + fn dc_s2_optional_producer_capability_never_repairs_untracked_or_corrupt_proof() { + let fixture = Fixture::new(); + let untracked = + ReferenceMutation::begin_producing(&fixture.layout.artifacts_root()).unwrap(); + assert!(untracked + .artifact_root_producer_capability() + .unwrap() + .is_none()); + fs::write( + fixture.layout.artifacts_root().join("primary-data"), + b"primary work remains allowed", + ) + .unwrap(); + drop(untracked); + let (_layout, domain) = tracked_domain(&fixture, "corrupt-producer-capability"); + let mutation = ReferenceMutation::begin_in_domain(&domain).unwrap(); + let path = domain.artifact_root().join(".use-cohort.json"); + let capability = mutation + .artifact_root_producer_capability() + .unwrap() + .unwrap(); + let original = fs::read(&path).unwrap(); + for bytes in [b"invalid".to_vec(), vec![b' '; 1025]] { + fs::write(&path, &bytes).unwrap(); + assert!(!capability.revalidate().unwrap()); + assert!(mutation + .artifact_root_producer_capability() + .unwrap() + .is_none()); + fs::write( + domain.artifact_root().join("primary-data"), + b"optional attestation is absent", + ) + .unwrap(); + assert_eq!(fs::read(&path).unwrap(), bytes); + } + fs::write(&path, original).unwrap(); + assert!(capability.revalidate().unwrap()); + } + + #[test] + fn dc_s2_producer_capability_uses_actual_borrowed_exclusive_root() { + let fixture = Fixture::new(); + let (layout, _domain) = tracked_domain(&fixture, "borrowed-producer-capability"); + write_json_atomically(&fixture.target.state_root.join("config.json"), &serde_json::json!({ + "schema_version": 2, "repo_root": fixture.root, "storage_root": layout.storage_root() + })).unwrap(); + let mut target = fixture.target.clone(); + target.storage_root = Some(layout.storage_root().to_owned()); + let lease = acquired(claim_maintenance(&target).unwrap()); + let mutation = lease.begin_reference_mutation().unwrap(); + assert!(mutation._root_use.is_none()); + let capability = mutation + .artifact_root_producer_capability() + .unwrap() + .unwrap(); + assert_eq!(capability.root_use.mode(), LockMode::Exclusive); + assert!(capability.revalidate().unwrap()); + // Consume this non-owning proof before releasing or checking its lender. + #[allow(clippy::drop_non_drop)] + drop(capability); + drop(mutation); + // Borrowed mutation/capability never releases caller-owned root guard. + assert!(try_open_existing_locked( + &layout.artifacts_root().join(".use.lock"), + LockMode::Shared + ) + .unwrap() + .is_none()); + lease.artifact.revalidate().unwrap(); + drop(lease); + assert!(try_open_existing_locked( + &layout.artifacts_root().join(".use.lock"), + LockMode::Shared + ) + .unwrap() + .is_some()); + } + + #[test] + fn dc_s2_factory_creates_bound_fixed_names_before_tracked_producer_use() { + use super::super::maintenance_controls::{self as controls, PublisherBinding}; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "bound-publisher-factory"); + let root = layout.artifacts_root(); + let binding_path = root.join(".publication.binding"); + let mutation = ReferenceMutation::begin_in_domain(&domain).unwrap(); + let mut capability = mutation + .artifact_root_producer_capability() + .unwrap() + .unwrap(); + // This is the positive native activation qualification. Optional + // unsupported-filesystem fallback is tested separately and cannot make + // this test pass without a durable, actually usable publisher binding. + assert!(binding_path.is_file(), "factory activation requires durable native namespace flushing on this fixture filesystem"); + let bytes = fs::read(&binding_path).unwrap(); + let binding = PublisherBinding::decode(&bytes).unwrap(); + assert_eq!(binding.root, capability.root_identity()); + assert_eq!(binding.coordination, capability.coordination_identity()); + assert_eq!(binding.incarnation, capability.incarnation()); + assert_eq!( + binding.stream, + Some(path_identity(&root.join(".publication.stream")).unwrap()) + ); + assert!(fs::read(root.join(".publication.stream")) + .unwrap() + .is_empty()); + let mut publication = controls::try_begin_root_publication(&mut capability) + .unwrap() + .unwrap(); + assert_eq!(publication.directory().identity(), binding.root); + assert_eq!(publication.root(), root); + assert_eq!( + publication.root_cohort_identity(), + path_identity(&root.join(".use-cohort.json")).unwrap() + ); + assert_eq!( + publication.publication_binding_identity(), + path_identity(&binding_path).unwrap() + ); + assert_eq!(publication.binding(), &binding); + assert_eq!(publication.committed().generation, 0); + assert!(publication.revalidate_existing().unwrap()); + let mut update = controls::PrimaryPublicationScratch::new(); + let next = publication + .publish_primary_control(b"producer creation intent", &mut update) + .unwrap(); + assert_eq!(next.generation, 1); + assert!(publication.revalidate_existing().unwrap()); + let mut committed = [0; controls::PAYLOAD_BYTES]; + let len = publication.read_primary_control(&mut committed).unwrap(); + assert_eq!(&committed[..len], b"producer creation intent"); + assert!( + try_open_existing_locked(&root.join(".publication.selector"), LockMode::Exclusive) + .unwrap() + .is_none() + ); + drop(publication); + assert!( + try_open_existing_locked(&root.join(".publication.selector"), LockMode::Exclusive) + .unwrap() + .is_some() + ); + let reopened = controls::try_begin_root_publication(&mut capability) + .unwrap() + .unwrap(); + assert_eq!(reopened.committed().generation, 1); + drop(reopened); + // Namespace lease drop cannot release the caller's root/activity guards. + assert!(ArtifactUseLease::try_exclusive(&root).unwrap().is_none()); + // Consume this non-owning proof before releasing or checking its lender. + #[allow(clippy::drop_non_drop)] + drop(capability); + drop(mutation); + assert!(ArtifactUseLease::try_exclusive(&root).unwrap().is_some()); + } + + #[test] + fn dc_s2_factory_partial_names_are_never_repaired_or_retried_on_acquisition() { + use super::super::maintenance_controls::{ + initialize_publication_controls, PublicationFactoryScratch, + }; + let fixture = Fixture::new(); + let root = fixture.root.join("partial-factory"); + fs::create_dir(&root).unwrap(); + let lock = open_locked(root.join(".use.lock"), LockMode::Exclusive).unwrap(); + lock.initialize_creation_incarnation([9; 32]).unwrap(); + let anchor = + super::super::locks::RootedDirectory::open(&root, directory_identity(&root).unwrap()) + .unwrap(); + fs::write( + root.join(".publication.data1"), + b"unknown preexisting control", + ) + .unwrap(); + let fresh = FreshPublicationRoot { + anchor: &anchor, + root_use: &lock, + incarnation: [9; 32], + }; + let mut scratch = PublicationFactoryScratch::new(); + assert!(initialize_publication_controls(&fresh, &mut scratch).is_err()); + assert!(!root.join(".publication.binding").exists()); + let first_id = path_identity(&root.join(".publication.data0")).unwrap(); + assert!(initialize_publication_controls(&fresh, &mut scratch).is_err()); + assert_eq!( + path_identity(&root.join(".publication.data0")).unwrap(), + first_id + ); + assert_eq!( + fs::read(root.join(".publication.data1")).unwrap(), + b"unknown preexisting control" + ); + drop(lock); + let producer = ReferenceMutation::begin_producing(&root).unwrap(); + assert!(producer + .artifact_root_producer_capability() + .unwrap() + .is_none()); + fs::write(root.join("primary-data"), b"primary work remains possible").unwrap(); + assert!(!root.join(".publication.binding").exists()); + assert!(!root.join(".use-cohort.json").exists()); + } + + #[test] + fn dc_s2_cache_consumer_is_existing_readonly_and_does_not_advance_or_decode_optional_proof() { + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "read-only-cache-consumer"); + let root = layout.artifacts_root(); + fs::write(root.join("partition.json"), b"validated cached data").unwrap(); + let before_epoch = fs::read(root.join(REFERENCE_EPOCH_FILE)).unwrap(); + let cohort_path = root.join(".use-cohort.json"); + let before_cohort = fs::read(&cohort_path).unwrap(); + fs::write(&cohort_path, b"invalid optional cleanup proof").unwrap(); + let lock_path = root.join(".use.lock"); + let before_lock = fs::read(&lock_path).unwrap(); + let probe = OpenOptions::new() + .read(true) + .write(true) + .open(&lock_path) + .unwrap(); + struct RestorePermissions { + path: PathBuf, + permissions: fs::Permissions, + } + impl Drop for RestorePermissions { + fn drop(&mut self) { + let _ = fs::set_permissions(&self.path, self.permissions.clone()); + } + } + let original = fs::metadata(&lock_path).unwrap().permissions(); + let _restore = RestorePermissions { + path: lock_path.clone(), + permissions: original.clone(), + }; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&lock_path, fs::Permissions::from_mode(0o444)).unwrap(); + } + #[cfg(windows)] + { + let mut readonly = original.clone(); + readonly.set_readonly(true); + fs::set_permissions(&lock_path, readonly).unwrap(); + } + let reader = ArtifactUseLease::try_existing_shared_read(&root) + .unwrap() + .unwrap(); + assert!(reader.reference_activity.is_none()); + assert_eq!( + fs::read(root.join("partition.json")).unwrap(), + b"validated cached data" + ); + assert_eq!(reader.lock.mode(), LockMode::Shared); + // The probe's rights were granted BEFORE readonly permissions changed, + // so this failure proves real lock exclusion rather than an ACL denial. + assert!(fs2::FileExt::try_lock_exclusive(&probe).is_err()); + drop(reader); + fs2::FileExt::try_lock_exclusive(&probe).unwrap(); + fs2::FileExt::unlock(&probe).unwrap(); + drop(probe); + fs::set_permissions(&lock_path, original).unwrap(); + assert_eq!(fs::read(&lock_path).unwrap(), before_lock); + assert_eq!( + fs::read(root.join(REFERENCE_EPOCH_FILE)).unwrap(), + before_epoch + ); + assert_eq!( + fs::read(&cohort_path).unwrap(), + b"invalid optional cleanup proof" + ); + fs::write(&cohort_path, before_cohort).unwrap(); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Verified(_) + )); + } + + #[test] + fn dc_s2_cache_consumer_missing_guard_never_creates_or_attests_caller_owned_root() { + let fixture = Fixture::new(); + let missing = fixture.root.join("missing-cache-root"); + assert!(ArtifactUseLease::try_existing_shared_read(&missing) + .unwrap() + .is_none()); + assert!(!missing.exists()); + let caller = fixture.root.join("caller-owned-cache"); + fs::create_dir(&caller).unwrap(); + fs::write(caller.join("payload"), b"caller data").unwrap(); + assert!(ArtifactUseLease::try_existing_shared_read(&caller) + .unwrap() + .is_none()); + assert_eq!(fs::read(caller.join("payload")).unwrap(), b"caller data"); + assert_eq!(fs::read_dir(&caller).unwrap().count(), 1); + assert!( + matches!(observe_artifact_cohort(&caller), Err(NativeError::Io(error)) if error.kind()==std::io::ErrorKind::NotFound) + ); + assert!(!caller.join(".use.lock").exists()); + assert!(!caller.join(".use-cohort.json").exists()); + } + + #[test] + fn dc_epoch_initial_creator_excludes_fresh_coverage_until_effects_finish() { + let fixture = Fixture::new(); + let layout = ManagedLayout::new(fixture.root.join("creator-domain")); + let fresh = super::super::layout::create_fresh_reference_root(layout.storage_root()) + .unwrap() + .unwrap(); + let producer = ReferenceMutation::begin_producing(&layout.artifacts_root()).unwrap(); + assert!(!producer.tracked()); + layout.ensure_roots().unwrap(); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(!initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + assert!(!domain.artifact_root().join(REFERENCE_EPOCH_FILE).exists()); + fs::write( + domain.artifact_root().join("creator-effect"), + b"before coverage", + ) + .unwrap(); + drop(producer); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + drop(writer); + assert_eq!(observed(&domain).counter(), 0); + } + + #[test] + fn dc_epoch_new_cache_cannot_initialize_old_reference_domain() { + let fixture = Fixture::new(); + assert!( + super::super::layout::create_fresh_reference_root(fixture.layout.storage_root()) + .unwrap() + .is_none() + ); + let mutation = + ReferenceMutation::begin_producing(&fixture.layout.artifacts_root()).unwrap(); + assert!(!mutation.tracked()); + drop(mutation); + let domain = ReferenceDomain::managed(&fixture.layout).unwrap(); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Pending(_) + )); + assert!(!domain.artifact_root().join(REFERENCE_EPOCH_FILE).exists()); + } + + #[test] + fn dc_epoch_direct_recovery_invalidates_and_idle_read_does_not() { + use crate::storage::direct::{DirectPublishJournal, DirectPublishPhase}; + let fixture = Fixture::new(); + let parent = fixture.root.join("fresh-direct-domain"); + let layout = DirectLayout::new(parent.join("graph.ldb"), parent.join("manifest.json")); + let store = DirectStore::new(layout.clone()).unwrap(); + drop(store.begin_write().unwrap()); + let domain = ReferenceDomain::direct(&layout).unwrap(); + let old = observed(&domain); + let mutation = ReferenceMutation::begin_in_domain(&domain).unwrap(); + fs::write(layout.db_candidate_path(), b"recovered database").unwrap(); + fs::write(layout.manifest_candidate_path(), b"{}").unwrap(); + let journal = DirectPublishJournal { + phase: DirectPublishPhase::Prepared, + db_path: layout.db_path().to_owned(), + db_candidate_path: layout.db_candidate_path(), + manifest_path: layout.manifest_path().to_owned(), + manifest_candidate_path: layout.manifest_candidate_path(), + db_sha256: format!("{:x}", Sha256::digest(b"recovered database")), + manifest_sha256: format!("{:x}", Sha256::digest(b"{}")), + sidecar_sha256: std::collections::BTreeMap::new(), + }; + write_json_atomically(&layout.journal_path(), &journal).unwrap(); + drop(mutation); + let before_recovery = observed(&domain); + drop(store.begin_read().unwrap()); + assert_eq!(fs::read(layout.db_path()).unwrap(), b"recovered database"); + assert!(!layout.journal_path().exists()); + let after = observed(&domain); + assert!(after.counter() > before_recovery.counter()); + assert_ne!(old, after); + drop(store.begin_read().unwrap()); + assert_eq!(after, observed(&domain)); + } + + fn publish_tracked_generation(store: &ManagedStore, bytes: &[u8]) -> String { + let mut session = store.begin_write().unwrap(); + fs::write(session.candidate_db_path(), bytes).unwrap(); + fs::write(session.candidate_manifest_path(), b"{}").unwrap(); + let id = session + .publish_with_stats(&crate::protocol::GraphSummary { + node_count: 0, + edge_count: 0, + }) + .unwrap(); + session.finish(); + id + } + + #[test] + fn dc_epoch_managed_publication_reader_drop_and_generation_removal() { + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "managed-publication-domain"); + let store = ManagedStore::new(layout.clone()); + let old = observed(&domain); + let first = publish_tracked_generation(&store, b"first"); + let first_epoch = observed(&domain); + assert!(first_epoch.counter() > old.counter()); + let reader = store.open_read().unwrap(); + assert_eq!(reader.generation_id, first); + drop(reader); + assert_eq!( + first_epoch, + observed(&domain), + "active reader release is not a reference mutation" + ); + let retained = store.open_read().unwrap(); + let second = publish_tracked_generation(&store, b"second"); + assert_ne!(first, second); + assert!(layout.generation(&first).unwrap().root().exists()); + let before_removal = observed(&domain); + drop(retained); + assert!(layout.generation(&first).unwrap().root().exists()); + assert_eq!(observed(&domain), before_removal); + store.cleanup().unwrap(); + assert!(!layout.generation(&first).unwrap().root().exists()); + assert!(observed(&domain).counter() > before_removal.counter()); + } + + #[test] + fn dc_epoch_managed_recovery_promotion_invalidates_before_references_change() { + use crate::storage::run_workspace::{RunPhase, RunWorkspace}; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "managed-recovery-domain"); + let store = ManagedStore::new(layout.clone()); + let base = publish_tracked_generation(&store, b"base"); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, Some(base)).unwrap(); + let id = super::super::layout::managed_generation_id(); + let generation = super::super::layout::GenerationPaths::new( + run.candidate_root().join(format!("gen-{id}")), + &id, + ); + generation.ensure_root().unwrap(); + fs::write(generation.db_path(), b"recovered").unwrap(); + fs::write(generation.manifest_path(), b"{}").unwrap(); + fs::write(generation.ready_path(), b"ready").unwrap(); + write_json_atomically( + &generation.metadata_path(), + &serde_json::json!({"schema_version":2,"generation_id":id}), + ) + .unwrap(); + run.register_candidate(id.clone()).unwrap(); + run.mark_phase(RunPhase::Publishing, None, None).unwrap(); + let run_root = run.abandon_for_test(); + let before = observed(&domain); + assert_eq!( + store.cleanup().unwrap().run_recovery.publishing_recovered, + 1 + ); + assert_eq!(store.open_read().unwrap().generation_id, id); + assert!(!run_root.exists()); + assert!(observed(&domain).counter() > before.counter()); + } + + #[test] + fn dc_epoch_retained_observation_does_not_block_current_writer_or_reader() { + use std::sync::mpsc; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "parked-observation-domain"); + let store = ManagedStore::new(layout); + let active = publish_tracked_generation(&store, b"readable"); + let (ready_tx, ready_rx) = mpsc::channel(); + let (resume_tx, resume_rx) = mpsc::channel(); + let background_domain = domain.clone(); + let background = std::thread::spawn(move || { + let epoch = observed(&background_domain); + ready_tx.send(()).unwrap(); + resume_rx.recv_timeout(Duration::from_secs(10)).unwrap(); + epoch + }); + ready_rx.recv_timeout(Duration::from_secs(10)).unwrap(); + assert_eq!(store.open_read().unwrap().generation_id, active); + let writer = store.begin_write().unwrap(); + writer.abort(None).unwrap(); + resume_tx.send(()).unwrap(); + assert_ne!(background.join().unwrap(), observed(&domain)); + } + + #[test] + fn dc_epoch_unassociated_old_orphan_is_preserved_without_repair() { + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "legacy-orphan-domain"); + let orphan = layout.runs_root().join("run-legacy"); + fs::create_dir(&orphan).unwrap(); + fs::write(orphan.join("lease.lock"), b"").unwrap(); + fs::write( + orphan.join("journal.json"), + br#"{"run_id":"legacy","phase":"created"}"#, + ) + .unwrap(); + let before = observed(&domain); + let report = + crate::storage::run_workspace::RunWorkspace::cleanup_orphans(layout.runs_root()) + .unwrap(); + assert_eq!(report.deleted, 0); + assert_eq!(report.skipped_locked, 1); + assert!(orphan.join("journal.json").is_file()); + assert!(!orphan.join(".reference-scope.json").exists()); + assert_eq!(before, observed(&domain)); + } + + #[test] + fn dc_epoch_observer_is_noncreating_and_does_not_advance() { + let fixture = Fixture::new(); + let untracked = ReferenceDomain::managed(&fixture.layout).unwrap(); + let before = fs::read_dir(fixture.layout.artifacts_root()) + .unwrap() + .map(|entry| entry.unwrap().file_name()) + .collect::>(); + assert!(matches!( + observe_reference_epoch(&untracked).unwrap(), + EpochObservation::Pending(_) + )); + let after = fs::read_dir(fixture.layout.artifacts_root()) + .unwrap() + .map(|entry| entry.unwrap().file_name()) + .collect::>(); + assert_eq!(before, after); + let (_, domain) = tracked_domain(&fixture, "observed-domain"); + let before = observed(&domain); + assert_eq!(before.counter(), 0); + assert_eq!(before, observed(&domain)); + assert_eq!( + fs::metadata(domain.artifact_root().join(REFERENCE_EPOCH_FILE)) + .unwrap() + .len(), + REFERENCE_EPOCH_SIZE as u64 + ); + } + + #[test] + fn dc_epoch_activity_excludes_snapshots_until_all_producers_finish() { + let fixture = Fixture::new(); + let (_, domain) = tracked_domain(&fixture, "active-domain"); + let old = observed(&domain); + let first = ReferenceMutation::begin_in_domain(&domain).unwrap(); + assert!(first.tracked()); + let second = ArtifactUseLease::acquire_shared(domain.artifact_root()) + .unwrap() + .into_lock(); + assert!( + matches!(observe_reference_epoch(&domain).unwrap(), EpochObservation::Pending(reason) if reason == "reference_activity_in_use") + ); + drop(first); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Pending(_) + )); + drop(second); + let new = observed(&domain); + assert_eq!(new.counter(), old.counter() + 2); + assert_ne!(old, new); + } + + #[test] + fn dc_epoch_crash_before_effect_invalidates_prior_snapshot() { + const CHILD_ROOT: &str = "CODEBASE_GRAPH_EPOCH_CRASH_ROOT"; + if let Some(root) = std::env::var_os(CHILD_ROOT) { + let _mutation = ReferenceMutation::begin_existing(Path::new(&root)).unwrap(); + assert!(_mutation.tracked()); + fs::write( + Path::new(&root).join("child-entered"), + b"durably invalidated", + ) + .unwrap(); + // Deliberately skip Rust destructors to reproduce producer death. + std::process::exit(0); + } + let fixture = Fixture::new(); + let (_, domain) = tracked_domain(&fixture, "crash-domain"); + let old = observed(&domain); + let module = module_path!().split_once("::").unwrap().1; + let test_name = + format!("{module}::dc_epoch_crash_before_effect_invalidates_prior_snapshot"); + let output = std::process::Command::new(std::env::current_exe().unwrap()) + .args(["--exact", &test_name, "--nocapture"]) + .env(CHILD_ROOT, domain.artifact_root()) + .output() + .unwrap(); + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + assert!( + domain.artifact_root().join("child-entered").is_file(), + "child branch must actually execute" + ); + assert!(!domain + .artifact_root() + .join("actual-artifact-effect") + .exists()); + assert_eq!(observed(&domain).counter(), old.counter() + 1); + } + + #[test] + fn dc_epoch_mismatched_producer_disables_existing_coverage_before_effects() { + let fixture = Fixture::new(); + let (_, domain) = tracked_domain(&fixture, "mismatched-domain"); + let old = observed(&domain); + let mut other = domain.clone(); + other.scope.address = [42; 32]; + let mutation = ReferenceMutation::begin_in_domain(&other).unwrap(); + assert!(!mutation.tracked()); + assert_eq!(mutation.pending_reason(), Some("reference_domain_mismatch")); + fs::write( + domain.artifact_root().join("uncovered-effect"), + b"preserved", + ) + .unwrap(); + drop(mutation); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Pending(_) + )); + assert_ne!(read_reference_epoch(domain.artifact_root()).unwrap(), old); + } + + #[test] + fn dc_epoch_missing_or_unwritable_control_never_repairs_recognized_proof() { + let fixture = Fixture::new(); + let (_, domain) = tracked_domain(&fixture, "missing-control-domain"); + let control = domain.artifact_root().join(".use.lock"); + fs::rename(&control, control.with_extension("old-use")).unwrap(); + assert!(ReferenceMutation::begin_in_domain(&domain).is_err()); + assert!(ArtifactUseLease::acquire_shared(domain.artifact_root()).is_err()); + assert!(!control.exists()); + assert!(!matches!( + observe_reference_epoch(&domain), + Ok(EpochObservation::Verified(_)) + )); + } + + #[test] + fn dc_epoch_exhaustion_rejects_before_effects_and_releases_activity() { + let fixture = Fixture::new(); + let (_, domain) = tracked_domain(&fixture, "exhausted-domain"); + let mut epoch = observed(&domain); + epoch.record.counter = u64::MAX; + let mut file = OpenOptions::new() + .write(true) + .open(domain.artifact_root().join(REFERENCE_EPOCH_FILE)) + .unwrap(); + file.write_all(&encode_reference_epoch(&epoch.record).unwrap()) + .unwrap(); + file.sync_all().unwrap(); + drop(file); + let before = observed(&domain); + let effect = domain.artifact_root().join("must-not-exist"); + let result = (|| -> Result<(), NativeError> { + let _mutation = ReferenceMutation::begin_producing(domain.artifact_root())?; + fs::write(&effect, b"effect")?; + Ok(()) + })(); + assert!(result.is_err()); + assert!(!effect.exists()); + assert_eq!(observed(&domain), before); + } + + #[test] + fn dc_epoch_scope_writer_replacement_stays_unknown() { + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "writer-replaced-domain"); + let writer = layout.writer_lock_path(); + fs::rename(&writer, writer.with_extension("old-writer")).unwrap(); + drop(open_locked(&writer, LockMode::Exclusive).unwrap()); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Pending(_) + )); + let current = ReferenceDomain::managed(&layout).unwrap(); + let mutation = ReferenceMutation::begin_in_domain(¤t).unwrap(); + assert!(!mutation.tracked()); + drop(mutation); + assert!(matches!( + observe_reference_epoch(¤t).unwrap(), + EpochObservation::Pending(_) + )); + } + + #[test] + fn dc_epoch_scoped_run_lifetime_and_orphan_removal_invalidate() { + use crate::storage::run_workspace::RunWorkspace; + const CHILD_ROOT: &str = "CODEBASE_GRAPH_EPOCH_ORPHAN_ROOT"; + if let Some(root) = std::env::var_os(CHILD_ROOT) { + let layout = ManagedLayout::new(PathBuf::from(root)); + let domain = ReferenceDomain::managed(&layout).unwrap(); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + write_json_atomically(&layout.storage_root().join("created-run.json"), &run.root()) + .unwrap(); + std::process::exit(0); + } + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "run-domain"); + let old = observed(&domain); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let path = run.root().to_owned(); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Pending(_) + )); + run.finish().unwrap(); + assert!( + path.exists(), + "producer completion retains its terminal root for bounded collection" + ); + assert!(observed(&domain).counter() > old.counter()); + assert_eq!( + RunWorkspace::cleanup_orphans(layout.runs_root()) + .unwrap() + .deleted, + 1 + ); + assert!( + !path.exists(), + "explicit strict compatibility cleanup still removes the terminal root" + ); + let module = module_path!().split_once("::").unwrap().1; + let test_name = + format!("{module}::dc_epoch_scoped_run_lifetime_and_orphan_removal_invalidate"); + let output = std::process::Command::new(std::env::current_exe().unwrap()) + .args(["--exact", &test_name, "--nocapture"]) + .env(CHILD_ROOT, layout.storage_root()) + .output() + .unwrap(); + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + let orphan: PathBuf = serde_json::from_slice( + &fs::read(layout.storage_root().join("created-run.json")).unwrap(), + ) + .unwrap(); + assert!(orphan.is_dir()); + let before_removal = observed(&domain); + assert_eq!( + RunWorkspace::cleanup_orphans(layout.runs_root()) + .unwrap() + .deleted, + 1 + ); + assert!(!orphan.exists()); + assert!(observed(&domain).counter() > before_removal.counter()); + } + + #[derive(Clone, Copy, Debug)] + pub(crate) enum EpochControlFault { + Corrupt, + Unwritable, + Exhausted, + } + + pub(crate) fn break_epoch_control(domain: &ReferenceDomain, fault: EpochControlFault) { + let path = domain.artifact_root().join(REFERENCE_EPOCH_FILE); + match fault { + EpochControlFault::Corrupt => fs::write(path, b"interrupted epoch write").unwrap(), + EpochControlFault::Unwritable => { + let mut permissions = fs::metadata(&path).unwrap().permissions(); + permissions.set_readonly(true); + fs::set_permissions(path, permissions).unwrap(); + } + EpochControlFault::Exhausted => { + let mut epoch = read_reference_epoch(domain.artifact_root()).unwrap(); + epoch.record.counter = u64::MAX; + let mut file = OpenOptions::new().write(true).open(path).unwrap(); + file.write_all(&encode_reference_epoch(&epoch.record).unwrap()) + .unwrap(); + file.sync_all().unwrap(); + } + } + } + + #[test] + fn dc_epoch_optional_managed_cleanup_preserves_committed_read_and_residue() { + for fault in [ + EpochControlFault::Corrupt, + EpochControlFault::Unwritable, + EpochControlFault::Exhausted, + ] { + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "optional-managed-domain"); + let store = ManagedStore::new(layout.clone()); + let old = publish_tracked_generation(&store, b"old committed graph"); + let retained = store.open_read().unwrap(); + let active = publish_tracked_generation(&store, b"current committed graph"); + let old_path = layout.generation(&old).unwrap().db_path(); + let old_identity = path_identity(&old_path).unwrap(); + let epoch_path = domain.artifact_root().join(REFERENCE_EPOCH_FILE); + let permissions = fs::metadata(&epoch_path).unwrap().permissions(); + break_epoch_control(&domain, fault); + let epoch_bytes = fs::read(&epoch_path).unwrap(); + let report = store.cleanup().unwrap(); + assert_eq!(report.retired_generations_pending, 1, "{fault:?}"); + assert_eq!(report.retired_generations_deleted, 0, "{fault:?}"); + assert_eq!( + store.open_read().unwrap().generation_id, + active, + "{fault:?}" + ); + assert_eq!(fs::read(&old_path).unwrap(), b"old committed graph"); + assert_eq!(path_identity(&old_path).unwrap(), old_identity); + assert!( + ReferenceMutation::begin_in_domain(&domain).is_err(), + "producer must reject {fault:?}" + ); + assert_eq!(fs::read(&epoch_path).unwrap(), epoch_bytes); + drop(retained); + assert_eq!(path_identity(&old_path).unwrap(), old_identity); + fs::set_permissions(epoch_path, permissions).unwrap(); + } + } + + #[test] + fn dc_epoch_optional_direct_cleanup_preserves_committed_read_but_not_journal_replay() { + use crate::storage::direct::{DirectPublishJournal, DirectPublishPhase}; + for fault in [ + EpochControlFault::Corrupt, + EpochControlFault::Unwritable, + EpochControlFault::Exhausted, + ] { + let fixture = Fixture::new(); + let parent = fixture.root.join("optional-direct-domain"); + let layout = DirectLayout::new(parent.join("graph.ldb"), parent.join("manifest.json")); + let store = DirectStore::new(layout.clone()).unwrap(); + let mut writer = store.begin_write().unwrap(); + fs::write(writer.db_candidate_path(), b"committed graph").unwrap(); + fs::write(writer.manifest_candidate_path(), b"{}").unwrap(); + writer.publish().unwrap(); + writer.finish(); + let domain = ReferenceDomain::direct(&layout).unwrap(); + let mutation = ReferenceMutation::begin_in_domain(&domain).unwrap(); + fs::write(layout.db_candidate_path(), b"unpublished residue").unwrap(); + fs::write(layout.manifest_candidate_path(), b"{}").unwrap(); + drop(mutation); + let candidate_identity = path_identity(&layout.db_candidate_path()).unwrap(); + let database_identity = path_identity(layout.db_path()).unwrap(); + let epoch_path = domain.artifact_root().join(REFERENCE_EPOCH_FILE); + let permissions = fs::metadata(&epoch_path).unwrap().permissions(); + break_epoch_control(&domain, fault); + let epoch_bytes = fs::read(&epoch_path).unwrap(); + assert!(!layout.journal_path().exists()); + drop(store.begin_read().unwrap()); + assert!(store.candidate_cleanup_pending(), "{fault:?}"); + assert_eq!( + path_identity(&layout.db_candidate_path()).unwrap(), + candidate_identity + ); + assert_eq!( + fs::read(layout.db_candidate_path()).unwrap(), + b"unpublished residue" + ); + assert_eq!(path_identity(layout.db_path()).unwrap(), database_identity); + assert_eq!(fs::read(layout.db_path()).unwrap(), b"committed graph"); + assert!( + store.begin_write().is_err(), + "producer must reject {fault:?}" + ); + let journal = DirectPublishJournal { + phase: DirectPublishPhase::Prepared, + db_path: layout.db_path().to_owned(), + db_candidate_path: layout.db_candidate_path(), + manifest_path: layout.manifest_path().to_owned(), + manifest_candidate_path: layout.manifest_candidate_path(), + db_sha256: format!("{:x}", Sha256::digest(b"unpublished residue")), + manifest_sha256: format!("{:x}", Sha256::digest(b"{}")), + sidecar_sha256: std::collections::BTreeMap::new(), + }; + write_json_atomically(&layout.journal_path(), &journal).unwrap(); + let journal_identity = path_identity(&layout.journal_path()).unwrap(); + assert!( + store.begin_read().is_err(), + "mandatory replay must reject {fault:?}" + ); + assert_eq!( + path_identity(&layout.journal_path()).unwrap(), + journal_identity + ); + assert_eq!( + path_identity(&layout.db_candidate_path()).unwrap(), + candidate_identity + ); + assert_eq!(path_identity(layout.db_path()).unwrap(), database_identity); + assert_eq!( + fs::read(domain.artifact_root().join(REFERENCE_EPOCH_FILE)).unwrap(), + epoch_bytes + ); + fs::set_permissions(epoch_path, permissions).unwrap(); + } + } + + #[test] + fn dc_epoch_collector_composes_owned_exclusive_root_without_reacquisition() { + let fixture = Fixture::new(); + let lease = acquired(claim_maintenance(&fixture.target).unwrap()); + let mutation = lease.begin_reference_mutation().unwrap(); + assert!(!mutation.tracked()); + assert_eq!(mutation.pending_reason(), Some("reference_epoch_missing")); + drop(mutation); + drop(lease); + } + + #[test] + fn dc_s2_family_replaced_parent_incarnation_invalidates_held_use() { + let fixture = Fixture::new(); + let parent = fixture.root.join("replaceable-parent"); + fs::create_dir(&parent).unwrap(); + let database = parent.join("graph.ldb"); + let sentinel = parent.join("public-sentinel"); + fs::write(&sentinel, b"original public data").unwrap(); + let shared = DatabaseUseLease::acquire_shared(&database).unwrap(); + let detached = fixture.root.join("detached-parent"); + if let Err(error) = fs::rename(&parent, &detached) { + #[cfg(windows)] + if error.kind() == std::io::ErrorKind::PermissionDenied { + // Windows can reject renaming a directory with open descendant + // handles. Prove that this native prevention leaves the public + // incarnation and actual family exclusion intact; do not skip. + assert_eq!(directory_identity(&parent).unwrap(), shared.parent_identity); + assert_eq!(fs::read(&sentinel).unwrap(), b"original public data"); + assert!(!database.exists()); + assert!(!detached.exists()); + shared.revalidate().unwrap(); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_none()); + let other = fixture.root.join("independent-parent"); + fs::create_dir(&other).unwrap(); + let independent = DatabaseUseLease::try_exclusive(&other.join("graph.ldb")) + .unwrap() + .unwrap(); + drop(independent); + drop(shared); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_some()); + return; + } + panic!("unexpected parent rename failure: {error}"); + } + assert_eq!( + fs::read(detached.join("public-sentinel")).unwrap(), + b"original public data" + ); + fs::create_dir(&parent).unwrap(); + fs::write(&database, b"fresh incarnation").unwrap(); + assert!(shared.revalidate().is_err()); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_some()); + } + + #[test] + fn dc_s2_family_group_deduplicates_same_parent_and_retains_all_guards() { + let fixture = Fixture::new(); + let database = fixture.root.join("same-parent.ldb"); + let manifest = fixture.root.join("same-parent.json"); + fs::write(&database, b"db").unwrap(); + fs::write(&manifest, b"{}").unwrap(); + let mut group = DatabaseUseLease::try_exclusive_many(&[&database, &manifest]) + .unwrap() + .unwrap(); + assert!(Arc::ptr_eq(&group[0].family, &group[1].family)); + let first = group[0].exclusive_proof().unwrap(); + let second = group[1].exclusive_proof().unwrap(); + assert_eq!(first.family_coordination, second.family_coordination); + assert_ne!(first.coordination, second.coordination); + // Dropping one resource cannot release its sibling's family authority. + drop(group.remove(0)); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_none()); + drop(group); + assert!( + DatabaseUseLease::try_exclusive_many(&[&database, &manifest]) + .unwrap() + .is_some() + ); + } + + #[test] + fn dc_s2_family_group_handles_distinct_parents_and_duplicate_resource_keys() { + let fixture = Fixture::new(); + let first = fixture.root.join("first"); + let second = fixture.root.join("second"); + fs::create_dir(&first).unwrap(); + fs::create_dir(&second).unwrap(); + let database = first.join("db.ldb"); + let manifest = second.join("manifest.json"); + fs::write(&database, b"db").unwrap(); + fs::write(&manifest, b"{}").unwrap(); + let group = DatabaseUseLease::try_exclusive_many(&[&database, &manifest, &database]) + .unwrap() + .unwrap(); + assert!(Arc::ptr_eq(&group[0].family, &group[2].family)); + assert!(Arc::ptr_eq(&group[0].lock, &group[2].lock)); + assert!(!Arc::ptr_eq(&group[0].family, &group[1].family)); + assert_ne!( + group[0].exclusive_proof().unwrap().family_coordination, + group[1].exclusive_proof().unwrap().family_coordination + ); + } + + #[test] + fn dc_s2_family_producer_conversion_holds_parent_until_session_release() { + let fixture = Fixture::new(); + let database = fixture.root.join("producer.ldb"); + let unrelated = fixture.root.join("other-name.ldb"); + let shared = DatabaseUseLease::acquire_shared(&database).unwrap(); + let converted = shared.into_lock().unwrap(); + assert!(DatabaseUseLease::try_exclusive(&unrelated) + .unwrap() + .is_none()); + // Shared operations in the same family continue to coexist. + drop(DatabaseUseLease::acquire_shared(&unrelated).unwrap()); + fs::write(&database, b"created while owned").unwrap(); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_none()); + drop(converted); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_some()); + } + + #[test] + fn dc_s2_family_recreated_coordination_invalidates_destination_proof() { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + let lease = acquired(claim_maintenance(&target).unwrap()); + let record = lease.cohort_record().unwrap(); + write_json_atomically(&lease.cohort_record_path(), &record).unwrap(); + assert!(lease.cohort_verified().unwrap()); + let family = layout + .db_path() + .parent() + .unwrap() + .join(".database-use-family.lock"); + drop(lease); + fs::rename(&family, family.with_extension("previous-family")).unwrap(); + drop(open_locked(&family, LockMode::Shared).unwrap()); + let lease = acquired(claim_maintenance(&target).unwrap()); + assert!(!lease.cohort_verified().unwrap()); + } + + #[cfg(unix)] + #[test] + fn dc_s2_family_shared_existing_readable_coordination_requires_no_write_permission() { + use std::os::unix::fs::PermissionsExt; + let fixture = Fixture::new(); + let database = fixture.root.join("owned.ldb"); + let unrelated = fixture.root.join("other-user.ldb"); + drop(DatabaseUseLease::acquire_shared(&database).unwrap()); + let family = fixture.root.join(".database-use-family.lock"); + fs::set_permissions(&family, fs::Permissions::from_mode(0o444)).unwrap(); + let shared = DatabaseUseLease::acquire_shared(&unrelated).unwrap(); + assert_eq!(shared.family.mode(), LockMode::Shared); + drop(shared); + fs::set_permissions(family, fs::Permissions::from_mode(0o600)).unwrap(); + } + + #[cfg(windows)] + #[test] + fn dc_s2_family_windows_shared_readonly_coordination_requires_no_write_permission() { + let fixture = Fixture::new(); + let database = fixture.root.join("owned.ldb"); + let unrelated = fixture.root.join("other-user.ldb"); + drop(DatabaseUseLease::acquire_shared(&database).unwrap()); + let family = fixture.root.join(".database-use-family.lock"); + let mut permissions = fs::metadata(&family).unwrap().permissions(); + permissions.set_readonly(true); + fs::set_permissions(&family, permissions).unwrap(); + let result = DatabaseUseLease::acquire_shared(&unrelated); + let mut permissions = fs::metadata(&family).unwrap().permissions(); + permissions.set_readonly(false); + fs::set_permissions(&family, permissions).unwrap(); + assert!(result.is_ok()); + } + + #[cfg(target_os = "macos")] + #[test] + fn dc_s2_family_macos_unicode_nfc_nfd_existing_and_creation_aliases_exclude() { + let fixture = Fixture::new(); + let database = fixture.root.join("caf\u{e9}.ldb"); + let alias = fixture.root.join("cafe\u{301}.ldb"); + // Guard the absent resource through creation under another spelling. + let shared = DatabaseUseLease::acquire_shared(&alias).unwrap(); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_none()); + fs::write(&database, b"database").unwrap(); + assert_eq!( + path_identity(&database).unwrap(), + path_identity(&alias).unwrap() + ); + assert_eq!( + super::super::locks::file_link_count(&fs::File::open(&alias).unwrap()).unwrap(), + 1 + ); + assert!(DatabaseUseLease::try_exclusive(&database) + .unwrap() + .is_none()); + drop(shared); + let shared = DatabaseUseLease::acquire_shared(&database).unwrap(); + assert!(DatabaseUseLease::try_exclusive(&alias).unwrap().is_none()); + drop(shared); + assert!(DatabaseUseLease::try_exclusive(&alias).unwrap().is_some()); + } + + #[cfg(target_os = "macos")] + #[test] + fn dc_s2_family_macos_long_s_consumer_blocks_ascii_direct_even_with_valid_record() { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + let lease = acquired(claim_maintenance(&target).unwrap()); + write_json_atomically(&lease.cohort_record_path(), &lease.cohort_record().unwrap()) + .unwrap(); + assert!(lease.cohort_verified().unwrap()); + drop(lease); + let name = layout.db_path().file_name().unwrap().to_str().unwrap(); + assert!(name.contains('s')); + let alias = layout + .db_path() + .with_file_name(name.replacen('s', "\u{17f}", 1)); + assert_eq!( + path_identity(&alias).unwrap(), + path_identity(layout.db_path()).unwrap() + ); + let shared = DatabaseUseLease::acquire_shared(&alias).unwrap(); + assert!(DatabaseUseLease::try_exclusive(layout.db_path()) + .unwrap() + .is_none()); + assert!( + matches!(claim_maintenance(&target).unwrap(), MaintenanceAdmission::Deferred(reason) if reason == "database_parent_in_use") + ); + drop(shared); + assert!(acquired(claim_maintenance(&target).unwrap()) + .cohort_verified() + .unwrap()); + } + + #[cfg(target_os = "macos")] + #[test] + fn dc_s2_family_macos_parent_aliases_deduplicate_physical_family() { + let fixture = Fixture::new(); + let parent = fixture.root.join("caf\u{e9}"); + let alias = fixture.root.join("cafe\u{301}"); + fs::create_dir(&parent).unwrap(); + let database = parent.join("db.ldb"); + let manifest = alias.join("manifest.json"); + fs::write(&database, b"db").unwrap(); + fs::write(&manifest, b"{}").unwrap(); + assert_eq!( + path_identity(&parent).unwrap(), + path_identity(&alias).unwrap() + ); + let group = DatabaseUseLease::try_exclusive_many(&[&database, &manifest]) + .unwrap() + .unwrap(); + assert!(Arc::ptr_eq(&group[0].family, &group[1].family)); + } + + #[test] + fn dc_s2_review_managed_read_resolution_completes_with_paused_collector() { + use std::sync::mpsc; + let fixture = Fixture::new(); + let generation = fixture.layout.generation("readable").unwrap(); + generation.ensure_root().unwrap(); + fs::write(generation.db_path(), b"committed database fixture").unwrap(); + fs::write(generation.manifest_path(), br#"{"files":{}}"#).unwrap(); + fs::write(generation.ready_path(), b"").unwrap(); + write_json_atomically( + &generation.metadata_path(), + &serde_json::json!({"schema_version":2,"generation_id":"readable"}), + ) + .unwrap(); + write_json_atomically( + &fixture.layout.active_pointer_path(), + &serde_json::json!({"schema_version":2,"generation_id":"readable"}), + ) + .unwrap(); + let collector = acquired(claim_maintenance(&fixture.target).unwrap()); + let store = ManagedStore::new(fixture.layout.clone()); + let (tx, rx) = mpsc::channel(); + let reader = std::thread::spawn(move || { + let result = store + .cleanup() + .and_then(|_| store.resolve_active_read()) + .map(|snapshot| snapshot.map(|snapshot| snapshot.generation_id)); + tx.send(result).unwrap(); + }); + assert_eq!( + rx.recv_timeout(Duration::from_secs(2)) + .expect("reader must finish while collector remains paused") + .unwrap(), + Some("readable".into()) + ); + assert!(collector.artifact_cohort_verified().unwrap()); + drop(collector); + reader.join().unwrap(); + } + + #[cfg(unix)] + #[test] + fn dc_s2_review_dangling_worker_state_is_unknown_and_preserved() { + use std::os::unix::fs::symlink; + let fixture = Fixture::new(); + let state = fixture.layout.worker_state_path(); + symlink(fixture.root.join("missing-worker-evidence"), &state).unwrap(); + assert!( + matches!(claim_maintenance(&fixture.target).unwrap(), MaintenanceAdmission::Unknown(reason) if reason == "worker_state_identity_unknown") + ); + assert!(fs::symlink_metadata(state) + .unwrap() + .file_type() + .is_symlink()); + } + + #[cfg(unix)] + #[test] + fn dc_s2_review_unreadable_worker_state_is_unknown_and_preserved() { + use std::os::unix::fs::PermissionsExt; + let fixture = Fixture::new(); + let state = fixture.layout.worker_state_path(); + fs::write(&state, b"unreadable or invalid containment evidence").unwrap(); + fs::set_permissions(&state, fs::Permissions::from_mode(0o0)).unwrap(); + let observed = claim_maintenance(&fixture.target).unwrap(); + fs::set_permissions(&state, fs::Permissions::from_mode(0o600)).unwrap(); + assert!( + matches!(observed, MaintenanceAdmission::Unknown(reason) if reason == "worker_state_unreadable" || reason == "worker_state_identity_unknown") + ); + assert_eq!( + fs::read(state).unwrap(), + b"unreadable or invalid containment evidence" + ); + } + + #[cfg(windows)] + #[test] + fn dc_s2_review_unreadable_windows_worker_state_is_unknown_and_preserved() { + use std::os::windows::fs::OpenOptionsExt; + let fixture = Fixture::new(); + let state = fixture.layout.worker_state_path(); + fs::write(&state, b"unavailable containment evidence").unwrap(); + let exclusive = OpenOptions::new() + .read(true) + .share_mode(0) + .open(&state) + .unwrap(); + assert!(matches!( + claim_maintenance(&fixture.target).unwrap(), + MaintenanceAdmission::Unknown(_) + )); + drop(exclusive); + assert!(state.is_file()); + } + + #[test] + fn dc_s2_review_legacy_database_new_cache_requires_destination_migration_proof() { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + // The legacy database existed before this cache was created. + let lease = acquired(claim_maintenance(&target).unwrap()); + assert!(lease.artifact_cohort_verified().unwrap()); + assert!(!lease.cohort_verified().unwrap()); + let record = lease.cohort_record().unwrap(); + assert_eq!(record.resource_use.len(), 2); + let path = lease.cohort_record_path(); + write_json_atomically(&path, &record).unwrap(); + assert!(lease.cohort_verified().unwrap()); + drop(lease); + // A recreated use-lock inode invalidates otherwise identical file proof. + let (_, use_path) = database_lock_path(layout.db_path(), false).unwrap(); + fs::rename(&use_path, use_path.with_extension("old-use-lock")).unwrap(); + drop(open_locked(&use_path, LockMode::Shared).unwrap()); + let lease = acquired(claim_maintenance(&target).unwrap()); + assert!(!lease.cohort_verified().unwrap()); + write_json_atomically(&path, &lease.cohort_record().unwrap()).unwrap(); + assert!(lease.cohort_verified().unwrap()); + drop(lease); + let old = fixture.target.state_root.join("old-database-incarnation"); + fs::rename(layout.db_path(), old).unwrap(); + fs::write(layout.db_path(), b"new database incarnation").unwrap(); + let lease = acquired(claim_maintenance(&target).unwrap()); + assert!(!lease.cohort_verified().unwrap()); + } + + #[test] + fn dc_s2_review_publication_proof_does_not_bless_legacy_destination_or_sibling() { + let fixture = Fixture::new(); + let collector = acquired(claim_maintenance(&fixture.target).unwrap()); + let mut use_leases = Vec::new(); + for id in ["fresh", "legacy"] { + let generation = fixture.layout.generation(id).unwrap(); + generation.ensure_root().unwrap(); + fs::write(generation.db_path(), id).unwrap(); + fs::write(generation.manifest_path(), br#"{"files":{}}"#).unwrap(); + let group = DatabaseUseLease::try_exclusive_many(&[ + &generation.db_path(), + &generation.manifest_path(), + ]) + .unwrap() + .unwrap(); + let mut group = group.into_iter(); + use_leases.push((group.next().unwrap(), group.next().unwrap())); + } + let (fresh_db, fresh_manifest) = &use_leases[0]; + let publication = collector + .publication_cohort_record(fresh_db, fresh_manifest) + .unwrap(); + assert!(collector + .publication_cohort_verified(&publication, fresh_db, fresh_manifest) + .unwrap()); + let (legacy_db, legacy_manifest) = &use_leases[1]; + assert!(!collector + .publication_cohort_verified(&publication, legacy_db, legacy_manifest) + .unwrap()); + write_json_atomically(&collector.cohort_record_path(), &publication).unwrap(); + assert!(!collector.cohort_verified().unwrap()); + assert_eq!( + serde_json::to_value(publication).unwrap()["origin"], + "guarded_publication" + ); + assert_eq!( + serde_json::to_value(collector.cohort_record().unwrap()).unwrap()["origin"], + "controlled_migration" + ); + drop(collector); + drop(use_leases); + let (target, _) = fixture.direct(); + let direct = acquired(claim_maintenance(&target).unwrap()); + let publication = direct.direct_publication_cohort_record().unwrap(); + assert!(direct + .direct_publication_cohort_verified(&publication) + .unwrap()); + write_json_atomically(&direct.cohort_record_path(), &publication).unwrap(); + assert!(!direct.cohort_verified().unwrap()); + } + + #[test] + fn dc_s2_publication_nonce_proof_is_optional_but_never_numeric_only() { + for mode in ["untracked", "corrupt", "recycled", "tracked"] { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + let lease = acquired(claim_maintenance(&target).unwrap()); + let record = lease.direct_publication_cohort_record().unwrap(); + assert!(lease.direct_publication_cohort_verified(&record).unwrap()); + let mut legacy = record.clone(); + legacy.version = 1; + assert!(!lease.direct_publication_cohort_verified(&legacy).unwrap()); + let mut missing = record.clone(); + missing.artifact_incarnation = None; + assert!(!lease.direct_publication_cohort_verified(&missing).unwrap()); + let root = lease.artifact.root.clone(); + drop(lease); + let path = root.join(".use.lock"); + let bytes = match mode { + "untracked" => Vec::new(), + "corrupt" => { + let mut bytes = fs::read(&path).unwrap(); + bytes[9] ^= 1; + bytes + } + "recycled" => { + let mut nonce = record.artifact_incarnation.unwrap(); + nonce[0] ^= 1; + super::super::locks::encode_coordination_incarnation(nonce).to_vec() + } + _ => fs::read(&path).unwrap(), + }; + fs::write(&path, &bytes).unwrap(); + // Same numeric inode and primary files remain usable: an optional + // cleanup proof cannot introduce a mandatory publication failure. + assert_eq!(path_identity(&path).unwrap(), record.artifact_coordination); + let lease = acquired(claim_maintenance(&target).unwrap()); + let informational = lease.direct_publication_cohort_record().unwrap(); + assert_eq!( + lease.direct_publication_cohort_verified(&record).unwrap(), + mode == "tracked" + ); + assert_eq!( + lease + .direct_publication_cohort_verified(&informational) + .unwrap(), + mode == "tracked" + ); + if matches!(mode, "untracked" | "corrupt") { + assert!(informational.artifact_incarnation.is_none()); + assert!(!lease + .direct_publication_cohort_verified(&informational) + .unwrap()); + } + drop(lease); + assert_eq!(fs::read(&path).unwrap(), bytes); + assert!(layout.db_path().is_file()); + assert!(layout.manifest_path().is_file()); + } + } + + #[cfg(target_os = "linux")] + #[test] + fn dc_s2_review_linux_case_only_distinct_registrations_remain_distinct() { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + let upper = layout.db_path().with_file_name( + layout + .db_path() + .file_name() + .unwrap() + .to_string_lossy() + .to_uppercase(), + ); + fs::write(&upper, b"distinct case-sensitive database").unwrap(); + assert_ne!( + path_identity(&upper).unwrap(), + path_identity(layout.db_path()).unwrap() + ); + let other_manifest = fixture.target.state_root.join("other-manifest.json"); + fs::write(&other_manifest, b"{}").unwrap(); + register_direct_pair(&DirectLayout::new(&upper, &other_manifest)).unwrap(); + assert!(!resources_overlap(&upper, layout.db_path())); + assert!(matches!( + claim_maintenance(&target).unwrap(), + MaintenanceAdmission::Acquired(_) + )); + } + + #[cfg(any(unix, windows))] + #[test] + fn dc_s2_review_hardlinked_coordination_invalidates_held_authority() { + let fixture = Fixture::new(); + let path = fixture.layout.writer_lock_path(); + let writer = open_locked(&path, LockMode::Exclusive).unwrap(); + let alias = fixture.root.join("held-writer-alias.lock"); + fs::hard_link(&path, &alias).unwrap(); + assert!(!writer.verifies(&path, LockMode::Exclusive).unwrap()); + assert!(try_open_existing_locked(&alias, LockMode::Exclusive).is_err()); + } + + #[cfg(windows)] + #[test] + fn dc_s2_review_windows_case_alias_shares_resource_coordination() { + let fixture = Fixture::new(); + let database = fixture.root.join("CaseOnly.ldb"); + let alias = fixture.root.join("caseonly.LDB"); + fs::write(&database, b"database").unwrap(); + assert_eq!( + path_identity(&database).unwrap(), + path_identity(&alias).unwrap() + ); + let shared = DatabaseUseLease::acquire_shared(&database).unwrap(); + assert!(DatabaseUseLease::try_exclusive(&alias).unwrap().is_none()); + drop(shared); + assert!(DatabaseUseLease::try_exclusive(&alias).unwrap().is_some()); + } + + #[cfg(windows)] + #[test] + fn dc_s2_review_windows_resource_and_coordination_hardlinks_are_rejected() { + let fixture = Fixture::new(); + let database = fixture.root.join("native-windows.ldb"); + fs::write(&database, b"database").unwrap(); + let alias = fixture.root.join("native-windows-alias.ldb"); + fs::hard_link(&database, &alias).unwrap(); + assert!(DatabaseUseLease::acquire_shared(&database).is_err()); + assert!(DatabaseUseLease::try_exclusive(&alias).is_err()); + let writer_path = fixture.layout.writer_lock_path(); + let writer = open_locked(&writer_path, LockMode::Exclusive).unwrap(); + let lock_alias = fixture.root.join("writer-alias.lock"); + fs::hard_link(&writer_path, &lock_alias).unwrap(); + assert!(!writer.verifies(&writer_path, LockMode::Exclusive).unwrap()); + assert!(try_open_existing_locked(&lock_alias, LockMode::Exclusive).is_err()); + } + + #[test] + fn dc_s2_shared_manifest_registration_across_database_parents_is_retained() { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + let other_parent = fixture.root.join("other-db-parent"); + fs::create_dir(&other_parent).unwrap(); + let alternate = DirectLayout::new(other_parent.join("other.ldb"), layout.manifest_path()); + register_direct_pair(&alternate).unwrap(); + assert!( + matches!(claim_maintenance(&target).unwrap(), MaintenanceAdmission::Unknown(r) if r == "direct_registered_pair_overlap") + ); + } + + #[test] + fn dc_s2_external_bound_managed_store_does_not_require_local_state() { + let fixture = Fixture::new(); + let external = ManagedLayout::new(fixture.root.join("external-storage")); + external.ensure_roots().unwrap(); + drop(open_locked(external.writer_lock_path(), LockMode::Exclusive).unwrap()); + let config_path = fixture.root.join("external-config.json"); + write_json_atomically(&config_path, &serde_json::json!({"schema_version":2, "repo_root": fixture.root, "storage_root":external.storage_root()})).unwrap(); + fs::remove_dir_all(&fixture.target.state_root).unwrap(); + let mut target = fixture.target.clone(); + target.config_path = Some(config_path); + target.storage_root = Some(external.storage_root().to_owned()); + let lease = acquired(claim_maintenance(&target).unwrap()); + assert!(lease.artifact_cohort_verified().unwrap()); + assert!(!fixture.target.state_root.exists()); + } + + #[test] + fn dc_s2_current_v2_receipt_and_unknown_v1_follow_existing_guardian_policy() { + use crate::storage::containment::{ContainmentProof, ExitOutcome}; + let fixture = Fixture::new(); + let lifecycle = fixture + .layout + .phase_workspace_root() + .join("phase-1/.lifecycle-complete"); + fs::create_dir_all(&lifecycle).unwrap(); + drop(open_locked(lifecycle.join("lease"), LockMode::Exclusive).unwrap()); + let descriptor = BuildDescriptorV2 { + version: 2, + build_id: "a".repeat(64), + lifecycle_dir: lifecycle.clone(), + endpoint: "unused".into(), + nonce: "unused".into(), + worker_pid: std::process::id(), + guardian_pid: std::process::id(), + proof: ContainmentProof::Pending, + }; + write_json_atomically(&lifecycle.join("proof.json"), &descriptor).unwrap(); + assert!( + matches!(claim_maintenance(&fixture.target).unwrap(), MaintenanceAdmission::Unknown(r) if r == "phase_containment_unknown") + ); + let receipt = ExitOutcome { + success: true, + code: Some(0), + signal: None, + high_water_bytes: 0, + cancelled: false, + containment_empty: true, + supervision_error: None, + }; + write_json_atomically(&lifecycle.join("receipt.json"), &receipt).unwrap(); + let lease = acquired(claim_maintenance(&fixture.target).unwrap()); + assert!(lifecycle.join("proof.json").exists()); + drop(lease); + write_json_atomically( + &fixture.layout.worker_state_path(), + &serde_json::json!({"version":1,"pid":std::process::id()}), + ) + .unwrap(); + assert!( + matches!(claim_maintenance(&fixture.target).unwrap(), MaintenanceAdmission::Unknown(r) if r == "legacy_quiescence_required") + ); + assert!(fixture.layout.worker_state_path().exists()); + } + + #[test] + fn dc_s2_waiter_on_detached_incarnation_cannot_write_fresh_public_root() { + use std::sync::mpsc; + let fixture = Fixture::new(); + let root = fixture.layout.artifacts_root(); + let exclusive = open_locked(root.join(".use.lock"), LockMode::Exclusive).unwrap(); + let (opened_tx, opened_rx) = mpsc::channel(); + let (start_tx, start_rx) = mpsc::channel(); + let producer_root = root.clone(); + let producer = std::thread::spawn(move || { + super::super::locks::before_lock_test_hook(move || { + opened_tx.send(()).unwrap(); + start_rx.recv_timeout(Duration::from_secs(10)).unwrap(); + }); + ArtifactUseLease::acquire_shared(&producer_root) + }); + opened_rx.recv_timeout(Duration::from_secs(10)).unwrap(); + let detached = fixture.layout.storage_root().join("detached"); + let moved = fs::rename(&root, &detached); + #[cfg(windows)] + { + // An open descendant prevents directory replacement on Windows. + // Exercise that real exclusion and let the waiting producer use + // only the unchanged physical root after its owner releases it. + let error = moved.unwrap_err(); + assert!(matches!(error.raw_os_error(), Some(5 | 32)), "{error}"); + let identity = path_identity(&root).unwrap(); + assert!(!detached.exists()); + start_tx.send(()).unwrap(); + drop(exclusive); + let lease = producer.join().unwrap().unwrap(); + lease.revalidate().unwrap(); + assert_eq!(lease.identity, identity); + } + #[cfg(not(windows))] + { + moved.unwrap(); + drop(ArtifactUseLease::acquire_shared(&root).unwrap()); + start_tx.send(()).unwrap(); + drop(exclusive); + assert!(producer.join().unwrap().is_err()); + assert!(observe_artifact_cohort(&root).unwrap().is_some()); + } + } + + #[test] + fn dc_s2_worker_launch_evidence_without_lifecycle_is_unknown() { + let fixture = Fixture::new(); + let root = fixture + .layout + .storage_root() + .join("workers/worker-incomplete"); + fs::create_dir_all(&root).unwrap(); + fs::write(root.join("request.json"), b"execution evidence").unwrap(); + assert!( + matches!(claim_maintenance(&fixture.target).unwrap(), MaintenanceAdmission::Unknown(r) if r == "phase_execution_evidence_without_lifecycle") + ); + assert!(root.join("request.json").exists()); + } + + #[test] + fn dc_s2_exact_borrowed_writer_requires_exclusive_exact_destination() { + let fixture = Fixture::new(); + let writer = open_locked(fixture.layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let lease = acquired(claim_maintenance_with_owned(&fixture.target, &writer).unwrap()); + assert!(lease.artifact_cohort_verified().unwrap()); + assert_eq!(lease.target(), &fixture.target); + drop(lease); + drop(writer); + let shared = open_locked(fixture.layout.writer_lock_path(), LockMode::Shared).unwrap(); + assert!(claim_maintenance_with_owned(&fixture.target, &shared).is_err()); + let foreign = open_locked(fixture.root.join("foreign.lock"), LockMode::Exclusive).unwrap(); + assert!(claim_maintenance_with_owned(&fixture.target, &foreign).is_err()); + } + + #[test] + fn dc_s2_writer_worker_and_startup_contention_are_nonblocking() { + let fixture = Fixture::new(); + for (path, reason) in [ + (fixture.layout.writer_lock_path(), "writer_in_use"), + (fixture.layout.worker_lock_path(), "worker_in_use"), + ] { + let held = open_locked(path, LockMode::Exclusive).unwrap(); + let started = Instant::now(); + assert!( + matches!(claim_maintenance(&fixture.target).unwrap(), MaintenanceAdmission::Deferred(r) if r == reason) + ); + assert!(started.elapsed() < Duration::from_secs(1)); + drop(held); + } + let startup = fixture + .layout + .phase_workspace_root() + .join(".lifecycle-starting"); + fs::create_dir_all(&startup).unwrap(); + let held = open_locked(startup.join("lease"), LockMode::Exclusive).unwrap(); + assert!( + matches!(claim_maintenance(&fixture.target).unwrap(), MaintenanceAdmission::Deferred(r) if r == "startup_in_use") + ); + drop(held); + let lease = acquired(claim_maintenance(&fixture.target).unwrap()); + assert!( + try_open_existing_locked(&startup.join("lease"), LockMode::Exclusive) + .unwrap() + .is_none() + ); + drop(lease); + } + + #[test] + fn dc_s2_missing_startup_lease_keeps_unknown_evidence() { + let fixture = Fixture::new(); + let startup = fixture + .layout + .phase_workspace_root() + .join(".lifecycle-unknown"); + fs::create_dir_all(&startup).unwrap(); + fs::write(startup.join("request.json"), b"launch evidence").unwrap(); + assert!( + matches!(claim_maintenance(&fixture.target).unwrap(), MaintenanceAdmission::Unknown(r) if r.contains("startup_lease_unknown")) + ); + assert_eq!( + fs::read(startup.join("request.json")).unwrap(), + b"launch evidence" + ); + assert!(!startup.join("lease").exists()); + } + + #[test] + fn dc_s2_owned_state_transition_never_waits_or_holds_state_during_validation() { + let fixture = Fixture::new(); + let lease = acquired(claim_maintenance(&fixture.target).unwrap()); + let state = open_locked(fixture.layout.state_lock_path(), LockMode::Shared).unwrap(); + assert_eq!(lease.with_state_lock(|| Ok(7)).unwrap(), None); + drop(state); + assert_eq!(lease.with_state_lock(|| Ok(7)).unwrap(), Some(7)); + } + + #[test] + fn dc_s2_producer_and_collector_exclude_whole_artifact_operation() { + let fixture = Fixture::new(); + let producer = ArtifactUseLease::acquire_shared(&fixture.layout.artifacts_root()).unwrap(); + assert!( + matches!(claim_maintenance(&fixture.target).unwrap(), MaintenanceAdmission::Deferred(r) if r == "artifact_root_in_use") + ); + fs::write( + fixture.layout.artifacts_root().join("produced"), + b"still protected", + ) + .unwrap(); + assert!( + ArtifactUseLease::try_exclusive(&fixture.layout.artifacts_root()) + .unwrap() + .is_none() + ); + drop(producer); + let lease = acquired(claim_maintenance(&fixture.target).unwrap()); + assert!(try_open_existing_locked( + &fixture.layout.artifacts_root().join(".use.lock"), + LockMode::Shared + ) + .unwrap() + .is_none()); + drop(lease); + } + + #[test] + fn dc_s2_direct_reader_and_alternate_manifest_exclude_collector() { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + let reader = DirectStore::new(layout.clone()) + .unwrap() + .begin_read() + .unwrap(); + assert!( + matches!(claim_maintenance(&target).unwrap(), MaintenanceAdmission::Deferred(r) if r == "writer_in_use") + ); + let alternate = DirectLayout::new( + layout.db_path(), + fixture.target.state_root.join("other-manifest.json"), + ); + let mut alt_target = target.clone(); + alt_target.direct_pair = Some(DirectPair { + database: alternate.db_path().to_owned(), + manifest: alternate.manifest_path().to_owned(), + }); + drop(ArtifactUseLease::acquire_shared(&alternate.artifact_root_path()).unwrap()); + assert!( + matches!(claim_maintenance(&alt_target).unwrap(), MaintenanceAdmission::Unknown(r) if r == "direct_registered_pair_overlap") + ); + assert!(DatabaseUseLease::try_exclusive(layout.db_path()) + .unwrap() + .is_none()); + drop(reader); + assert!(DatabaseUseLease::try_exclusive(layout.db_path()) + .unwrap() + .is_some()); + } + + #[test] + fn dc_s2_direct_borrowed_session_truthfully_defers_active_companions() { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + let session = DirectStore::new(layout).unwrap().begin_write().unwrap(); + let started = Instant::now(); + assert!( + matches!(claim_maintenance_with_owned(&target, session.writer_lease().unwrap()).unwrap(), MaintenanceAdmission::Deferred(r) if r == "database_parent_in_use") + ); + assert!(started.elapsed() < Duration::from_secs(1)); + drop(session); + let lease = acquired(claim_maintenance(&target).unwrap()); + assert!(lease.artifact_cohort_verified().unwrap()); + } + + #[test] + fn dc_s2_managed_borrowed_session_defers_active_reference_use_without_deadlock() { + let fixture = Fixture::new(); + let session = ManagedStore::new(fixture.layout.clone()) + .begin_write() + .unwrap(); + let started = Instant::now(); + assert!( + matches!(claim_maintenance_with_owned(&fixture.target, session.writer_lease().unwrap()).unwrap(), MaintenanceAdmission::Deferred(reason) if reason == "artifact_root_in_use") + ); + assert!(started.elapsed() < Duration::from_secs(1)); + drop(session); + let lease = acquired(claim_maintenance(&fixture.target).unwrap()); + assert!(lease.artifact_cohort_verified().unwrap()); + } + + #[test] + fn dc_s2_registered_shared_manifest_and_shared_database_are_retained() { + for shared_database in [true, false] { + let fixture = Fixture::new(); + let (target, layout) = fixture.direct(); + let alternate = if shared_database { + DirectLayout::new( + layout.db_path(), + fixture.target.state_root.join("alternate.json"), + ) + } else { + DirectLayout::new( + fixture.target.state_root.join("alternate.ldb"), + layout.manifest_path(), + ) + }; + register_direct_pair(&alternate).unwrap(); + assert!( + matches!(claim_maintenance(&target).unwrap(), MaintenanceAdmission::Unknown(r) if r == "direct_registered_pair_overlap") + ); + } + } + + #[test] + fn dc_s2_guarded_creation_and_controlled_proof_are_incarnation_bound() { + let fixture = Fixture::new(); + let lease = acquired(claim_maintenance(&fixture.target).unwrap()); + assert!(lease.artifact_cohort_verified().unwrap()); + assert!(!lease.cohort_verified().unwrap()); + let record = lease.cohort_record().unwrap(); + let path = lease.cohort_record_path(); + write_json_atomically(&path, &record).unwrap(); + assert!(lease.cohort_verified().unwrap()); + let old = fixture.layout.artifacts_root(); + let artifact_record = fs::read(old.join(".use-cohort.json")).unwrap(); + let detached = fixture.layout.storage_root().join("detached-artifacts"); + #[cfg(windows)] + { + let error = fs::rename(&old, &detached).unwrap_err(); + assert!(matches!(error.raw_os_error(), Some(5 | 32)), "{error}"); + lease.revalidate().unwrap(); + drop(lease); + } + fs::rename(&old, &detached).unwrap(); + drop(ArtifactUseLease::acquire_shared(&old).unwrap()); + #[cfg(not(windows))] + { + assert!(lease.revalidate().is_err()); + drop(lease); + } + // Copying the old proof onto a new incarnation is not authority. + fs::write(old.join(".use-cohort.json"), artifact_record).unwrap(); + let fresh = acquired(claim_maintenance(&fixture.target).unwrap()); + assert!(!fresh.artifact_cohort_verified().unwrap()); + assert!(!fresh.cohort_verified().unwrap()); + } + + #[test] + fn dc_s2_preexisting_root_never_becomes_modern_from_guard_acquisition() { + let fixture = Fixture::new(); + let root = fixture.root.join("preexisting"); + fs::create_dir(&root).unwrap(); + fs::write(root.join("legacy"), b"unknown").unwrap(); + drop(ArtifactUseLease::acquire_shared(&root).unwrap()); + assert!(!root.join(".use-cohort.json").exists()); + assert!(observe_artifact_cohort(&root).unwrap().is_none()); + } + + #[test] + fn dc_s2_inventory_cohort_observer_is_noncreating_and_validates_lock_identity() { + let fixture = Fixture::new(); + let root = fixture.root.join("missing"); + assert!(observe_artifact_cohort(&root).is_err()); + assert!(!root.exists()); + let root = fixture.root.join("existing"); + fs::create_dir(&root).unwrap(); + fs::write(root.join(".use-cohort.json"), b"true").unwrap(); + assert!(observe_artifact_cohort(&root).is_err()); + assert!(!root.join(".use.lock").exists()); + let root = fixture.layout.artifacts_root(); + let before = fs::read(root.join(".use-cohort.json")).unwrap(); + assert!(observe_artifact_cohort(&root).unwrap().is_some()); + assert_eq!(fs::read(root.join(".use-cohort.json")).unwrap(), before); + fs::remove_file(root.join(".use.lock")).unwrap(); + drop(open_locked(root.join(".use.lock"), LockMode::Shared).unwrap()); + assert!(observe_artifact_cohort(&root).unwrap().is_none()); + } + + #[cfg(unix)] + #[test] + fn dc_s2_cohort_recycled_numeric_lock_identity_does_not_restore_creation() { + let fixture = Fixture::new(); + let root = fixture.layout.artifacts_root(); + let path = root.join(".use.lock"); + let mut record: ArtifactCohortRecord = + serde_json::from_slice(&fs::read(root.join(".use-cohort.json")).unwrap()).unwrap(); + assert!(observe_artifact_cohort(&root).unwrap().is_some()); + fs::remove_file(&path).unwrap(); + drop(open_locked(&path, LockMode::Shared).unwrap()); + // Deterministically model inode ABA even on an allocator that did not + // recycle this time: the stale tuple now equals the replacement tuple. + // The creation nonce remains the old one; no old FD is retained. + record.coordination = path_identity(&path).unwrap(); + write_json_atomically(&root.join(".use-cohort.json"), &record).unwrap(); + let before_record = fs::read(root.join(".use-cohort.json")).unwrap(); + let before_lock = fs::read(&path).unwrap(); + assert!(before_lock.is_empty()); + assert!(observe_artifact_cohort(&root).unwrap().is_none()); + assert_eq!(fs::read(&path).unwrap(), before_lock); + assert_eq!( + fs::read(root.join(".use-cohort.json")).unwrap(), + before_record + ); + } + + #[test] + fn dc_s2_cohort_unknown_or_corrupt_creation_header_never_repairs_on_use_or_observation() { + for corrupt in [false, true] { + let fixture = Fixture::new(); + let root = fixture.layout.artifacts_root(); + let path = root.join(".use.lock"); + let bytes = if corrupt { + let mut bytes = fs::read(&path).unwrap(); + bytes[9] ^= 1; + bytes + } else { + Vec::new() + }; + fs::write(&path, &bytes).unwrap(); + let before = fs::read(root.join(".use-cohort.json")).unwrap(); + assert!(observe_artifact_cohort(&root).unwrap().is_none()); + let reader = ArtifactUseLease::acquire_shared(&root).unwrap(); + assert!(!reader.cohort_verified().unwrap()); + drop(reader); + assert_eq!(fs::read(&path).unwrap(), bytes); + assert_eq!(fs::read(root.join(".use-cohort.json")).unwrap(), before); + } + } + + #[test] + fn dc_s2_fresh_creation_header_is_bound_and_really_downgraded_to_shared() { + let fixture = Fixture::new(); + let root = fixture.root.join("fresh-nonce-root"); + let first = ArtifactUseLease::acquire_shared(&root).unwrap(); + let record: ArtifactCohortRecord = + serde_json::from_slice(&fs::read(root.join(".use-cohort.json")).unwrap()).unwrap(); + assert_eq!(record.version, 2); + assert_eq!( + first.lock.coordination_incarnation().unwrap(), + Some(record.coordination_incarnation) + ); + let second = try_open_existing_locked(&root.join(".use.lock"), LockMode::Shared) + .unwrap() + .expect("actual second shared handle must be admitted"); + assert!( + try_open_existing_locked(&root.join(".use.lock"), LockMode::Exclusive) + .unwrap() + .is_none() + ); + drop(first); + drop(second); + assert!(observe_artifact_cohort(&root).unwrap().is_some()); + } + + #[test] + fn dc_epoch_root_creation_nonce_is_bound_beyond_recycled_numeric_metadata() { + let fixture = Fixture::new(); + let (_layout, domain) = tracked_domain(&fixture, "nonce-bound-epoch"); + let before = observed(&domain); + let root = domain.artifact_root(); + let mut nonce = before.record.use_incarnation; + nonce[0] ^= 1; + fs::write( + root.join(".use.lock"), + super::super::locks::encode_coordination_incarnation(nonce), + ) + .unwrap(); + let cohort_path = root.join(".use-cohort.json"); + let mut cohort: ArtifactCohortRecord = + serde_json::from_slice(&fs::read(&cohort_path).unwrap()).unwrap(); + cohort.coordination_incarnation = nonce; + // Keep the same cohort and lock inode IDs while changing their bound + // creation generation; the old reference epoch must not become valid. + fs::write(&cohort_path, serde_json::to_vec(&cohort).unwrap()).unwrap(); + assert_eq!(path_identity(&cohort_path).unwrap(), before.record.cohort); + assert_eq!( + path_identity(&root.join(".use.lock")).unwrap(), + before.record.use_lock + ); + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Pending(_) + )); + assert!(ReferenceMutation::begin_in_domain(&domain).is_err()); + assert_eq!(read_reference_epoch(root).unwrap(), before); + } + + #[test] + fn dc_s2_legacy_cohort_v1_is_not_upgraded_by_read_or_guard_acquisition() { + let fixture = Fixture::new(); + let root = fixture.layout.artifacts_root(); + let record_path = root.join(".use-cohort.json"); + let mut record: serde_json::Value = + serde_json::from_slice(&fs::read(&record_path).unwrap()).unwrap(); + record["version"] = 1.into(); + record + .as_object_mut() + .unwrap() + .remove("coordination_incarnation"); + fs::write(&record_path, serde_json::to_vec(&record).unwrap()).unwrap(); + let before = fs::read(&record_path).unwrap(); + assert!(observe_artifact_cohort(&root).unwrap().is_none()); + drop(ArtifactUseLease::acquire_shared(&root).unwrap()); + assert_eq!(fs::read(&record_path).unwrap(), before); + } + + #[cfg(unix)] + #[test] + fn dc_s2_hardlinks_symlinks_and_case_aliases_cannot_bypass_resource_guard() { + use std::os::unix::fs::symlink; + let fixture = Fixture::new(); + let database = fixture.root.join("Graph.ldb"); + fs::write(&database, b"db").unwrap(); + let held = DatabaseUseLease::acquire_shared(&database).unwrap(); + assert!( + DatabaseUseLease::try_exclusive(&fixture.root.join("graph.ldb")) + .unwrap() + .is_none() + ); + let alias = fixture.root.join("alias.ldb"); + symlink(&database, &alias).unwrap(); + assert!(DatabaseUseLease::acquire_shared(&alias).is_err()); + fs::remove_file(&alias).unwrap(); + fs::hard_link(&database, &alias).unwrap(); + assert!(DatabaseUseLease::acquire_shared(&alias).is_err()); + drop(held); + } + + #[test] + fn dc_s2_replaced_writer_inode_does_not_validate_borrowed_authority() { + let fixture = Fixture::new(); + let path = fixture.layout.writer_lock_path(); + let writer = open_locked(&path, LockMode::Exclusive).unwrap(); + fs::remove_file(&path).unwrap(); + drop(open_locked(&path, LockMode::Shared).unwrap()); + assert!(claim_maintenance_with_owned(&fixture.target, &writer).is_err()); + } + + // Test-only owning preparation: all paths and budgeted buffers stay outside + // the primitive's authority callback, including errors and panic unwind. + struct FixedEpochPaths { + root: PathBuf, + destination: PathBuf, + writer: PathBuf, + root_use: PathBuf, + cohort: PathBuf, + epoch: PathBuf, + activity: PathBuf, + serial: PathBuf, + references: [Option; 2], + } + impl FixedEpochPaths { + fn new(domain: &ReferenceDomain) -> Self { + Self { + root: domain.artifact_root.clone(), + destination: domain.destination_root.clone(), + writer: domain.writer_path.clone(), + root_use: domain.artifact_root.join(".use.lock"), + cohort: domain.artifact_root.join(".use-cohort.json"), + epoch: domain.artifact_root.join(REFERENCE_EPOCH_FILE), + activity: domain.artifact_root.join(REFERENCE_ACTIVITY_FILE), + serial: domain.artifact_root.join(REFERENCE_SERIAL_FILE), + references: domain.reference_roots.clone(), + } + } + fn borrow(&self) -> CanonicalEpochPaths<'_> { + CanonicalEpochPaths { + root: &self.root, + destination: &self.destination, + writer: &self.writer, + root_use: &self.root_use, + cohort: &self.cohort, + epoch: &self.epoch, + activity: &self.activity, + serial: &self.serial, + reference_roots: [self.references[0].as_deref(), self.references[1].as_deref()], + } + } + } + struct FixedEpochBuffers { + memory: super::super::maintenance_budget::CleanupMemory, + scratch: super::super::locks::FixedControlScratch, + prepared: super::super::maintenance_budget::ReservedBytes, + read: super::super::maintenance_budget::ReservedBytes, + } + impl FixedEpochBuffers { + fn new() -> Self { + use super::super::{locks::FixedControlScratch, maintenance_budget::CleanupMemory}; + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .unwrap(), + ) + .unwrap(); + let prepared = memory + .reserve(2 * REFERENCE_EPOCH_SIZE) + .unwrap() + .bytes(2 * REFERENCE_EPOCH_SIZE) + .unwrap(); + let read = memory + .reserve(REFERENCE_EPOCH_SIZE) + .unwrap() + .bytes(REFERENCE_EPOCH_SIZE) + .unwrap(); + Self { + memory, + scratch, + prepared, + read, + } + } + } + fn fixed_epoch_guards(layout: &ManagedLayout) -> (WriterLease, ArtifactUseLease) { + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let root = ArtifactUseLease::try_exclusive(&layout.artifacts_root()) + .unwrap() + .unwrap(); + (writer, root) + } + fn fixed_epoch_controls_released(paths: &FixedEpochPaths) { + for path in [&paths.activity, &paths.serial] { + assert!(try_open_existing_locked(path, LockMode::Exclusive) + .unwrap() + .is_some()); + } + } + + #[test] + fn dc_fixed_epoch_exact_compare_precedes_increment_and_retains_real_controls() { + use super::super::maintenance_budget::MutationGrant; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-advance"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + let mut called = false; + let advance = with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + &mut |advance| { + called = true; + assert_eq!(advance.before, expected.counter()); + assert_eq!(advance.after, expected.counter() + 1); + for path in [&paths.activity, &paths.serial] { + assert!(try_open_existing_locked(path, LockMode::Exclusive) + .unwrap() + .is_none()); + } + assert!(matches!( + observe_reference_epoch(&domain).unwrap(), + EpochObservation::Pending(_) + )); + Ok(()) + }, + ) + .unwrap(); + assert!(called); + fixed_epoch_controls_released(&paths); + assert!(writer + .verifies(&layout.writer_lock_path(), LockMode::Exclusive) + .unwrap()); + root.revalidate().unwrap(); + drop(root); + assert_eq!(observed(&domain).counter(), advance.after); + assert!(buffers.memory.used() <= buffers.memory.limit()); + } + + #[test] + fn dc_fixed_epoch_stale_preparation_never_adopts_an_unrelated_increment() { + use super::super::{ + locks::{FixedControlError, FixedControlProblem}, + maintenance_budget::MutationGrant, + }; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-stale"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + drop(ReferenceMutation::begin_in_domain(&domain).unwrap()); + let before = fs::read(&paths.epoch).unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + let mut called = false; + assert!(matches!( + with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + &mut |_| { + called = true; + Ok(()) + } + ), + Err(FixedControlError::Invalid( + FixedControlProblem::EpochMismatch + )) + )); + assert!(!called); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + fixed_epoch_controls_released(&paths); + } + + #[test] + fn dc_fixed_epoch_rejects_shared_writer_and_shared_root_without_reacquiring_them() { + use super::super::{ + locks::{FixedControlError, FixedControlProblem}, + maintenance_budget::MutationGrant, + }; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-wrong-guards"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let shared_writer = open_locked(layout.writer_lock_path(), LockMode::Shared).unwrap(); + let root = ArtifactUseLease::try_exclusive(&layout.artifacts_root()) + .unwrap() + .unwrap(); + let before = fs::read(&paths.epoch).unwrap(); + assert!(matches!( + with_canonical_epoch_advance( + &frame, + &shared_writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + &mut |_| panic!("not admitted") + ), + Err(FixedControlError::Invalid( + FixedControlProblem::GuardMismatch + )) + )); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + drop(root); + drop(shared_writer); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let shared_root = ArtifactUseLease::acquire_in_domain(&domain).unwrap(); + let before = fs::read(&paths.epoch).unwrap(); + assert!(matches!( + with_canonical_epoch_advance( + &frame, + &writer, + &shared_root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + &mut |_| panic!("not admitted") + ), + Err(FixedControlError::Invalid( + FixedControlProblem::GuardMismatch + )) + )); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + } + + #[test] + fn dc_fixed_epoch_activity_contention_returns_and_preserves_caller_ownership() { + use super::super::{ + locks::{FixedControlError, FixedControlProblem}, + maintenance_budget::MutationGrant, + }; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-contention"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + let activity = try_open_existing_locked(&paths.activity, LockMode::Shared) + .unwrap() + .unwrap(); + let before = fs::read(&paths.epoch).unwrap(); + assert!(matches!( + with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + &mut |_| panic!("not admitted") + ), + Err(FixedControlError::Invalid(FixedControlProblem::Busy)) + )); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + assert!(writer + .verifies(&layout.writer_lock_path(), LockMode::Exclusive) + .unwrap()); + root.revalidate().unwrap(); + drop(activity); + fixed_epoch_controls_released(&paths); + } + + #[test] + fn dc_fixed_epoch_cohort_or_control_replacement_rejects_before_effect() { + use super::super::maintenance_budget::MutationGrant; + for replace_cohort in [false, true] { + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-control-replaced"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let target = if replace_cohort { + &paths.cohort + } else { + &paths.serial + }; + let bytes = fs::read(target).unwrap(); + fs::rename(target, target.with_extension("old")).unwrap(); + fs::write(target, bytes).unwrap(); + let before = fs::read(&paths.epoch).unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + assert!(with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + &mut |_| panic!("not admitted") + ) + .is_err()); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + fixed_epoch_controls_released(&paths); + } + } + + #[test] + fn dc_fixed_epoch_replaced_root_with_same_control_inodes_is_not_the_same_domain() { + use super::super::maintenance_budget::MutationGrant; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-root-replaced"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + let old = paths.root.with_extension("old"); + #[cfg(windows)] + { + let error = fs::rename(&paths.root, &old).unwrap_err(); + assert!(matches!(error.raw_os_error(), Some(5 | 32)), "{error}"); + root.revalidate().unwrap(); + drop(root); + drop(writer); + } + fs::rename(&paths.root, &old).unwrap(); + fs::create_dir(&paths.root).unwrap(); + for entry in fs::read_dir(&old).unwrap() { + let entry = entry.unwrap(); + fs::rename(entry.path(), paths.root.join(entry.file_name())).unwrap(); + } + #[cfg(windows)] + let (writer, root) = fixed_epoch_guards(&layout); + let before = fs::read(&paths.epoch).unwrap(); + assert!(with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + &mut |_| panic!("not admitted") + ) + .is_err()); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + fixed_epoch_controls_released(&paths); + } + + #[test] + fn dc_fixed_epoch_callback_error_and_unwind_release_owned_controls_only() { + use super::super::{ + locks::{FixedControlError, FixedControlProblem}, + maintenance_budget::MutationGrant, + }; + for panic_in_callback in [false, true] { + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-callback-exit"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + let memory_before = buffers.memory.used(); + let mut callback = |_| -> Result<(), FixedControlError> { + if panic_in_callback { + panic!("fixture callback unwind"); + } + Err(FixedControlError::Invalid( + FixedControlProblem::InvalidRecord, + )) + }; + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::from_secs(10)), + &mut callback, + ) + })); + if panic_in_callback { + assert!(result.is_err()); + } else { + assert!(result.unwrap().is_err()); + } + fixed_epoch_controls_released(&paths); + assert!(writer + .verifies(&layout.writer_lock_path(), LockMode::Exclusive) + .unwrap()); + root.revalidate().unwrap(); + drop(root); + assert_eq!(observed(&domain).counter(), expected.counter() + 1); + assert_eq!(buffers.memory.used(), memory_before); + } + } + + #[test] + fn dc_fixed_epoch_zero_and_short_grants_have_no_epoch_effect() { + use super::super::maintenance_budget::MutationGrant; + for remaining in [0, 1, 16] { + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-budget"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let before = fs::read(&paths.epoch).unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(10)); + grant.take_entries(100 - remaining).unwrap(); + assert!(with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut grant, + &mut |_| panic!("not admitted") + ) + .is_err()); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + fixed_epoch_controls_released(&paths); + } + } + + #[test] + fn dc_fixed_epoch_complete_effect_reservation_allows_its_last_reserved_entry() { + use super::super::maintenance_budget::MutationGrant; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-exact-grant"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + let mut grant = MutationGrant::for_test(17, std::time::Duration::from_secs(10)); + let mut called = false; + with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut grant, + &mut |_| { + called = true; + Ok(()) + }, + ) + .unwrap(); + assert!(called); + assert_eq!(grant.remaining_entries(), 0); + root.revalidate().unwrap(); + drop(root); + assert_eq!(observed(&domain).counter(), expected.counter() + 1); + } + + #[test] + fn dc_fixed_epoch_expired_grant_does_not_open_or_change_epoch_controls() { + use super::super::maintenance_budget::MutationGrant; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-expired"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + let before = fs::read(&paths.epoch).unwrap(); + let (writer, root) = fixed_epoch_guards(&layout); + assert!(matches!( + with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::for_test(100, std::time::Duration::ZERO), + &mut |_| panic!("not admitted") + ), + Err(super::super::locks::FixedControlError::Grant( + super::super::maintenance_budget::GrantExhausted::Time + )) + )); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + fixed_epoch_controls_released(&paths); + } + + #[test] + fn dc_fixed_epoch_preparation_delay_does_not_consume_fresh_mutation_grant() { + use super::super::maintenance_budget::MutationGrant; + let fixture = Fixture::new(); + let (layout, domain) = tracked_domain(&fixture, "fixed-slow-preparation"); + let expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let frame = prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch, + ) + .unwrap(); + std::thread::sleep(std::time::Duration::from_millis(110)); + // Real writer ownership is available during the advisory pause. + drop( + try_open_existing_locked(&layout.writer_lock_path(), LockMode::Exclusive) + .unwrap() + .unwrap(), + ); + let (writer, root) = fixed_epoch_guards(&layout); + with_canonical_epoch_advance( + &frame, + &writer, + &root, + &mut buffers.read, + &mut buffers.scratch, + &mut MutationGrant::start(), + &mut |_| Ok(()), + ) + .unwrap(); + root.revalidate().unwrap(); + drop(root); + assert_eq!(observed(&domain).counter(), expected.counter() + 1); + } + + #[test] + fn dc_fixed_epoch_preparation_is_noncreating_and_rejects_exhausted_or_oversized_controls() { + let fixture = Fixture::new(); + let (_layout, domain) = tracked_domain(&fixture, "fixed-preparation-errors"); + let mut expected = observed(&domain); + let paths = FixedEpochPaths::new(&domain); + let mut buffers = FixedEpochBuffers::new(); + let before = fs::read(&paths.epoch).unwrap(); + expected.record.counter = u64::MAX; + assert!(prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch + ) + .is_err()); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + let expected = observed(&domain); + fs::write(&paths.cohort, vec![b'x'; 1025]).unwrap(); + assert!(prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch + ) + .is_err()); + fs::remove_file(&paths.cohort).unwrap(); + assert!(prepare_canonical_epoch_frame( + paths.borrow(), + &expected, + &mut buffers.prepared, + &mut buffers.read, + &mut buffers.scratch + ) + .is_err()); + assert!(!paths.cohort.exists()); + assert_eq!(fs::read(&paths.epoch).unwrap(), before); + } +} + +/// Owned advisory epoch images. Paths and both images remain off the mutation +/// slot; a callback borrows only their fixed frame. No authority is stored. +pub(super) struct PreparedEpoch { + domain: ReferenceDomain, + expected: ReferenceEpoch, + controls: [PathBuf; 5], + images: super::maintenance_budget::ReservedBytes, + cohort: super::locks::FixedFileStamp, + header: [u8; super::locks::COORDINATION_HEADER_BYTES], + _paths: super::maintenance_budget::MemoryReservation, +} +impl PreparedEpoch { + pub(super) fn prepare( + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + memory: &super::maintenance_budget::CleanupMemory, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, + ) -> Result { + use super::locks::{FixedControlError as Error, FixedControlProblem as Problem}; + if domain.scope != expected.record.scope + || domain.artifact_identity != Some(expected.record.root) + || domain.use_identity != Some(expected.record.use_lock) + { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + let mut path_bytes = 16384usize; + for path in [ + Some(&domain.artifact_root), + Some(&domain.destination_root), + Some(&domain.writer_path), + domain.reference_roots[0].as_ref(), + domain.reference_roots[1].as_ref(), + ] + .into_iter() + .flatten() + { + path_bytes = path_bytes + .checked_add( + path.as_os_str() + .as_encoded_bytes() + .len() + .checked_mul(16) + .ok_or(Error::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?, + ) + .ok_or(Error::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?; + } + let paths = memory.reserve(path_bytes).map_err(Error::Resource)?; + let domain = domain.clone(); + let controls = [ + domain.artifact_root.join(".use.lock"), + domain.artifact_root.join(".use-cohort.json"), + domain.artifact_root.join(REFERENCE_EPOCH_FILE), + domain.artifact_root.join(REFERENCE_ACTIVITY_FILE), + domain.artifact_root.join(REFERENCE_SERIAL_FILE), + ]; + let mut images = memory + .reserve(2 * REFERENCE_EPOCH_SIZE) + .map_err(Error::Resource)? + .bytes(2 * REFERENCE_EPOCH_SIZE) + .map_err(Error::Resource)?; + let frame = prepare_canonical_epoch_frame( + CanonicalEpochPaths { + root: &domain.artifact_root, + destination: &domain.destination_root, + writer: &domain.writer_path, + root_use: &controls[0], + cohort: &controls[1], + epoch: &controls[2], + activity: &controls[3], + serial: &controls[4], + reference_roots: [ + domain.reference_roots[0].as_deref(), + domain.reference_roots[1].as_deref(), + ], + }, + expected, + &mut images, + read, + scratch, + )?; + let cohort = frame.cohort; + let header = frame.coordination_header; + Ok(Self { + domain, + expected: expected.clone(), + controls, + images, + cohort, + header, + _paths: paths, + }) + } + pub(super) fn frame(&self) -> CanonicalEpochFrame<'_> { + CanonicalEpochFrame { + paths: CanonicalEpochPaths { + root: &self.domain.artifact_root, + destination: &self.domain.destination_root, + writer: &self.domain.writer_path, + root_use: &self.controls[0], + cohort: &self.controls[1], + epoch: &self.controls[2], + activity: &self.controls[3], + serial: &self.controls[4], + reference_roots: [ + self.domain.reference_roots[0].as_deref(), + self.domain.reference_roots[1].as_deref(), + ], + }, + expected: &self.expected, + bytes: self.images.as_slice(), + cohort: self.cohort, + coordination_header: self.header, + } + } +} + +impl PreparedEpoch { + /// Local lane observes a stable fixed record without requiring exclusive + /// global observation. A concurrently torn producer write simply retries. + pub(super) fn prepare_local( + domain: &ReferenceDomain, + memory: &super::maintenance_budget::CleanupMemory, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, + ) -> Result { + let expected = read_reference_epoch(domain.artifact_root()).map_err(|_| { + super::locks::FixedControlError::Invalid( + super::locks::FixedControlProblem::InvalidRecord, + ) + })?; + Self::prepare(domain, &expected, memory, read, scratch) + } + /// Invalidate CURRENT global E under Shared activity + Exclusive serial. + /// Immutable local source E/A/G are deliberately not parameters to this + /// primitive. It cannot relabel a local sealed source as global references. + #[cfg(test)] + pub(super) fn with_local_invalidation( + &self, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, + effect_entries: usize, + apply: &mut impl FnMut( + CanonicalEpochAdvance, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError>, + ) -> Result { + self.with_local_validated( + read, + scratch, + grant, + effect_entries, + &mut |_, _| Ok(()), + apply, + ) + } + pub(super) fn with_local_validated( + &self, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, + effect_entries: usize, + validate: &mut impl FnMut( + &mut super::locks::FixedControlScratch, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError>, + apply: &mut impl FnMut( + CanonicalEpochAdvance, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError>, + ) -> Result { + self.with_local_validated_owned_root( + None, + read, + scratch, + grant, + effect_entries, + validate, + apply, + ) + } + // Keep independent borrowed authorities and caller-owned scratch explicit. + #[allow(clippy::too_many_arguments)] + pub(super) fn with_local_validated_owned_root( + &self, + borrowed_root: Option<&super::locks::FixedLockedFile>, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, + effect_entries: usize, + validate: &mut impl FnMut( + &mut super::locks::FixedControlScratch, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError>, + apply: &mut impl FnMut( + CanonicalEpochAdvance, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), super::locks::FixedControlError>, + ) -> Result { + use super::locks::{ + read_fixed_control, FixedControlError as Error, FixedControlKind as Kind, + FixedControlProblem as Problem, FixedFileStamp, FixedLockedFile, + }; + grant.check()?; + let frame = self.frame(); + let expected = &self.expected.record; + let owned_root = if borrowed_root.is_none() { + let root_file = + fixed_epoch_open(frame.paths.root_use, Kind::File, false, scratch, grant)?; + Some( + FixedLockedFile::try_lock(root_file, LockMode::Shared)? + .ok_or(Error::Invalid(Problem::Busy))?, + ) + } else { + None + }; + let root_use = borrowed_root + .or(owned_root.as_ref()) + .ok_or(Error::Invalid(Problem::Busy))?; + if borrowed_root.is_some() && root_use.mode() != LockMode::Exclusive { + return Err(Error::Invalid(Problem::Busy)); + } + if FixedFileStamp::capture(root_use.file(), Kind::File)?.identity() != expected.use_lock + || !root_use.matches_fixed_header(&self.header)? + { + return Err(Error::Invalid(Problem::Changed)); + } + let activity_file = + fixed_epoch_open(frame.paths.activity, Kind::File, false, scratch, grant)?; + let activity = FixedLockedFile::try_lock(activity_file, LockMode::Shared)? + .ok_or(Error::Invalid(Problem::Busy))?; + let serial_file = fixed_epoch_open(frame.paths.serial, Kind::File, true, scratch, grant)?; + let serial = FixedLockedFile::try_lock(serial_file, LockMode::Exclusive)? + .ok_or(Error::Invalid(Problem::Busy))?; + for (path, kind, id) in [ + (frame.paths.root, Kind::Directory, expected.root), + (frame.paths.root_use, Kind::File, expected.use_lock), + (frame.paths.activity, Kind::File, expected.activity), + (frame.paths.serial, Kind::File, expected.serial), + (frame.paths.cohort, Kind::File, expected.cohort), + ] { + let file = fixed_epoch_open(path, kind, false, scratch, grant)?; + if FixedFileStamp::capture(&file, kind)?.identity() != id { + return Err(Error::Invalid(Problem::Changed)); + } + } + if FixedFileStamp::capture(activity.file(), Kind::File)?.identity() != expected.activity + || FixedFileStamp::capture(serial.file(), Kind::File)?.identity() != expected.serial + { + return Err(Error::Invalid(Problem::Changed)); + } + let mut epoch = fixed_epoch_open(frame.paths.epoch, Kind::File, true, scratch, grant)?; + let stamp = FixedFileStamp::capture(&epoch, Kind::File)?; + if stamp.identity() != self.expected.file || stamp.len() != REFERENCE_EPOCH_SIZE as u64 { + return Err(Error::Invalid(Problem::Changed)); + } + read_fixed_control(&mut epoch, &stamp, read)?; + // Fixed2048 checksum/decimal patch is the explicit local invalidation + // exception. No Serde/Value/string/path allocation or payload hash. + let bytes = &mut read.as_mut_slice()[..REFERENCE_EPOCH_SIZE]; + if bytes[..8] != REFERENCE_EPOCH_MAGIC[..] + || Sha256::digest(&bytes[..REFERENCE_EPOCH_SIZE - 32]).as_slice() + != &bytes[REFERENCE_EPOCH_SIZE - 32..] + { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + let template = &self.images.as_slice()[..REFERENCE_EPOCH_SIZE]; + let marker = b"\"counter\":"; + let position = template + .windows(marker.len()) + .position(|w| w == marker) + .ok_or(Error::Invalid(Problem::InvalidRecord))? + + marker.len(); + if bytes[12..position] != template[12..position] { + return Err(Error::Invalid(Problem::Changed)); + } + let mut end = position; + let mut before = 0u64; + while end < REFERENCE_EPOCH_SIZE - 32 && bytes[end].is_ascii_digit() { + before = before + .checked_mul(10) + .and_then(|n| n.checked_add(u64::from(bytes[end] - b'0'))) + .ok_or(Error::Invalid(Problem::EpochExhausted))?; + end += 1; + } + if end == position || (end - position > 1 && bytes[position] == b'0') { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + let after = before + .checked_add(1) + .ok_or(Error::Invalid(Problem::EpochExhausted))?; + let length = u32::from_le_bytes(bytes[8..12].try_into().unwrap()) as usize; + let suffix = b",\"covered\":true}"; + if end + suffix.len() != 12 + length + || bytes.get(end..end + suffix.len()) != Some(suffix.as_slice()) + || bytes[12 + length..REFERENCE_EPOCH_SIZE - 32] + .iter() + .any(|b| *b != 0) + { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + let mut decimal = [0u8; 20]; + let mut cursor = decimal.len(); + let mut number = after; + loop { + cursor -= 1; + decimal[cursor] = b'0' + (number % 10) as u8; + number /= 10; + if number == 0 { + break; + } + } + let digits = &decimal[cursor..]; + let updated_end = position + digits.len(); + bytes[position..updated_end].copy_from_slice(digits); + bytes[updated_end..updated_end + suffix.len()].copy_from_slice(suffix); + bytes[updated_end + suffix.len()..REFERENCE_EPOCH_SIZE - 32].fill(0); + bytes[8..12].copy_from_slice(&((updated_end + suffix.len() - 12) as u32).to_le_bytes()); + let checksum = Sha256::digest(&bytes[..REFERENCE_EPOCH_SIZE - 32]); + bytes[REFERENCE_EPOCH_SIZE - 32..].copy_from_slice(&checksum); + validate(scratch, grant)?; + grant.take_entries(effect_entries.checked_add(2).ok_or(Error::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?)?; + grant.check_time()?; + epoch.seek(SeekFrom::Start(0))?; + epoch.write_all(bytes)?; + grant.check_time()?; + epoch.sync_all()?; + grant.check_time()?; + // Exact fresh-address binding and actual readback; use the already + // allocated second image as scratch, never allocate under authority. + let current = + super::locks::open_fixed_control(frame.paths.epoch, Kind::File, false, scratch)?; + if FixedFileStamp::capture(¤t, Kind::File)?.identity() != self.expected.file { + return Err(Error::Invalid(Problem::Changed)); + } + let mut verify = [0u8; REFERENCE_EPOCH_SIZE]; + let mut current = current; + current.read_exact(&mut verify)?; + if verify != bytes { + return Err(Error::Invalid(Problem::Changed)); + } + grant.check_time()?; + let advance = CanonicalEpochAdvance { before, after }; + apply(advance, grant)?; + drop(serial); + drop(activity); + drop(owned_root); + Ok(advance) + } +} + +impl PreparedEpoch { + pub(super) fn root_facts(&self) -> (FileIdentity, FileIdentity, [u8; 32], FileIdentity) { + ( + self.expected.record.root, + self.expected.record.use_lock, + self.expected.record.use_incarnation, + self.expected.record.cohort, + ) + } +} + +pub(super) struct FreshWorkspacePublisher { + pub(super) anchor: super::locks::RootedDirectory, + pub(super) nonce: [u8; 32], + pub(super) owner: FileIdentity, + pub(super) parent_root: FileIdentity, + pub(super) parent_coordination: FileIdentity, + pub(super) parent_nonce: [u8; 32], + pub(super) parent_cohort: FileIdentity, + pub(super) publication_binding: FileIdentity, + pub(super) registry: FileIdentity, +} +/// Private genuine-creation hook. Kind1 is a scoped managed run, Kind2 is a +/// registered worker workspace. Prefix/shape or a borrowed root-use bool cannot +/// enter this factory; both the fresh physical root and live domain mutation +/// are required. Optional proof failure leaves primary work untracked. +pub(super) fn initialize_workspace_publisher( + fresh: &super::layout::FreshReferenceRoot, + domain: &ReferenceDomain, + mutation: &ReferenceMutation<'_>, + owner: &LockedFile, + kind: u8, +) -> Result, NativeError> { + let Some(parent) = mutation.artifact_root_producer_capability()? else { + return Ok(None); + }; + if !parent.revalidate()? || parent.root() != domain.artifact_root() || !domain.revalidate()? { + return Ok(None); + } + let registry = match (kind, domain.scope.kind) { + (1, 1) => domain.reference_roots[1] + .clone() + .ok_or_else(|| invalid("run registry missing"))?, + (1, 2) => fresh + .path() + .parent() + .ok_or_else(|| invalid("run registry missing"))? + .to_owned(), + (3, 1) => domain.destination_root.join("phases"), + (3, 2) => { + let pair = domain + .raw_address + .as_ref() + .ok_or_else(|| invalid("direct addresses missing"))?; + DirectLayout::new(&pair[0], &pair[1]).phase_workspace_root() + } + (2, 1) => domain.destination_root.join("workers"), + (2, 2) => { + let pair = domain + .raw_address + .as_ref() + .ok_or_else(|| invalid("direct addresses missing"))?; + DirectLayout::new(&pair[0], &pair[1]).worker_workspace_root_path() + } + _ => return Ok(None), + }; + let registry_id = directory_identity(®istry)?; + if fresh.path().parent().map(directory_identity).transpose()? != Some(registry_id) + || directory_identity(fresh.path())? != fresh.identity() + || !owner.verifies(&fresh.path().join("lease.lock"), LockMode::Exclusive)? + { + return Ok(None); + } + let anchor = super::locks::RootedDirectory::open(fresh.path(), fresh.identity())?; + let mut nonce = [0; 32]; + getrandom::fill(&mut nonce).map_err(|_| invalid("workspace entropy unavailable"))?; + owner.initialize_creation_incarnation(nonce)?; + let creation = FreshPublicationRoot { + anchor: &anchor, + root_use: owner, + incarnation: nonce, + }; + let mut scratch = super::maintenance_controls::PublicationFactoryScratch::new(); + if super::maintenance_controls::initialize_creation_controls( + &creation, + &mut scratch, + &super::maintenance_controls::WorkspaceControl::building().encode(), + ) + .is_err() + { + return Ok(None); + } + let publication_binding = super::locks::file_identity( + &anchor.open_readonly_file(super::maintenance_controls::BINDING_NAME)?, + )?; + Ok(Some(FreshWorkspacePublisher { + anchor, + nonce, + owner: owner.identity()?, + parent_root: parent.root_identity(), + parent_coordination: parent.coordination_identity(), + parent_nonce: parent.incarnation(), + parent_cohort: parent.cohort_identity(), + publication_binding, + registry: registry_id, + })) +} +impl ReferenceDomain { + pub(crate) fn workspace_startup_path(&self) -> PathBuf { + self.destination_root.join(".workspace-startup.lock") + } + pub(crate) fn workspace_registry( + &self, + kind: u8, + state_root: Option<&Path>, + ) -> Option { + match (self.scope.kind, kind) { + (1, 1) => Some(self.destination_root.join("runs")), + (1, 2) => Some(self.destination_root.join("workers")), + (1, 3) => Some(self.destination_root.join("phases")), + (2, 1) => Some( + state_root + .unwrap_or(&self.destination_root) + .join("direct-runs"), + ), + (2, 2) | (2, 3) => self.raw_address.as_ref().map(|pair| { + let layout = DirectLayout::new(&pair[0], &pair[1]); + if kind == 2 { + layout.worker_workspace_root_path() + } else { + layout.phase_workspace_root() + } + }), + _ => None, + } + } +} + +/// Storage-private sealed-by-visibility producer interface. Implementations +/// borrow actual live owner/activity guards; serialized facts cannot implement +/// this at any external producer API boundary. +pub(super) trait PublicationRootAuthority { + fn root(&self) -> &Path; + fn root_identity(&self) -> FileIdentity; + fn coordination_identity(&self) -> FileIdentity; + fn incarnation(&self) -> [u8; 32]; + fn cohort_identity(&self) -> FileIdentity; + fn revalidate(&self) -> Result; +} +impl PublicationRootAuthority for ArtifactRootProducerCapability<'_> { + fn root(&self) -> &Path { + ArtifactRootProducerCapability::root(self) + } + fn root_identity(&self) -> FileIdentity { + ArtifactRootProducerCapability::root_identity(self) + } + fn coordination_identity(&self) -> FileIdentity { + ArtifactRootProducerCapability::coordination_identity(self) + } + fn incarnation(&self) -> [u8; 32] { + ArtifactRootProducerCapability::incarnation(self) + } + fn cohort_identity(&self) -> FileIdentity { + ArtifactRootProducerCapability::cohort_identity(self) + } + fn revalidate(&self) -> Result { + ArtifactRootProducerCapability::revalidate(self) + } +} + +impl PreparedEpoch { + fn advisory_current( + &self, + read: &mut super::maintenance_budget::ReservedBytes, + scratch: &mut super::locks::FixedControlScratch, + ) -> Result { + use super::locks::{ + open_fixed_control, read_fixed_control, FixedControlKind as Kind, FixedFileStamp, + FixedLockedFile, + }; + let frame = self.frame(); + let expected = &self.expected.record; + let root_file = open_fixed_control(frame.paths.root_use, Kind::File, false, scratch)?; + let Some(root_use) = FixedLockedFile::try_lock(root_file, LockMode::Shared)? else { + return Ok(false); + }; + let activity_file = open_fixed_control(frame.paths.activity, Kind::File, true, scratch)?; + let Some(activity) = FixedLockedFile::try_lock(activity_file, LockMode::Exclusive)? else { + return Ok(false); + }; + if FixedFileStamp::capture(root_use.file(), Kind::File)?.identity() != expected.use_lock + || !root_use.matches_fixed_header(&self.header)? + || FixedFileStamp::capture(activity.file(), Kind::File)?.identity() != expected.activity + { + return Ok(false); + } + for (path, kind, id) in [ + (frame.paths.root, Kind::Directory, expected.root), + ( + frame.paths.destination, + Kind::Directory, + expected.scope.destination, + ), + (frame.paths.writer, Kind::File, expected.scope.writer), + (frame.paths.root_use, Kind::File, expected.use_lock), + (frame.paths.activity, Kind::File, expected.activity), + ] { + let file = open_fixed_control(path, kind, false, scratch)?; + if FixedFileStamp::capture(&file, kind)?.identity() != id { + return Ok(false); + } + } + for (path, id) in frame + .paths + .reference_roots + .into_iter() + .zip(expected.scope.reference_roots) + { + match (path, id) { + (Some(path), Some(id)) => { + let file = open_fixed_control(path, Kind::Directory, false, scratch)?; + if FixedFileStamp::capture(&file, Kind::Directory)?.identity() != id { + return Ok(false); + } + } + (None, None) => (), + _ => return Ok(false), + } + } + let cohort = open_fixed_control(frame.paths.cohort, Kind::File, false, scratch)?; + if frame.cohort.recheck(&cohort).is_err() { + return Ok(false); + } + let mut epoch = open_fixed_control(frame.paths.epoch, Kind::File, false, scratch)?; + let stamp = FixedFileStamp::capture(&epoch, Kind::File)?; + if stamp.identity() != self.expected.file || stamp.len() != REFERENCE_EPOCH_SIZE as u64 { + return Ok(false); + } + read_fixed_control(&mut epoch, &stamp, read)?; + Ok(read.as_slice()[..REFERENCE_EPOCH_SIZE] + == self.images.as_slice()[..REFERENCE_EPOCH_SIZE]) + } +} + +pub(crate) fn expected_reference_current( + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result { + use super::locks::{FixedControlError as Error, FixedControlScratch}; + let mut scratch = FixedControlScratch::new( + memory + .reserve(FixedControlScratch::required_bytes()) + .map_err(Error::Resource)?, + )?; + let mut read = memory + .reserve(REFERENCE_EPOCH_SIZE) + .map_err(Error::Resource)? + .bytes(REFERENCE_EPOCH_SIZE) + .map_err(Error::Resource)?; + let prepared = PreparedEpoch::prepare(domain, expected, memory, &mut read, &mut scratch)?; + prepared.advisory_current(&mut read, &mut scratch) +} + +impl ReferenceEpoch { + /// Exact facts only; outer journal owns its checksum. Decoding cannot mint + /// authority and resume must compare this ORIGINAL E under actual guards. + pub(crate) fn encode_checkpoint(&self, out: &mut [u8; 512]) { + out.fill(0); + out[..8].copy_from_slice(b"CGECHK01"); + out[8..12].copy_from_slice(&self.record.version.to_le_bytes()); + out[12] = self.record.scope.kind; + out[13] = u8::from(self.record.covered); + out[14] = u8::from(self.record.scope.reference_roots[0].is_some()); + out[15] = u8::from(self.record.scope.reference_roots[1].is_some()); + out[16..24].copy_from_slice(&self.record.counter.to_le_bytes()); + for (index, identity) in [ + Some(self.file), + Some(self.record.root), + Some(self.record.use_lock), + Some(self.record.cohort), + Some(self.record.scope.destination), + Some(self.record.scope.writer), + Some(self.record.activity), + Some(self.record.serial), + self.record.scope.reference_roots[0], + self.record.scope.reference_roots[1], + ] + .into_iter() + .enumerate() + { + if let Some(identity) = identity { + let (a, b) = identity.fixed_words(); + out[24 + index * 16..32 + index * 16].copy_from_slice(&a.to_le_bytes()); + out[32 + index * 16..40 + index * 16].copy_from_slice(&b.to_le_bytes()); + } + } + out[184..216].copy_from_slice(&self.record.use_incarnation); + out[216..248].copy_from_slice(&self.record.incarnation); + out[248..280].copy_from_slice(&self.record.scope.address); + } + pub(crate) fn decode_checkpoint( + input: &[u8; 512], + ) -> Result { + use super::locks::{FixedControlError as Error, FixedControlProblem as Problem}; + if &input[..8] != b"CGECHK01" + || u32::from_le_bytes(input[8..12].try_into().unwrap()) != 2 + || !matches!(input[12], 1 | 2) + || input[13..16].iter().any(|b| *b > 1) + || input[280..].iter().any(|b| *b != 0) + { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + let identity = |index: usize| { + FileIdentity::from_fixed_words( + u64::from_le_bytes(input[24 + index * 16..32 + index * 16].try_into().unwrap()), + u64::from_le_bytes(input[32 + index * 16..40 + index * 16].try_into().unwrap()), + ) + }; + let mut roots = [None, None]; + for (index, root) in roots.iter_mut().enumerate() { + if input[14 + index] == 1 { + *root = Some(identity(8 + index)); + } else if input[152 + index * 16..168 + index * 16] + .iter() + .any(|b| *b != 0) + { + return Err(Error::Invalid(Problem::InvalidRecord)); + } + } + Ok(Self { + file: identity(0), + record: ReferenceEpochRecord { + version: 2, + root: identity(1), + use_lock: identity(2), + use_incarnation: input[184..216].try_into().unwrap(), + cohort: identity(3), + scope: ReferenceScopeRecord { + kind: input[12], + destination: identity(4), + writer: identity(5), + address: input[248..280].try_into().unwrap(), + reference_roots: roots, + }, + incarnation: input[216..248].try_into().unwrap(), + activity: identity(6), + serial: identity(7), + counter: u64::from_le_bytes(input[16..24].try_into().unwrap()), + covered: input[13] == 1, + }, + }) + } +} + +// Only the actual administrative enrollment kernel implements this sealed +// borrowing interface. Foundations do not depend on orchestration modules. +pub(super) mod administrative_coverage_seal { + pub trait Sealed {} +} +pub(crate) trait AdministrativeCoverageAuthority: + administrative_coverage_seal::Sealed +{ + fn directory(&self) -> &super::locks::RootedDirectory; + fn artifact_directory(&self) -> &super::locks::RootedDirectory; + fn writer_identity(&self) -> Result; + fn artifact_use_identity(&self) -> Result; + fn proposed_artifact_incarnation(&self) -> [u8; 32]; + fn is_administrative(&self) -> bool; +} +#[path = "maintenance_legacy_coverage.rs"] +mod legacy_coverage; +pub(crate) use legacy_coverage::{prepare_legacy_coverage, PreparedLegacyCoverage}; + +#[path = "maintenance_automatic_direct.rs"] +mod automatic_direct; +pub(crate) use automatic_direct::{prepare_automatic_direct, PreparedAutomaticDirect}; diff --git a/src/storage/maintenance_projection.rs b/src/storage/maintenance_projection.rs new file mode 100644 index 00000000..b8246d31 --- /dev/null +++ b/src/storage/maintenance_projection.rs @@ -0,0 +1,929 @@ +//! Advisory bounded control projections. Facts retain source stamps and spans; +//! they never grant authority or materialize an ignored JSON map/string. +use super::locks::{FixedControlKind, FixedFileStamp}; +use super::maintenance_budget::{CleanupMemory, MemoryReservation, ReservedBytes, ResourceLimit}; +use super::maintenance_json::{JsonFailure, JsonToken, MaintenanceJsonReader, TokenKind}; +use std::fs::File; +use std::io::{self, Read}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ProjectionFailure { + Resource(ResourceLimit), + Json(JsonFailure), + Io(io::ErrorKind), + Changed, + InvalidObject, + InvalidString, + InvalidNumber, + UnsupportedNumericMode, +} +impl From for ProjectionFailure { + fn from(value: ResourceLimit) -> Self { + Self::Resource(value) + } +} +impl From for ProjectionFailure { + fn from(value: JsonFailure) -> Self { + Self::Json(value) + } +} +impl From for ProjectionFailure { + fn from(value: io::Error) -> Self { + Self::Io(value.kind()) + } +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct StringSpan { + pub(crate) start: u64, + pub(crate) end: u64, + pub(crate) decoded_bytes: u64, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ScalarProjection { + Missing, + Null, + String(StringSpan), + Number { unsigned: Option }, + Other, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ConfigField { + SchemaVersion, + Repository, + Storage, + Database, + Manifest, +} +impl ConfigField { + fn index(self) -> usize { + self as usize + } + fn key(bytes: &[u8]) -> Option { + match bytes { + b"schema_version" => Some(Self::SchemaVersion), + b"repo_root" => Some(Self::Repository), + b"storage_root" => Some(Self::Storage), + b"database_path" => Some(Self::Database), + b"manifest_path" => Some(Self::Manifest), + _ => None, + } + } +} +pub(crate) struct ConfigProjection { + fields: [ScalarProjection; 5], + source: FixedFileStamp, + fingerprint: [u8; 32], + // Includes this fixed retained result, numeric parser and string decoder + // stack scratch; scanner retains its separate floor in the SAME account. + _floor: MemoryReservation, +} +impl ConfigProjection { + pub(crate) fn field(&self, field: ConfigField) -> ScalarProjection { + self.fields[field.index()] + } + pub(crate) fn source(&self) -> &FixedFileStamp { + &self.source + } + pub(crate) fn string( + &self, + file: &File, + field: ConfigField, + memory: &CleanupMemory, + ) -> Result, ProjectionFailure> { + match self.field(field) { + ScalarProjection::Missing | ScalarProjection::Null => Ok(None), + ScalarProjection::String(span) => { + if fingerprint(file, memory)? != self.fingerprint { + return Err(ProjectionFailure::Changed); + } + let decoded = decode_projected_string(file, &self.source, span, memory)?; + if fingerprint(file, memory)? != self.fingerprint { + return Err(ProjectionFailure::Changed); + } + Ok(Some(decoded)) + } + _ => Err(ProjectionFailure::InvalidString), + } + } +} + +// Preparation only: bounded content evidence also detects same-length edits +// while Windows has not yet published updated timestamps to existing handles. +fn fingerprint(file: &File, memory: &CleanupMemory) -> Result<[u8; 32], ProjectionFailure> { + use sha2::{Digest, Sha256}; + let _floor = memory.reserve(8192 + std::mem::size_of::())?; + let mut input = SpanReader::new(file, 0, file.metadata()?.len())?; + let mut hash = Sha256::new(); + let mut buffer = [0; 8192]; + loop { + let read = input.read(&mut buffer)?; + if read == 0 { + break; + } + hash.update(&buffer[..read]); + } + Ok(hash.finalize().into()) +} + +/// Positional reads do not disturb the scanner's read-ahead/cursor. Duplicated +/// Unix FDs would share offsets, so File::try_clone is deliberately not used. +struct SpanReader<'a> { + file: &'a File, + #[cfg(windows)] + independent: File, + offset: u64, + end: u64, +} +impl<'a> SpanReader<'a> { + fn new(file: &'a File, offset: u64, end: u64) -> io::Result { + #[cfg(windows)] + let independent = { + use std::os::windows::io::{AsRawHandle, FromRawHandle}; + use windows_sys::Win32::{ + Foundation::{GENERIC_READ, INVALID_HANDLE_VALUE}, + Storage::FileSystem::{ + ReOpenFile, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, + }, + }; + // A duplicated handle shares its cursor. ReOpenFile creates a new + // file object for this exact inode without resolving a pathname. + let handle = unsafe { + ReOpenFile( + file.as_raw_handle(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + ) + }; + if handle == INVALID_HANDLE_VALUE { + return Err(io::Error::last_os_error()); + } + // SAFETY: the successful reopen returned one uniquely owned handle. + let reopened = unsafe { File::from_raw_handle(handle) }; + if super::locks::file_identity(file).map_err(|_| io::ErrorKind::InvalidData)? + != super::locks::file_identity(&reopened).map_err(|_| io::ErrorKind::InvalidData)? + { + return Err(io::ErrorKind::InvalidData.into()); + } + reopened + }; + Ok(Self { + file, + #[cfg(windows)] + independent, + offset, + end, + }) + } +} +impl Read for SpanReader<'_> { + fn read(&mut self, output: &mut [u8]) -> io::Result { + let len = usize::try_from((self.end - self.offset).min(output.len() as u64)) + .map_err(|_| io::ErrorKind::InvalidInput)?; + if len == 0 { + return Ok(0); + } + #[cfg(unix)] + let read = { + use std::os::unix::fs::FileExt; + self.file.read_at(&mut output[..len], self.offset)? + }; + #[cfg(windows)] + let read = { + use std::os::windows::fs::FileExt; + let _ = self.file; + self.independent + .seek_read(&mut output[..len], self.offset)? + }; + #[cfg(not(any(unix, windows)))] + return Err(io::ErrorKind::Unsupported.into()); + self.offset += read as u64; + Ok(read) + } +} +struct StringReader<'a> { + input: SpanReader<'a>, + buffer: [u8; 512], + cursor: usize, + filled: usize, +} +impl StringReader<'_> { + fn byte(&mut self) -> Result { + if self.cursor == self.filled { + self.filled = self.input.read(&mut self.buffer)?; + self.cursor = 0; + if self.filled == 0 { + return Err(ProjectionFailure::InvalidString); + } + } + let byte = self.buffer[self.cursor]; + self.cursor += 1; + Ok(byte) + } + fn hex4(&mut self) -> Result { + let mut value = 0; + for _ in 0..4 { + let digit = match self.byte()? { + b'0'..=b'9' => self.buffer[self.cursor - 1] - b'0', + b'a'..=b'f' => self.buffer[self.cursor - 1] - b'a' + 10, + b'A'..=b'F' => self.buffer[self.cursor - 1] - b'A' + 10, + _ => return Err(ProjectionFailure::InvalidString), + }; + value = (value << 4) | u32::from(digit); + } + Ok(value) + } +} +fn decode_string_into( + file: &File, + span: StringSpan, + output: &mut [u8], +) -> Result<(), ProjectionFailure> { + if span.end < span.start || span.decoded_bytes != output.len() as u64 { + return Err(ProjectionFailure::InvalidString); + } + let mut reader = StringReader { + input: SpanReader::new(file, span.start, span.end)?, + buffer: [0; 512], + cursor: 0, + filled: 0, + }; + if reader.byte()? != b'"' { + return Err(ProjectionFailure::InvalidString); + } + let mut written = 0usize; + loop { + let byte = reader.byte()?; + if byte == b'"' { + break; + } + let mut encoded = [0; 4]; + let bytes: &[u8] = if byte == b'\\' { + match reader.byte()? { + b'"' => b"\"", + b'\\' => b"\\", + b'/' => b"/", + b'b' => b"\x08", + b'f' => b"\x0c", + b'n' => b"\n", + b'r' => b"\r", + b't' => b"\t", + b'u' => { + let mut point = reader.hex4()?; + if (0xd800..=0xdbff).contains(&point) { + if reader.byte()? != b'\\' || reader.byte()? != b'u' { + return Err(ProjectionFailure::InvalidString); + } + let low = reader.hex4()?; + if !(0xdc00..=0xdfff).contains(&low) { + return Err(ProjectionFailure::InvalidString); + } + point = 0x10000 + ((point - 0xd800) << 10) + (low - 0xdc00); + } + let ch = char::from_u32(point).ok_or(ProjectionFailure::InvalidString)?; + ch.encode_utf8(&mut encoded).as_bytes() + } + _ => return Err(ProjectionFailure::InvalidString), + } + } else { + encoded[0] = byte; + &encoded[..1] + }; + let end = written + .checked_add(bytes.len()) + .ok_or(ProjectionFailure::InvalidString)?; + if end > output.len() { + return Err(ProjectionFailure::InvalidString); + } + output[written..end].copy_from_slice(bytes); + written = end; + } + let consumed = reader.input.offset - (reader.filled - reader.cursor) as u64; + if written != output.len() || consumed != span.end || std::str::from_utf8(output).is_err() { + return Err(ProjectionFailure::InvalidString); + } + Ok(()) +} +/// Reserve exact decoded storage BEFORE allocating; long relevant values return +/// the account's typed limit, never truncation or a global control-file cutoff. +fn decode_projected_string( + file: &File, + source: &FixedFileStamp, + span: StringSpan, + memory: &CleanupMemory, +) -> Result { + let _floor = memory.reserve(1024)?; + source + .recheck(file) + .map_err(|_| ProjectionFailure::Changed)?; + let len = usize::try_from(span.decoded_bytes).map_err(|_| ResourceLimit::ArithmeticOverflow)?; + let mut bytes = memory.reserve(len)?.bytes(len)?; + decode_string_into(file, span, bytes.as_mut_slice())?; + source + .recheck(file) + .map_err(|_| ProjectionFailure::Changed)?; + Ok(bytes) +} +fn span(token: JsonToken, decoded_bytes: u64) -> StringSpan { + StringSpan { + start: token.start, + end: token.end, + decoded_bytes, + } +} +/// Validate ALL number values against the pinned ordinary loader's Number +/// implementation, including ignored/nested/overwritten numeric fields. There +/// is no64-byte producer-number cutoff here. Default-runtime Number parsing is +/// incremental; this fixed same-account floor covers its scalar/error scratch. +fn number(file: &File, token: JsonToken) -> Result { + serde_json::from_reader(SpanReader::new(file, token.start, token.end)?) + .map_err(|_| ProjectionFailure::InvalidNumber) +} +pub(crate) fn project_config( + file: &File, + memory: &CleanupMemory, +) -> Result { + let floor = memory.reserve(std::mem::size_of::() + 2048)?; + // The pinned default-runtime Number parser is incremental. An unexpected + // arbitrary-precision feature would allocate complete unbounded literals; + // detect that mode using only this fixed, already-accounted probe before + // accepting any source-controlled token. It disables advisory proof only. + if serde_json::from_str::("1e9999").is_ok() { + return Err(ProjectionFailure::UnsupportedNumericMode); + } + let source = FixedFileStamp::capture(file, FixedControlKind::File) + .map_err(|_| ProjectionFailure::Changed)?; + let fingerprint = fingerprint(file, memory)?; + let end = file.metadata()?.len(); + let mut scanner = MaintenanceJsonReader::new(SpanReader::new(file, 0, end)?, memory)?; + let mut fields = [ScalarProjection::Missing; 5]; + let mut depth = 0usize; + let mut key = None; + let mut root = false; + while let Some(token) = scanner.next_token()? { + if !root { + if !matches!(token.kind, TokenKind::ObjectStart) { + return Err(ProjectionFailure::InvalidObject); + } + root = true; + } + let numeric = if matches!(token.kind, TokenKind::Number) { + Some(number(file, token)?) + } else { + None + }; + if depth == 1 { + if let TokenKind::String { + key: true, + decoded_len, + .. + } = token.kind + { + key = if decoded_len <= 16 { + let mut bytes = [0; 16]; + let len = decoded_len as usize; + decode_string_into(file, span(token, decoded_len), &mut bytes[..len])?; + ConfigField::key(&bytes[..len]) + } else { + None + }; + } else if let Some(field) = key.take() { + fields[field.index()] = match token.kind { + TokenKind::Null => ScalarProjection::Null, + TokenKind::String { + key: false, + decoded_len, + .. + } => ScalarProjection::String(span(token, decoded_len)), + TokenKind::Number => ScalarProjection::Number { + unsigned: numeric.as_ref().and_then(serde_json::Number::as_u64), + }, + _ => ScalarProjection::Other, + }; + } + } + match token.kind { + TokenKind::ObjectStart | TokenKind::ArrayStart => depth += 1, + TokenKind::ObjectEnd | TokenKind::ArrayEnd => { + depth = depth + .checked_sub(1) + .ok_or(ProjectionFailure::InvalidObject)? + } + _ => (), + } + } + scanner.finish()?; + if !root || depth != 0 { + return Err(ProjectionFailure::InvalidObject); + } + source + .recheck(file) + .map_err(|_| ProjectionFailure::Changed)?; + if self::fingerprint(file, memory)? != fingerprint { + return Err(ProjectionFailure::Changed); + } + Ok(ConfigProjection { + fields, + source, + fingerprint, + _floor: floor, + }) +} + +/// Fully resolved advisory selectors, never authority. Every owned native path +/// is covered by this shared-account envelope before any path allocation. +pub(crate) struct BoundConfiguration { + pub(crate) storage: std::path::PathBuf, + pub(crate) direct: Option, + pub(crate) config_path: std::path::PathBuf, + pub(crate) config_stamp: Option, + pub(crate) repository: super::locks::FileIdentity, + pub(crate) state: super::locks::FileIdentity, + _paths: MemoryReservation, + _projected_paths: Option, +} + +/// Reserve a conservative envelope for actual native path allocations and +/// transient joins. Long-path support does not require charging every short +/// selector as a maximum-length Windows path, repeatedly, in the same account. +pub(super) fn path_charge<'a>( + paths: impl IntoIterator, + copies: usize, + owner: usize, +) -> Result { + paths.into_iter().try_fold( + owner + .checked_add(4096) + .ok_or(ResourceLimit::ArithmeticOverflow)?, + |total, path| { + path.as_os_str() + .as_encoded_bytes() + .len() + .checked_add(512) + .and_then(|bytes| bytes.checked_mul(2)) + .and_then(|bytes| bytes.checked_mul(copies)) + .and_then(|bytes| total.checked_add(bytes)) + .ok_or(ResourceLimit::ArithmeticOverflow) + }, + ) +} + +pub(crate) fn project_binding( + target: &super::inventory::MaintenanceTarget, + memory: &CleanupMemory, +) -> Result { + use super::inventory::{default_direct, real_path, same_physical_path}; + use super::locks::{open_fixed_control, path_identity, FixedControlScratch}; + use std::path::{Component, Path, PathBuf}; + #[cfg(unix)] + const PATH_BYTES: usize = libc::PATH_MAX as usize; + #[cfg(windows)] + const PATH_BYTES: usize = 131072; + #[cfg(not(any(unix, windows)))] + const PATH_BYTES: usize = 4096; + for path in [ + Some(&target.repository_root), + Some(&target.state_root), + target.storage_root.as_ref(), + target.config_path.as_ref(), + target.direct_pair.as_ref().map(|p| &p.database), + target.direct_pair.as_ref().map(|p| &p.manifest), + ] + .into_iter() + .flatten() + { + #[cfg(windows)] + { + use std::os::windows::ffi::OsStrExt; + if path.as_os_str().encode_wide().count() >= 32768 { + return Err(ProjectionFailure::InvalidString); + } + } + #[cfg(not(windows))] + if path.as_os_str().as_encoded_bytes().len() >= PATH_BYTES { + return Err(ProjectionFailure::InvalidString); + } + } + // Native paths have a fixed addressing domain. This envelope covers all + // simultaneously retained selector clones, joins and decoded path images; + // strings themselves are separately reserved exactly before decoding. + let paths = memory.reserve(path_charge( + [ + Some(target.repository_root.as_path()), + Some(target.state_root.as_path()), + target.storage_root.as_deref(), + target.config_path.as_deref(), + target.direct_pair.as_ref().map(|p| p.database.as_path()), + target.direct_pair.as_ref().map(|p| p.manifest.as_path()), + ] + .into_iter() + .flatten(), + 24, + std::mem::size_of::(), + )?)?; + if !target.repository_root.is_absolute() || !real_path(&target.repository_root) { + return Err(ProjectionFailure::InvalidObject); + } + let conventional = target.repository_root.join(".codebaseGraph"); + if target.state_root != conventional || !real_path(&conventional) { + return Err(ProjectionFailure::InvalidObject); + } + let default_config = conventional.join("config.json"); + let config_path = target + .config_path + .as_ref() + .unwrap_or(&default_config) + .clone(); + let mut scratch = + FixedControlScratch::new(memory.reserve(FixedControlScratch::required_bytes())?) + .map_err(|_| ProjectionFailure::Changed)?; + let file = match open_fixed_control(&config_path, FixedControlKind::File, false, &mut scratch) { + Ok(file) => Some(file), + Err(super::locks::FixedControlError::Io(error)) + if error.kind() == io::ErrorKind::NotFound && target.config_path.is_none() => + { + None + } + _ => return Err(ProjectionFailure::Changed), + }; + let mut managed = false; + let mut storage = conventional.join("storage"); + let mut configured = default_direct(target); + let mut config_stamp = None; + let mut projected_paths = None; + if let Some(file) = file { + let projection = project_config(&file, memory)?; + let projected_bytes = [ + ConfigField::Repository, + ConfigField::Storage, + ConfigField::Database, + ConfigField::Manifest, + ] + .into_iter() + .try_fold(0usize, |total, field| { + let bytes = match projection.field(field) { + ScalarProjection::String(span) => usize::try_from(span.decoded_bytes) + .map_err(|_| ResourceLimit::ArithmeticOverflow)?, + _ => 0, + }; + bytes + .checked_mul(48) + .and_then(|bytes| total.checked_add(bytes)) + .ok_or(ResourceLimit::ArithmeticOverflow) + })?; + projected_paths = Some(memory.reserve(projected_bytes)?); + config_stamp = Some(*projection.source()); + let path = |field: ConfigField| -> Result, ProjectionFailure> { + let Some(bytes) = projection.string(&file, field, memory)? else { + return Ok(None); + }; + let string = std::str::from_utf8(bytes.as_slice()) + .map_err(|_| ProjectionFailure::InvalidString)?; + if string.len() >= PATH_BYTES || string.as_bytes().contains(&0) { + return Err(ProjectionFailure::InvalidString); + } + let value = Path::new(string); + let result = if value.is_absolute() { + value.to_owned() + } else { + config_path + .parent() + .ok_or(ProjectionFailure::InvalidString)? + .join(value) + }; + if result.as_os_str().as_encoded_bytes().len() >= PATH_BYTES { + return Err(ProjectionFailure::InvalidString); + } + Ok(Some(result)) + }; + match path(ConfigField::Repository)? { + Some(repository) + if real_path(&repository) + && same_physical_path(&repository, &target.repository_root) => {} + None if config_path == default_config => (), + _ => return Err(ProjectionFailure::InvalidObject), + } + match projection.field(ConfigField::SchemaVersion) { + ScalarProjection::Missing | ScalarProjection::Null => (), + ScalarProjection::Number { unsigned: Some(1) } => (), + ScalarProjection::Number { + unsigned: Some(2 | 3), + } => managed = true, + _ => return Err(ProjectionFailure::InvalidNumber), + } + if managed { + if let Some(value) = path(ConfigField::Storage)? { + if !real_path(&value) { + return Err(ProjectionFailure::Changed); + } + storage = value; + } + } + if let Some(value) = path(ConfigField::Database)? { + configured.database = value; + } + if let Some(value) = path(ConfigField::Manifest)? { + configured.manifest = value; + } + let current = open_fixed_control(&config_path, FixedControlKind::File, false, &mut scratch) + .map_err(|_| ProjectionFailure::Changed)?; + projection + .source() + .recheck(¤t) + .map_err(|_| ProjectionFailure::Changed)?; + } + let selected_storage = target.storage_root.as_ref().unwrap_or(&storage); + if selected_storage != &storage + && !(real_path(&storage) + && real_path(selected_storage) + && same_physical_path(selected_storage, &storage)) + { + return Err(ProjectionFailure::InvalidObject); + } + // Selecting an ancestor of repository/state could collect its own controls. + for protected in [&target.repository_root, &conventional] { + if protected + .ancestors() + .any(|p| same_physical_path(p, selected_storage)) + { + return Err(ProjectionFailure::InvalidObject); + } + } + let direct = target + .direct_pair + .clone() + .or_else(|| (!managed).then_some(configured.clone())); + if let Some(pair) = &direct { + let local = pair.database.starts_with(&conventional) + && pair.manifest.starts_with(&conventional) + && !pair + .database + .components() + .chain(pair.manifest.components()) + .any(|c| matches!(c, Component::ParentDir)); + if (!local && (config_stamp.is_none() || pair != &configured)) + || pair.database == pair.manifest + { + return Err(ProjectionFailure::InvalidObject); + } + } + if super::inventory::bound_roles_overlap_paths(target, selected_storage, direct.as_ref()) { + return Err(ProjectionFailure::InvalidObject); + } + Ok(BoundConfiguration { + storage: selected_storage.clone(), + direct, + config_path, + config_stamp, + repository: path_identity(&target.repository_root) + .map_err(|_| ProjectionFailure::Changed)?, + state: path_identity(&target.state_root).map_err(|_| ProjectionFailure::Changed)?, + _paths: paths, + _projected_paths: projected_paths, + }) +} + +impl BoundConfiguration { + pub(super) fn duplicate(&self, memory: &CleanupMemory) -> Result { + let paths = memory.reserve(self._paths.remaining())?; + let projected_paths = self + ._projected_paths + .as_ref() + .map(|charge| memory.reserve(charge.remaining())) + .transpose()?; + Ok(Self { + storage: self.storage.clone(), + direct: self.direct.clone(), + config_path: self.config_path.clone(), + config_stamp: self.config_stamp, + repository: self.repository, + state: self.state, + _paths: paths, + _projected_paths: projected_paths, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::{self, OpenOptions}; + use std::path::PathBuf; + + #[test] + fn numeric_projection_preserves_the_input_cursor() { + use std::io::{Seek, SeekFrom}; + let fixture = Fixture::new(br#"{"schema_version":3,"ignored":[1,-2,3.5]}"#); + let mut file = fixture.file(); + file.seek(SeekFrom::Start(2)).unwrap(); + let memory = CleanupMemory::new(32768).unwrap(); + let projection = project_config(&file, &memory).unwrap(); + assert_eq!( + projection.field(ConfigField::SchemaVersion), + ScalarProjection::Number { unsigned: Some(3) } + ); + assert_eq!(file.stream_position().unwrap(), 2); + } + + struct Fixture { + path: PathBuf, + } + impl Fixture { + fn new(bytes: &[u8]) -> Self { + let path = std::env::temp_dir().join(format!( + "cleanup-projection-{}", + crate::storage::layout::managed_generation_id() + )); + let mut file = OpenOptions::new() + .create_new(true) + .write(true) + .open(&path) + .unwrap(); + std::io::Write::write_all(&mut file, bytes).unwrap(); + Self { path } + } + fn file(&self) -> File { + File::open(&self.path).unwrap() + } + } + impl Drop for Fixture { + fn drop(&mut self) { + let _ = fs::remove_file(&self.path); + } + } + fn projected( + bytes: &[u8], + ) -> Result<(Fixture, ConfigProjection, CleanupMemory), ProjectionFailure> { + let fixture = Fixture::new(bytes); + let memory = CleanupMemory::new(32768).unwrap(); + let projection = project_config(&fixture.file(), &memory)?; + Ok((fixture, projection, memory)) + } + #[test] + fn dc_projection_matches_current_config_scalars_duplicates_and_escaped_keys() { + for bytes in [ + br#"{}"#.as_slice(), + br#"{"schema_version":1,"repo_root":null,"database_path":"raw/../db.ldb"}"#, + br#"{"schema_version":2,"schema_version":3,"repo_root":"old","repo_root":"new"}"#, + br#"{"schema_version":2.0,"storage_root":{},"manifest_path":false}"#, + br#"{"schema_version":-0,"repo_root":["x"],"ignored":{"schema_version":3}}"#, + br#"{"schema\u005fversion":3,"database\u005fpath":"a\\b\"c"}"#, + ] { + let ordinary: serde_json::Value = serde_json::from_slice(bytes).unwrap(); + let (fixture, projection, memory) = projected(bytes).unwrap(); + for (field, key) in [ + (ConfigField::SchemaVersion, "schema_version"), + (ConfigField::Repository, "repo_root"), + (ConfigField::Storage, "storage_root"), + (ConfigField::Database, "database_path"), + (ConfigField::Manifest, "manifest_path"), + ] { + let expected = match ordinary.get(key) { + None => ScalarProjection::Missing, + Some(serde_json::Value::Null) => ScalarProjection::Null, + Some(serde_json::Value::Number(n)) => ScalarProjection::Number { + unsigned: n.as_u64(), + }, + Some(serde_json::Value::String(s)) => { + let actual = projection + .string(&fixture.file(), field, &memory) + .unwrap() + .unwrap(); + assert_eq!(actual.as_slice(), s.as_bytes()); + assert!(matches!( + projection.field(field), + ScalarProjection::String(_) + )); + continue; + } + _ => ScalarProjection::Other, + }; + assert_eq!(projection.field(field), expected, "{key}"); + } + } + } + #[test] + fn dc_projection_decodes_utf8_surrogates_and_all_json_escapes_exactly() { + let bytes = br#"{"repo_root":"\uD83D\uDE00/\u00e9/\b\f\n\r\t\/\\\""}"#; + let ordinary: serde_json::Value = serde_json::from_slice(bytes).unwrap(); + let (fixture, projection, memory) = projected(bytes).unwrap(); + let decoded = projection + .string(&fixture.file(), ConfigField::Repository, &memory) + .unwrap() + .unwrap(); + assert_eq!( + decoded.as_slice(), + ordinary["repo_root"].as_str().unwrap().as_bytes() + ); + assert_eq!( + std::str::from_utf8(decoded.as_slice()) + .unwrap() + .chars() + .next(), + Some('😀') + ); + } + #[test] + fn dc_projection_streams_large_ignored_keys_values_and_limits_only_decoded_storage() { + let mut bytes = b"{\"".to_vec(); + bytes.extend(std::iter::repeat_n(b'x', 100_000)); + bytes.extend_from_slice(b"\":\""); + bytes.extend(std::iter::repeat_n(b'y', 200_000)); + bytes.extend_from_slice(b"\",\"repo_root\":\""); + bytes.extend(std::iter::repeat_n(b'z', 100_000)); + bytes.extend_from_slice(b"\",\"schema_version\":3}"); + let (fixture, projection, memory) = projected(&bytes).unwrap(); + assert_eq!( + projection.field(ConfigField::SchemaVersion), + ScalarProjection::Number { unsigned: Some(3) } + ); + let before = memory.used(); + assert!(memory.peak() <= memory.limit()); + assert!(matches!( + projection.string(&fixture.file(), ConfigField::Repository, &memory), + Err(ProjectionFailure::Resource(ResourceLimit::CeilingExceeded)) + )); + assert_eq!(memory.used(), before); + drop(projection); + assert!(memory.used() < before); + } + #[test] + fn dc_projection_validates_long_ignored_numbers_like_ordinary_loader_without_cutoff() { + let values = [ + "0".into(), + "-0".into(), + "2.0".into(), + "2e0".into(), + format!("0.{}", "0".repeat(10_000)), + format!("1e{}", "0".repeat(10_000)), + "1e309".into(), + "-1e99999999999999999999".into(), + "9".repeat(10_000), + ]; + for value in values { + let bytes = format!("{{\"ignored\":[{value}],\"schema_version\":2}}"); + let expected = serde_json::from_str::(&bytes).is_ok(); + assert_eq!( + projected(bytes.as_bytes()).is_ok(), + expected, + "numeric prefix {:?}", + &value[..value.len().min(20)] + ); + } + } + #[test] + fn dc_projection_requires_complete_object_and_preserves_loader_depth_errors() { + for bytes in [ + b"null".as_slice(), + b"[]", + b"{} {}", + br#"{"repo_root":"\ud800"}"#, + br#"{"x":01}"#, + br#"{"x":true,}"#, + ] { + assert!(projected(bytes).is_err()); + } + let bytes = format!("{{\"ignored\":{}0{}}}", "[".repeat(140), "]".repeat(140)); + assert!(serde_json::from_str::(&bytes).is_err()); + assert!(projected(bytes.as_bytes()).is_err()); + } + #[test] + fn projection_rejects_changed_content_when_the_metadata_stamp_matches() { + let (fixture, mut projection, memory) = + projected(br#"{"repo_root":"old","ignored":"one"}"#).unwrap(); + fs::write(&fixture.path, br#"{"repo_root":"old","ignored":"two"}"#).unwrap(); + let mut file = fixture.file(); + use std::io::{Seek, SeekFrom}; + file.seek(SeekFrom::Start(2)).unwrap(); + // Isolate the content check from timestamp resolution and publication. + projection.source = FixedFileStamp::capture(&file, FixedControlKind::File).unwrap(); + projection.source.recheck(&file).unwrap(); + let before = memory.used(); + assert!(matches!( + projection.string(&file, ConfigField::Repository, &memory), + Err(ProjectionFailure::Changed) + )); + assert_eq!(memory.used(), before); + assert_eq!(file.stream_position().unwrap(), 2); + } + #[test] + fn dc_projection_rechecks_current_source_before_decoding_and_does_not_move_file_cursor() { + let (fixture, projection, memory) = + projected(br#"{"repo_root":"old","schema_version":3}"#).unwrap(); + let mut file = fixture.file(); + use std::io::{Seek, SeekFrom}; + file.seek(SeekFrom::Start(2)).unwrap(); + let value = projection + .string(&file, ConfigField::Repository, &memory) + .unwrap() + .unwrap(); + assert_eq!(value.as_slice(), b"old"); + assert_eq!(file.stream_position().unwrap(), 2); + drop(value); + fs::write(&fixture.path, br#"{"repo_root":"new","schema_version":3}"#).unwrap(); + assert!(matches!( + projection.string(&file, ConfigField::Repository, &memory), + Err(ProjectionFailure::Changed) + )); + } +} diff --git a/src/storage/maintenance_references.rs b/src/storage/maintenance_references.rs new file mode 100644 index 00000000..6d755f51 --- /dev/null +++ b/src/storage/maintenance_references.rs @@ -0,0 +1,1153 @@ +//! Bounded current-manifest reachability facts. No ownership or deletion authority. +use super::inventory::MaintenanceTarget; +use super::locks::{ + open_fixed_control, FixedControlKind, FixedControlScratch, FixedFileStamp, RootedDirectory, +}; +use super::maintenance_budget::{CleanupMemory, ResourceLimit}; +use super::maintenance_json::{JsonFailure, JsonToken, MaintenanceJsonReader, TokenKind}; +use super::maintenance_ownership::{ReferenceDomain, ReferenceEpoch}; +use super::maintenance_validation::{compare_decoded_spans, ByteSource, Span, ValidationIssue}; +use std::cmp::Ordering; +use std::fs::File; +use std::io::{self, Read}; +use std::path::Path; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ReferenceIssue { + ResourceLimit(ResourceLimit), + Io(io::ErrorKind), + Unknown, + Stale, + Cancelled, +} +impl From for ReferenceIssue { + fn from(e: ResourceLimit) -> Self { + Self::ResourceLimit(e) + } +} +impl From for ReferenceIssue { + fn from(e: io::Error) -> Self { + Self::Io(e.kind()) + } +} +impl From for ReferenceIssue { + fn from(e: JsonFailure) -> Self { + match e { + JsonFailure::Resource(r) => Self::ResourceLimit(r), + JsonFailure::Io(k) => Self::Io(k), + _ => Self::Unknown, + } + } +} +impl From for ReferenceIssue { + fn from(e: ValidationIssue) -> Self { + match e { + ValidationIssue::Resource(r) => Self::ResourceLimit(r), + ValidationIssue::Io(k) => Self::Io(k), + _ => Self::Unknown, + } + } +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct ArtifactReferences { + pub(crate) referenced: bool, + pub(crate) complete: bool, +} + +struct Source<'a> { + file: &'a File, + len: u64, +} +impl ByteSource for Source<'_> { + fn len(&self) -> u64 { + self.len + } + fn read_at(&self, offset: u64, out: &mut [u8]) -> Result { + #[cfg(unix)] + { + use std::os::unix::fs::FileExt; + self.file + .read_at(out, offset) + .map_err(|e| ValidationIssue::Io(e.kind())) + } + #[cfg(windows)] + { + use std::os::windows::fs::FileExt; + self.file + .seek_read(out, offset) + .map_err(|e| ValidationIssue::Io(e.kind())) + } + #[cfg(not(any(unix, windows)))] + { + let _ = (offset, out); + Err(ValidationIssue::Schema) + } + } +} +struct Input<'a> { + source: &'a Source<'a>, + offset: u64, + end: u64, +} +impl Read for Input<'_> { + fn read(&mut self, out: &mut [u8]) -> io::Result { + let n = usize::try_from((self.end - self.offset).min(out.len() as u64)) + .map_err(|_| io::ErrorKind::InvalidData)?; + let n = self + .source + .read_at(self.offset, &mut out[..n]) + .map_err(|_| io::ErrorKind::InvalidData)?; + self.offset += n as u64; + Ok(n) + } +} +/// Exact decoded-token spans in one fully validated, unchanged manifest. +/// The caller retains/rechecks the actual bound manifest FD/name and original E. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct ManifestReferenceContext { + pub(crate) stamp: FixedFileStamp, + pub(crate) path: Span, + pub(crate) content_hash: Span, + pub(crate) language: Span, + pub(crate) partition_id: Span, +} +struct Parser<'a> { + source: &'a Source<'a>, + reader: MaintenanceJsonReader>, + memory: &'a CleanupMemory, + cancel: &'a dyn Fn() -> bool, + key: [u8; 66], + hit: bool, + context: Option<[Span; 4]>, +} +fn span(t: JsonToken) -> Span { + Span { + start: t.start, + end: t.end, + } +} +impl Parser<'_> { + fn token(&mut self) -> Result { + if (self.cancel)() { + return Err(ReferenceIssue::Cancelled); + } + let token = self.reader.next_token()?.ok_or(ReferenceIssue::Unknown)?; + if matches!(token.kind, TokenKind::Number) { + if serde_json::from_str::("1e9999").is_ok() { + return Err(ReferenceIssue::Unknown); + } + let _: serde_json::Number = serde_json::from_reader(Input { + source: self.source, + offset: token.start, + end: token.end, + }) + .map_err(|_| ReferenceIssue::Unknown)?; + } + Ok(token) + } + fn equal(&self, t: JsonToken, literal: &[u8]) -> Result { + Ok(compare_decoded_spans( + self.source, + span(t), + literal, + Span { + start: 0, + end: literal.len() as u64, + }, + self.memory, + )? == Ordering::Equal) + } + fn field(&self, t: JsonToken, fields: &[&[u8]]) -> Result, ReferenceIssue> { + if !matches!(t.kind, TokenKind::String { key: true, .. }) { + return Err(ReferenceIssue::Unknown); + } + for (i, f) in fields.iter().enumerate() { + if self.equal(t, f)? { + return Ok(Some(i)); + } + } + Ok(None) + } + fn string(&self, t: JsonToken) -> Result<(), ReferenceIssue> { + if matches!(t.kind, TokenKind::String { key: false, .. }) { + Ok(()) + } else { + Err(ReferenceIssue::Unknown) + } + } + fn unsigned(&self, t: JsonToken) -> Result<(), ReferenceIssue> { + if !matches!(t.kind, TokenKind::Number) + || serde_json::from_str::("1e9999").is_ok() + { + return Err(ReferenceIssue::Unknown); + } + let n: serde_json::Number = serde_json::from_reader(Input { + source: self.source, + offset: t.start, + end: t.end, + }) + .map_err(|_| ReferenceIssue::Unknown)?; + n.as_u64().map(|_| ()).ok_or(ReferenceIssue::Unknown) + } + fn skip(&mut self, t: JsonToken) -> Result<(), ReferenceIssue> { + let mut depth = usize::from(matches!( + t.kind, + TokenKind::ObjectStart | TokenKind::ArrayStart + )); + while depth > 0 { + match self.token()?.kind { + TokenKind::ObjectStart | TokenKind::ArrayStart => depth += 1, + TokenKind::ObjectEnd | TokenKind::ArrayEnd => depth -= 1, + _ => {} + } + } + Ok(()) + } + fn strings(&mut self, t: JsonToken, map: bool) -> Result<(), ReferenceIssue> { + if t.kind + != if map { + TokenKind::ObjectStart + } else { + TokenKind::ArrayStart + } + { + return Err(ReferenceIssue::Unknown); + } + let mut previous = None; + loop { + let t = self.token()?; + if t.kind + == if map { + TokenKind::ObjectEnd + } else { + TokenKind::ArrayEnd + } + { + break; + } + if map { + self.ordered(t, &mut previous)?; + let v = self.token()?; + self.string(v)?; + } else { + self.string(t)?; + } + } + Ok(()) + } + fn ordered(&self, t: JsonToken, previous: &mut Option) -> Result<(), ReferenceIssue> { + if !matches!(t.kind, TokenKind::String { key: true, .. }) { + return Err(ReferenceIssue::Unknown); + } + if let Some(p) = *previous { + if compare_decoded_spans(self.source, p, self.source, span(t), self.memory)? + != Ordering::Less + { + return Err(ReferenceIssue::Unknown); + } + } + *previous = Some(span(t)); + Ok(()) + } + fn entry(&mut self, t: JsonToken) -> Result<(), ReferenceIssue> { + if t.kind != TokenKind::ObjectStart { + return Err(ReferenceIssue::Unknown); + } + let fields: &[&[u8]] = &[ + br#""path""#, + br#""content_hash""#, + br#""language""#, + br#""partition_id""#, + br#""artifact_key""#, + br#""node_ids""#, + br#""edge_ids""#, + br#""node_types""#, + br#""edge_types""#, + br#""node_count""#, + br#""edge_count""#, + br#""materialized_at""#, + ]; + let mut seen = 0u16; + let mut context = [Span { start: 0, end: 0 }; 4]; + let mut entry_hit = false; + loop { + let t = self.token()?; + if t.kind == TokenKind::ObjectEnd { + break; + } + let f = self.field(t, fields)?; + let v = self.token()?; + if let Some(i) = f { + if seen & (1 << i) != 0 { + return Err(ReferenceIssue::Unknown); + } + seen |= 1 << i; + match i { + 0..=3 => { + self.string(v)?; + context[i] = span(v); + } + 11 => self.string(v)?, + 4 => { + if v.kind != TokenKind::Null { + self.string(v)?; + if self.equal(v, &self.key)? { + self.hit = true; + entry_hit = true + } + } + } + 5 | 6 => self.strings(v, false)?, + 7 | 8 => self.strings(v, true)?, + 9 | 10 => self.unsigned(v)?, + _ => unreachable!(), + } + } else { + self.skip(v)? + } + } + if seen & 15 != 15 { + return Err(ReferenceIssue::Unknown); + } + if entry_hit { + if let Some(previous) = self.context { + for (left, right) in previous.into_iter().zip(context) { + if compare_decoded_spans(self.source, left, self.source, right, self.memory)? + != Ordering::Equal + { + return Err(ReferenceIssue::Unknown); + } + } + } + self.context = Some(context); + } + Ok(()) + } + fn files(&mut self, t: JsonToken) -> Result<(), ReferenceIssue> { + // Historical array manifests deliberately remain unknown. + if t.kind != TokenKind::ObjectStart { + return Err(ReferenceIssue::Unknown); + } + let mut previous = None; + loop { + let k = self.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + self.ordered(k, &mut previous)?; + let v = self.token()?; + self.entry(v)?; + } + Ok(()) + } + fn search(&mut self, t: JsonToken) -> Result<(), ReferenceIssue> { + if t.kind == TokenKind::Null { + return Ok(()); + } + if t.kind != TokenKind::ObjectStart { + return Err(ReferenceIssue::Unknown); + } + let mut seen = 0u8; + let fields: &[&[u8]] = &[ + br#""backend""#, + br#""format_version""#, + br#""files""#, + br#""document_count""#, + br#""term_count""#, + br#""total_tokens""#, + ]; + loop { + let k = self.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let f = self.field(k, fields)?; + let v = self.token()?; + if let Some(i) = f { + if seen & (1 << i) != 0 { + return Err(ReferenceIssue::Unknown); + } + seen |= 1 << i; + match i { + 0 => self.string(v)?, + 2 => self.strings(v, true)?, + _ => self.unsigned(v)?, + } + } else { + self.skip(v)? + } + } + if seen & 3 != 3 { + return Err(ReferenceIssue::Unknown); + } + Ok(()) + } + fn manifest(&mut self) -> Result { + if self.token()?.kind != TokenKind::ObjectStart { + return Err(ReferenceIssue::Unknown); + } + let mut seen = 0u8; + let fields: &[&[u8]] = &[ + br#""schema_version""#, + br#""ontology""#, + br#""parser_version""#, + br#""graph_build_digest""#, + br#""search_backend""#, + br#""files""#, + ]; + loop { + let k = self.token()?; + if k.kind == TokenKind::ObjectEnd { + break; + } + let f = self.field(k, fields)?; + let v = self.token()?; + if let Some(i) = f { + if seen & (1 << i) != 0 { + return Err(ReferenceIssue::Unknown); + } + seen |= 1 << i; + match i { + 0 => self.unsigned(v)?, + 1 | 2 => self.string(v)?, + 3 => { + if v.kind != TokenKind::Null { + self.string(v)? + } + } + 4 => self.search(v)?, + 5 => self.files(v)?, + _ => unreachable!(), + } + } else { + self.skip(v)? + } + } + if seen & 7 != 7 || self.reader.next_token()?.is_some() { + return Err(ReferenceIssue::Unknown); + } + Ok(self.hit) + } +} +pub(crate) fn manifest_reference_context( + file: &File, + key: [u8; 32], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result, ReferenceIssue> { + let _floor = memory.reserve(std::mem::size_of::>() + 4096)?; + let stamp = FixedFileStamp::capture(file, FixedControlKind::File) + .map_err(|_| ReferenceIssue::Unknown)?; + let source = Source { + file, + len: stamp.len(), + }; + let mut literal = [b'"'; 66]; + let hex = b"0123456789abcdef"; + for (i, b) in key.iter().enumerate() { + literal[1 + 2 * i] = hex[(b >> 4) as usize]; + literal[2 + 2 * i] = hex[(b & 15) as usize]; + } + let reader = MaintenanceJsonReader::new( + Input { + source: &source, + offset: 0, + end: source.len, + }, + memory, + )?; + let mut parser = Parser { + source: &source, + reader, + memory, + cancel, + key: literal, + hit: false, + context: None, + }; + parser.manifest()?; + stamp.recheck(file).map_err(|_| ReferenceIssue::Stale)?; + Ok(parser.context.map(|context| ManifestReferenceContext { + stamp, + path: context[0], + content_hash: context[1], + language: context[2], + partition_id: context[3], + })) +} +#[cfg(test)] +fn manifest( + file: &File, + key: [u8; 32], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + Ok(manifest_reference_context(file, key, memory, cancel)?.is_some()) +} +fn control(error: super::locks::FixedControlError) -> ReferenceIssue { + match error { + super::locks::FixedControlError::Resource(r) => ReferenceIssue::ResourceLimit(r), + super::locks::FixedControlError::Io(e) => ReferenceIssue::Io(e.kind()), + _ => ReferenceIssue::Stale, + } +} +fn directory( + path: &Path, + scratch: &mut FixedControlScratch, +) -> Result { + let file = + open_fixed_control(path, FixedControlKind::Directory, false, scratch).map_err(control)?; + let stamp = FixedFileStamp::capture(&file, FixedControlKind::Directory).map_err(control)?; + RootedDirectory::open_fixed(path, stamp.identity(), false, scratch).map_err(control) +} +fn absent(path: &Path, scratch: &mut FixedControlScratch) -> Result { + match open_fixed_control(path, FixedControlKind::File, false, scratch) { + Ok(_) => Ok(false), + Err(super::locks::FixedControlError::Io(e)) if e.kind() == io::ErrorKind::NotFound => { + Ok(true) + } + Err(_) => Err(ReferenceIssue::Unknown), + } +} +/// Observe every relevant current manifest under the ORIGINAL expected epoch. +/// All generations are retained as references (including leased and recoverable +/// predecessors). Runs or Direct publication evidence defer absence until their +/// real owner reconciles them. Before/after exclusive activity observations +/// reject a producer already live at capture as well as a producer starting +/// during this scan; no lock survives either observation. +pub(crate) fn prepare_artifact_references( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + key: [u8; 32], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, +) -> Result { + prepare_artifact_references_with_context( + target, + domain, + expected, + key, + memory, + cancel, + &mut |_, _| Ok(()), + ) +} +/// The visitor borrows a dedicated, bound source FD only while its complete +/// context is observed. No context is usable unless the returned complete flag +/// and ORIGINAL-E final seal both succeed. No FD/catalog union is retained. +pub(crate) fn prepare_artifact_references_with_context( + target: &MaintenanceTarget, + domain: &ReferenceDomain, + expected: &ReferenceEpoch, + key: [u8; 32], + memory: &CleanupMemory, + cancel: &dyn Fn() -> bool, + visit: &mut dyn FnMut(&File, ManifestReferenceContext) -> Result<(), ReferenceIssue>, +) -> Result { + if cancel() { + return Err(ReferenceIssue::Cancelled); + } + let mut charge = 32768usize; + for path in [ + Some(target.repository_root.as_path()), + Some(target.state_root.as_path()), + target.storage_root.as_deref(), + target.direct_pair.as_ref().map(|p| p.database.as_path()), + target.direct_pair.as_ref().map(|p| p.manifest.as_path()), + Some(domain.artifact_root()), + ] + .into_iter() + .flatten() + { + charge = charge + .checked_add( + path.as_os_str() + .as_encoded_bytes() + .len() + .checked_mul(32) + .ok_or(ResourceLimit::ArithmeticOverflow)?, + ) + .ok_or(ResourceLimit::ArithmeticOverflow)?; + } + let _floor = memory.reserve(charge)?; + let mut scratch = + FixedControlScratch::new(memory.reserve(FixedControlScratch::required_bytes())?) + .map_err(control)?; + if !super::maintenance_ownership::expected_reference_current(domain, expected, memory) + .map_err(control)? + { + return Err(ReferenceIssue::Stale); + } + let mut result = ArtifactReferences { + referenced: false, + complete: true, + }; + let managed = target + .storage_root + .as_ref() + .filter(|p| p.join("artifacts") == domain.artifact_root()); + if let Some(storage) = managed { + let generations_path = storage.join("generations"); + let runs_path = storage.join("runs"); + let generations_source = open_fixed_control( + &generations_path, + FixedControlKind::Directory, + false, + &mut scratch, + ) + .map_err(control)?; + let generations_stamp = + FixedFileStamp::capture(&generations_source, FixedControlKind::Directory) + .map_err(control)?; + let runs_source = + open_fixed_control(&runs_path, FixedControlKind::Directory, false, &mut scratch) + .map_err(control)?; + let runs_stamp = + FixedFileStamp::capture(&runs_source, FixedControlKind::Directory).map_err(control)?; + let generations = directory(&generations_path, &mut scratch)?; + let runs = directory(&runs_path, &mut scratch)?; + let mut buffer = [0u8; 4096]; + let mut failure = None; + generations + .visit_entries(&mut buffer, &mut |name| { + if cancel() { + failure = Some(ReferenceIssue::Cancelled); + return Err(io::ErrorKind::Interrupted.into()); + } + let observed = (|| { + // Unrecognized generation leaves are retained, never interpreted as empty. + if !name.starts_with(b"gen-") { + return Err(ReferenceIssue::Unknown); + } + let generation = generations.open_directory_readonly(name)?; + let file = generation.open_readonly_file(b"manifest.json")?; + let stamp = + FixedFileStamp::capture(&file, FixedControlKind::File).map_err(control)?; + let context = manifest_reference_context(&file, key, memory, cancel)?; + if let Some(context) = context { + visit(&file, context)?; + } + let hit = context.is_some(); + let current = generation.open_readonly_file(b"manifest.json")?; + stamp.recheck(¤t).map_err(control)?; + if generations.open_directory_readonly(name)?.identity() + != generation.identity() + { + return Err(ReferenceIssue::Stale); + } + Ok(hit) + })(); + match observed { + Ok(hit) => result.referenced |= hit, + Err(ReferenceIssue::Cancelled) => { + failure = Some(ReferenceIssue::Cancelled); + return Err(io::ErrorKind::Interrupted.into()); + } + Err(ReferenceIssue::ResourceLimit(r)) => { + failure = Some(ReferenceIssue::ResourceLimit(r)); + return Err(io::ErrorKind::OutOfMemory.into()); + } + Err(_) => result.complete = false, + }; + Ok(()) + }) + .map_err(|e| failure.unwrap_or_else(|| e.into()))?; + runs.visit_entries(&mut buffer, &mut |_| { + if cancel() { + return Err(io::ErrorKind::Interrupted.into()); + } + result.complete = false; + Ok(()) + }) + .map_err(|e| { + if cancel() { + ReferenceIssue::Cancelled + } else { + e.into() + } + })?; + for (path, source, stamp) in [ + (&generations_path, &generations_source, generations_stamp), + (&runs_path, &runs_source, runs_stamp), + ] { + stamp.recheck(source).map_err(control)?; + let current = + open_fixed_control(path, FixedControlKind::Directory, false, &mut scratch) + .map_err(control)?; + stamp.recheck(¤t).map_err(control)?; + } + } else { + let pair = target + .direct_pair + .clone() + .unwrap_or_else(|| super::inventory::default_direct(target)); + let layout = super::layout::DirectLayout::new(&pair.database, &pair.manifest); + if layout.artifact_root_path() != domain.artifact_root() { + return Err(ReferenceIssue::Unknown); + } + // Recovery/candidate/shadow manifests can carry keys absent from current. + // Do not infer their irrelevance or silently reduce them to an empty set. + let mut shadow = pair.manifest.as_os_str().to_os_string(); + shadow.push(".shadow"); + for path in [ + layout.journal_path(), + layout.manifest_candidate_path(), + std::path::PathBuf::from(shadow), + ] { + if !absent(&path, &mut scratch)? { + result.complete = false; + } + } + let file = open_fixed_control(&pair.manifest, FixedControlKind::File, false, &mut scratch) + .map_err(control)?; + let source_stamp = + FixedFileStamp::capture(&file, FixedControlKind::File).map_err(control)?; + match manifest_reference_context(&file, key, memory, cancel) { + Ok(context) => { + if let Some(context) = context { + visit(&file, context)?; + } + result.referenced = context.is_some(); + } + Err(ReferenceIssue::Unknown) => result.complete = false, + Err(e) => return Err(e), + } + let current = + open_fixed_control(&pair.manifest, FixedControlKind::File, false, &mut scratch) + .map_err(control)?; + source_stamp.recheck(¤t).map_err(control)?; + } + if cancel() { + return Err(ReferenceIssue::Cancelled); + } + if !super::maintenance_ownership::expected_reference_current(domain, expected, memory) + .map_err(control)? + { + return Err(ReferenceIssue::Stale); + } + Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::protocol::{ManifestEntry, NativeManifest}; + use std::collections::BTreeMap; + struct TestRoot(std::path::PathBuf); + impl TestRoot { + fn new() -> Self { + let path = std::env::temp_dir().join(format!( + "cleanup-refs-{}", + super::super::layout::managed_generation_id() + )); + std::fs::create_dir(&path).unwrap(); + Self(std::fs::canonicalize(path).unwrap()) + } + } + impl Drop for TestRoot { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.0); + } + } + fn producer(key: Option) -> Vec { + serde_json::to_vec(&NativeManifest { + schema_version: 2, + ontology: "code".into(), + parser_version: "native".into(), + graph_build_digest: None, + search_backend: None, + files: BTreeMap::from([( + "src/a.rs".into(), + ManifestEntry { + path: "src/a.rs".into(), + content_hash: "hash".into(), + language: "rust".into(), + partition_id: "part".into(), + artifact_key: key, + node_ids: vec![], + edge_ids: vec![], + node_types: BTreeMap::new(), + edge_types: BTreeMap::new(), + node_count: 0, + edge_count: 0, + materialized_at: String::new(), + }, + )]), + }) + .unwrap() + } + fn read( + bytes: &[u8], + key: [u8; 32], + limit: usize, + cancel: &dyn Fn() -> bool, + ) -> Result { + let temp = TestRoot::new(); + let path = temp.0.join("manifest.json"); + std::fs::write(&path, bytes).unwrap(); + let file = File::open(path).unwrap(); + let memory = CleanupMemory::new(limit).unwrap(); + let before = memory.used(); + let result = manifest(&file, key, &memory, cancel); + assert_eq!(memory.used(), before); + assert!(memory.peak() <= limit); + result + } + #[test] + fn dc_references_actual_current_manifest_exact_key() { + let bytes = producer(Some("ab".repeat(32))); + assert_eq!(read(&bytes, [0xab; 32], 128 * 1024, &|| false), Ok(true)); + assert_eq!(read(&bytes, [0xac; 32], 128 * 1024, &|| false), Ok(false)); + assert_eq!( + read(&producer(None), [0xab; 32], 128 * 1024, &|| false), + Ok(false) + ); + } + #[test] + fn dc_references_escaped_keys_match_decoded_exact_bytes() { + let bytes = producer(Some("ab".repeat(32))); + let text = String::from_utf8(bytes) + .unwrap() + .replace("\"artifact_key\"", "\"artifact_\\u006bey\"") + .replace(&"ab".repeat(32), &"\\u0061b".repeat(32)); + assert_eq!( + read(text.as_bytes(), [0xab; 32], 128 * 1024, &|| false), + Ok(true) + ); + } + #[test] + fn dc_references_duplicate_schema_fields_are_unknown() { + let text = String::from_utf8(producer(Some("ab".repeat(32)))).unwrap(); + let text = text.replace( + "\"artifact_key\":", + "\"artifact_key\":null,\"artifact_key\":", + ); + assert_eq!( + read(text.as_bytes(), [0xab; 32], 128 * 1024, &|| false), + Err(ReferenceIssue::Unknown) + ); + } + #[test] + fn dc_references_duplicate_map_keys_fail_closed_after_last_value() { + let text = String::from_utf8(producer(None)).unwrap(); + let value: serde_json::Value = serde_json::from_str(&text).unwrap(); + let entry = serde_json::to_string(&value["files"]["src/a.rs"]).unwrap(); + let text = format!( + r#"{{"schema_version":2,"ontology":"code","parser_version":"native","files":{{"src/a.rs":{entry},"src/\u0061.rs":{entry}}}}}"# + ); + assert_eq!( + read(text.as_bytes(), [0; 32], 128 * 1024, &|| false), + Err(ReferenceIssue::Unknown) + ); + } + #[test] + fn dc_references_malformed_tail_and_required_entry_fields_fail_closed() { + let bytes = producer(Some("ab".repeat(32))); + let mut malformed = bytes.clone(); + malformed.extend_from_slice(b"false"); + assert_eq!( + read(&malformed, [0xab; 32], 128 * 1024, &|| false), + Err(ReferenceIssue::Unknown) + ); + let text = String::from_utf8(bytes) + .unwrap() + .replace("\"language\":\"rust\",", ""); + assert_eq!( + read(text.as_bytes(), [0xab; 32], 128 * 1024, &|| false), + Err(ReferenceIssue::Unknown) + ); + } + #[test] + fn dc_references_historical_arrays_and_invalid_map_values_are_unknown() { + for files in ["[]", "null", "{\"a\":false}"] { + let text = format!( + r#"{{"schema_version":2,"ontology":"code","parser_version":"native","files":{files}}}"# + ); + assert_eq!( + read(text.as_bytes(), [0; 32], 128 * 1024, &|| false), + Err(ReferenceIssue::Unknown) + ); + } + } + #[test] + fn dc_references_large_irrelevant_string_streams_below_cap() { + let text = String::from_utf8(producer(None)).unwrap().replacen( + '{', + &format!("{{\"extension\":\"{}\",", "x".repeat(1024 * 1024)), + 1, + ); + assert_eq!( + read(text.as_bytes(), [0; 32], 32 * 1024, &|| false), + Ok(false) + ); + } + #[test] + fn dc_references_memory_and_cancellation_are_typed() { + assert!(matches!( + read(&producer(None), [0; 32], 1024, &|| false), + Err(ReferenceIssue::ResourceLimit(_)) + )); + assert_eq!( + read(&producer(None), [0; 32], 128 * 1024, &|| true), + Err(ReferenceIssue::Cancelled) + ); + } + #[test] + fn dc_references_short_prefix_and_long_key_never_match() { + for key in ["ab".repeat(31), "ab".repeat(33)] { + assert_eq!( + read(&producer(Some(key)), [0xab; 32], 128 * 1024, &|| false), + Ok(false) + ); + } + } + struct ManagedFixture { + _root: TestRoot, + target: MaintenanceTarget, + layout: super::super::layout::ManagedLayout, + domain: ReferenceDomain, + } + impl ManagedFixture { + fn new() -> Self { + use super::super::locks::{open_locked, LockMode}; + use super::super::maintenance_ownership::initialize_fresh_reference_domain; + let root = TestRoot::new(); + let layout = super::super::layout::ManagedLayout::new(root.0.join("storage")); + let fresh = super::super::layout::create_fresh_reference_root(layout.storage_root()) + .unwrap() + .unwrap(); + layout.ensure_roots().unwrap(); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::managed(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + drop(writer); + let mutation = + super::super::maintenance_ownership::ReferenceMutation::begin_in_domain(&domain) + .unwrap(); + let generation = layout.generations_root().join("gen-current"); + std::fs::create_dir(&generation).unwrap(); + std::fs::write( + generation.join("manifest.json"), + producer(Some("ab".repeat(32))), + ) + .unwrap(); + drop(mutation); + let target = MaintenanceTarget { + repository_root: root.0.clone(), + state_root: root.0.clone(), + storage_root: Some(layout.storage_root().to_owned()), + direct_pair: None, + config_path: None, + }; + Self { + _root: root, + target, + layout, + domain, + } + } + fn observe(&self) -> ReferenceEpoch { + match super::super::maintenance_ownership::observe_reference_epoch(&self.domain) + .unwrap() + { + super::super::maintenance_ownership::EpochObservation::Verified(e) => e, + other => panic!("{other:?}"), + } + } + } + #[test] + fn dc_references_managed_current_and_predecessor_manifests_are_covered() { + let f = ManagedFixture::new(); + let memory = CleanupMemory::new(256 * 1024).unwrap(); + let e = f.observe(); + assert_eq!( + prepare_artifact_references(&f.target, &f.domain, &e, [0xab; 32], &memory, &|| false) + .unwrap(), + ArtifactReferences { + referenced: true, + complete: true + } + ); + assert_eq!( + prepare_artifact_references(&f.target, &f.domain, &e, [0xac; 32], &memory, &|| false) + .unwrap(), + ArtifactReferences { + referenced: false, + complete: true + } + ); + let mutation = + super::super::maintenance_ownership::ReferenceMutation::begin_in_domain(&f.domain) + .unwrap(); + let generation = f.layout.generations_root().join("gen-leased-or-recovery"); + std::fs::create_dir(&generation).unwrap(); + std::fs::write( + generation.join("manifest.json"), + producer(Some("ac".repeat(32))), + ) + .unwrap(); + drop(mutation); + assert_eq!( + prepare_artifact_references(&f.target, &f.domain, &e, [0xac; 32], &memory, &|| false), + Err(ReferenceIssue::Stale) + ); + let e = f.observe(); + assert_eq!( + prepare_artifact_references(&f.target, &f.domain, &e, [0xac; 32], &memory, &|| false) + .unwrap(), + ArtifactReferences { + referenced: true, + complete: true + } + ); + } + #[test] + fn dc_references_live_producer_and_changed_epoch_never_prove_absence() { + let f = ManagedFixture::new(); + let memory = CleanupMemory::new(256 * 1024).unwrap(); + let e = f.observe(); + let mutation = + super::super::maintenance_ownership::ReferenceMutation::begin_in_domain(&f.domain) + .unwrap(); + assert!(prepare_artifact_references( + &f.target, + &f.domain, + &e, + [0xac; 32], + &memory, + &|| false + ) + .is_err()); + drop(mutation); + assert_eq!( + prepare_artifact_references(&f.target, &f.domain, &e, [0xac; 32], &memory, &|| false), + Err(ReferenceIssue::Stale) + ); + } + #[test] + fn dc_references_recovery_runs_and_unknown_generations_remain_incomplete() { + let f = ManagedFixture::new(); + let memory = CleanupMemory::new(256 * 1024).unwrap(); + let mutation = + super::super::maintenance_ownership::ReferenceMutation::begin_in_domain(&f.domain) + .unwrap(); + std::fs::create_dir(f.layout.runs_root().join("run-recovery")).unwrap(); + drop(mutation); + let e = f.observe(); + assert!( + !prepare_artifact_references(&f.target, &f.domain, &e, [0xac; 32], &memory, &|| false) + .unwrap() + .complete + ); + let mutation = + super::super::maintenance_ownership::ReferenceMutation::begin_in_domain(&f.domain) + .unwrap(); + std::fs::remove_dir(f.layout.runs_root().join("run-recovery")).unwrap(); + std::fs::write(f.layout.generations_root().join("foreign"), b"unknown").unwrap(); + drop(mutation); + let e = f.observe(); + assert!( + !prepare_artifact_references(&f.target, &f.domain, &e, [0xac; 32], &memory, &|| false) + .unwrap() + .complete + ); + } + #[test] + fn dc_references_source_change_during_scan_cannot_prove_absence() { + let root = TestRoot::new(); + let path = root.0.join("manifest.json"); + let bytes = producer(None); + std::fs::write(&path, &bytes).unwrap(); + let file = File::open(&path).unwrap(); + let memory = CleanupMemory::new(128 * 1024).unwrap(); + let calls = std::cell::Cell::new(0); + let observe = || { + let n = calls.get() + 1; + calls.set(n); + if n == 5 { + let mut changed = bytes.clone(); + changed.extend_from_slice(b" "); + std::fs::write(&path, changed).unwrap(); + } + false + }; + assert_eq!( + manifest(&file, [0xab; 32], &memory, &observe), + Err(ReferenceIssue::Stale) + ); + } + #[test] + fn dc_references_direct_current_and_recovery_manifest_preservation() { + use super::super::inventory::DirectPair; + use super::super::layout::{create_fresh_reference_root, DirectLayout}; + use super::super::locks::{open_locked, LockMode}; + use super::super::maintenance_ownership::{ + initialize_fresh_reference_domain, observe_reference_epoch, ArtifactUseLease, + EpochObservation, + }; + let root = TestRoot::new(); + let destination = root.0.join("direct"); + let fresh = create_fresh_reference_root(&destination).unwrap().unwrap(); + let pair = DirectPair { + database: destination.join("graph.ldb"), + manifest: destination.join("manifest.json"), + }; + let layout = DirectLayout::new(&pair.database, &pair.manifest); + std::fs::write(&pair.database, b"fixture database, never opened").unwrap(); + std::fs::write(&pair.manifest, producer(Some("ab".repeat(32)))).unwrap(); + drop(ArtifactUseLease::acquire_shared(&layout.artifact_root_path()).unwrap()); + let writer = open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap(); + let domain = ReferenceDomain::direct(&layout).unwrap(); + assert!(initialize_fresh_reference_domain(&domain, &fresh, &writer).unwrap()); + drop(writer); + let target = MaintenanceTarget { + repository_root: root.0.clone(), + state_root: root.0.clone(), + storage_root: None, + direct_pair: Some(pair.clone()), + config_path: None, + }; + let memory = CleanupMemory::new(256 * 1024).unwrap(); + let observe = || match observe_reference_epoch(&domain).unwrap() { + EpochObservation::Verified(e) => e, + other => panic!("{other:?}"), + }; + let e = observe(); + assert_eq!( + prepare_artifact_references(&target, &domain, &e, [0xab; 32], &memory, &|| false) + .unwrap(), + ArtifactReferences { + referenced: true, + complete: true + } + ); + let mutation = + super::super::maintenance_ownership::ReferenceMutation::begin_in_domain(&domain) + .unwrap(); + std::fs::write( + layout.manifest_candidate_path(), + producer(Some("ac".repeat(32))), + ) + .unwrap(); + drop(mutation); + let e = observe(); + assert!( + !prepare_artifact_references(&target, &domain, &e, [0xac; 32], &memory, &|| false) + .unwrap() + .complete + ); + let mutation = + super::super::maintenance_ownership::ReferenceMutation::begin_in_domain(&domain) + .unwrap(); + std::fs::remove_file(layout.manifest_candidate_path()).unwrap(); + std::fs::write(layout.journal_path(), b"unknown recovery").unwrap(); + drop(mutation); + let e = observe(); + assert!( + !prepare_artifact_references(&target, &domain, &e, [0xac; 32], &memory, &|| false) + .unwrap() + .complete + ); + } +} diff --git a/src/storage/maintenance_retirement.rs b/src/storage/maintenance_retirement.rs new file mode 100644 index 00000000..7971ddde --- /dev/null +++ b/src/storage/maintenance_retirement.rs @@ -0,0 +1,531 @@ +//! Incarnation-bound Direct read/rebuild state. These control records alone do +//! not prove bundle origin, quiescence or authorize retirement of any leaf. +use super::layout::DirectLayout; +use super::locks::{ + file_identity, path_identity, FileIdentity, LockMode, RootedDirectory, WriterLease, +}; +use crate::error::NativeError; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; + +const BLOCK: usize = 256; +const HEADER_MAGIC: &[u8; 8] = b"CGDRTH02"; +const RECORD_MAGIC: &[u8; 8] = b"CGDRTS02"; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum DirectDisposition { + Untracked, + Active, + Retiring, + Retired, + Unknown, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +enum Phase { + Active = 1, + Retiring = 2, + Retired = 3, +} +#[derive(Clone, Copy)] +struct Header { + root: FileIdentity, + writer: FileIdentity, + control: FileIdentity, + key: [u8; 64], + nonce: [u8; 32], +} +#[derive(Clone, Copy)] +struct Record { + phase: Phase, + generation: u64, + database: FileIdentity, + manifest: FileIdentity, + incarnation: [u8; 32], +} +fn invalid(message: &str) -> NativeError { + NativeError::InvalidInput(message.to_owned()) +} +fn put_identity(out: &mut [u8], id: FileIdentity) { + let (a, b) = id.fixed_words(); + out[..8].copy_from_slice(&a.to_le_bytes()); + out[8..16].copy_from_slice(&b.to_le_bytes()); +} +fn identity(bytes: &[u8]) -> FileIdentity { + FileIdentity::from_fixed_words( + u64::from_le_bytes(bytes[..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + ) +} +fn finish(bytes: &mut [u8; BLOCK]) { + let hash = Sha256::digest(&bytes[..BLOCK - 32]); + bytes[BLOCK - 32..].copy_from_slice(&hash); +} +fn checked(bytes: &[u8; BLOCK], magic: &[u8; 8]) -> bool { + &bytes[..8] == magic && bytes[BLOCK - 32..] == Sha256::digest(&bytes[..BLOCK - 32])[..] +} +fn encode_header(header: Header) -> [u8; BLOCK] { + let mut bytes = [0; BLOCK]; + bytes[..8].copy_from_slice(HEADER_MAGIC); + put_identity(&mut bytes[8..24], header.root); + put_identity(&mut bytes[24..40], header.writer); + put_identity(&mut bytes[40..56], header.control); + bytes[56..120].copy_from_slice(&header.key); + bytes[120..152].copy_from_slice(&header.nonce); + finish(&mut bytes); + bytes +} +fn decode_header(bytes: &[u8; BLOCK]) -> Option

{ + if !checked(bytes, HEADER_MAGIC) || bytes[152..BLOCK - 32].iter().any(|b| *b != 0) { + return None; + } + let key = bytes[56..120].try_into().ok()?; + Some(Header { + root: identity(&bytes[8..24]), + writer: identity(&bytes[24..40]), + control: identity(&bytes[40..56]), + key, + nonce: bytes[120..152].try_into().ok()?, + }) +} +fn encode_record(header: Header, record: Record) -> [u8; BLOCK] { + let mut bytes = [0; BLOCK]; + bytes[..8].copy_from_slice(RECORD_MAGIC); + bytes[8] = record.phase as u8; + bytes[16..24].copy_from_slice(&record.generation.to_le_bytes()); + bytes[24..56].copy_from_slice(&header.nonce); + put_identity(&mut bytes[56..72], record.database); + put_identity(&mut bytes[72..88], record.manifest); + bytes[88..120].copy_from_slice(&record.incarnation); + finish(&mut bytes); + bytes +} +fn decode_record(header: Header, bytes: &[u8; BLOCK]) -> Option { + if !checked(bytes, RECORD_MAGIC) + || bytes[24..56] != header.nonce + || bytes[9..16] + .iter() + .chain(bytes[120..BLOCK - 32].iter()) + .any(|b| *b != 0) + { + return None; + } + let phase = match bytes[8] { + 1 => Phase::Active, + 2 => Phase::Retiring, + 3 => Phase::Retired, + _ => return None, + }; + Some(Record { + phase, + generation: u64::from_le_bytes(bytes[16..24].try_into().ok()?), + database: identity(&bytes[56..72]), + manifest: identity(&bytes[72..88]), + incarnation: bytes[88..120].try_into().ok()?, + }) +} +fn component(layout: &DirectLayout) -> (Vec, [u8; 64]) { + // This new private control uses a lossless full native pair digest. Existing + // writer/registration/journal paths retain their established layout keys. + let mut hash = Sha256::new(); + #[cfg(unix)] + { + use std::os::unix::ffi::OsStrExt; + hash.update(b"direct-marker-unix-v2"); + for path in [layout.db_path(), layout.manifest_path()] { + let bytes = path.as_os_str().as_bytes(); + hash.update((bytes.len() as u64).to_le_bytes()); + hash.update(bytes); + } + } + #[cfg(windows)] + { + use std::os::windows::ffi::OsStrExt; + hash.update(b"direct-marker-windows-v2"); + for path in [layout.db_path(), layout.manifest_path()] { + hash.update((path.as_os_str().encode_wide().count() as u64).to_le_bytes()); + for unit in path.as_os_str().encode_wide() { + hash.update(unit.to_le_bytes()); + } + } + } + #[cfg(not(any(unix, windows)))] + { + hash.update(b"direct-marker-unsupported-v2"); + hash.update(layout.db_path().to_string_lossy().as_bytes()); + hash.update([0]); + hash.update(layout.manifest_path().to_string_lossy().as_bytes()); + } + let mut fixed = [0; 64]; + let digits = b"0123456789abcdef"; + for (i, byte) in hash.finalize().iter().enumerate() { + fixed[i * 2] = digits[(byte >> 4) as usize]; + fixed[i * 2 + 1] = digits[(byte & 15) as usize]; + } + let short = std::str::from_utf8(&fixed).expect("hex ASCII"); + ( + format!(".direct-retirement-{short}.log").into_bytes(), + fixed, + ) +} +fn read_block(file: &mut File, offset: u64) -> std::io::Result<[u8; BLOCK]> { + let mut bytes = [0; BLOCK]; + file.seek(SeekFrom::Start(offset))?; + file.read_exact(&mut bytes)?; + Ok(bytes) +} +fn root_anchor(layout: &DirectLayout, write: bool) -> Result { + let root = layout + .db_path() + .parent() + .ok_or_else(|| invalid("direct_marker_missing_parent"))?; + let id = path_identity(root)?; + Ok(if write { + RootedDirectory::open(root, id)? + } else { + RootedDirectory::open_readonly_root(root, id)? + }) +} +fn named_writer( + anchor: &RootedDirectory, + layout: &DirectLayout, +) -> Result { + let path = layout.writer_lock_path(); + let name = path + .file_name() + .and_then(|s| s.to_str()) + .ok_or_else(|| invalid("direct_marker_writer_name"))?; + file_identity(&anchor.open_readonly_file(name.as_bytes())?) +} +fn bound_header( + anchor: &RootedDirectory, + layout: &DirectLayout, + file: &mut File, + key: [u8; 64], +) -> Result, NativeError> { + if file.metadata()?.len() < BLOCK as u64 { + return Ok(None); + } + let Some(header) = decode_header(&read_block(file, 0)?) else { + return Ok(None); + }; + if header.root != anchor.identity() + || header.writer != named_writer(anchor, layout)? + || header.control != file_identity(file)? + || header.key != key + { + return Ok(None); + } + Ok(Some(header)) +} +fn last_record(file: &mut File, header: Header) -> Result, NativeError> { + if file.metadata()?.len() != 3 * BLOCK as u64 { + return Ok(None); + } + let mut records = [None, None]; + for (index, record) in records.iter_mut().enumerate() { + let block = read_block(file, ((index + 1) * BLOCK) as u64)?; + if block.iter().all(|b| *b == 0) { + continue; + } + let Some(decoded) = decode_record(header, &block) else { + return Err(invalid("direct_marker_unknown_slot_preserved")); + }; + if decoded.generation == 0 { + return Err(invalid("direct_marker_invalid_generation")); + } + *record = Some(decoded); + } + match records { + [None, None] => Ok(None), + [Some(one), None] | [None, Some(one)] if one.generation == 1 => Ok(Some(one)), + [Some(a), Some(b)] if a.generation.checked_add(1) == Some(b.generation) => Ok(Some(b)), + [Some(a), Some(b)] if b.generation.checked_add(1) == Some(a.generation) => Ok(Some(a)), + _ => Err(invalid("direct_marker_ambiguous_slots")), + } +} + +fn regular_identity(path: &std::path::Path) -> Result, NativeError> { + match std::fs::symlink_metadata(path) { + Ok(m) => { + if !m.is_file() || m.file_type().is_symlink() { + return Ok(None); + } + Ok(Some(path_identity(path)?)) + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(e) => Err(e.into()), + } +} +fn observed_source_disposition( + layout: &DirectLayout, +) -> Result, NativeError> { + use super::maintenance_legacy_guard as legacy; + let Some(parent) = layout.db_path().parent() else { + return Ok(None); + }; + if parent + .file_name() + .is_none_or(|name| name != ".codebaseGraph") + || layout.manifest_path() != parent.join("manifest.json") + { + return Ok(None); + } + let root = match root_anchor(layout, false) { + Ok(root) => root, + Err(_) => return Ok(None), + }; + let gate = match root.open_readonly_file(legacy::GATE) { + Ok(file) => file, + Err(_) => return Ok(None), + }; + let state = match root.open_readonly_file(legacy::STATE) { + Ok(file) => file, + Err(_) => return Ok(None), + }; + let observed = match legacy::observe_files(&root, &gate, &state) { + Ok(value) => value, + Err(_) => return Ok(None), + }; + if observed.binding.kind != legacy::LegacyScopeKind::DefaultDirect + || layout + .db_path() + .file_name() + .map(|name| name.as_encoded_bytes()) + != Some(&observed.binding.database[..observed.binding.database_len]) + { + return Ok(None); + } + let mut hash = Sha256::new(); + hash.update(layout.db_path().as_os_str().as_encoded_bytes()); + hash.update([0]); + hash.update(layout.manifest_path().as_os_str().as_encoded_bytes()); + let mut key = [0; 64]; + let digits = b"0123456789abcdef"; + for (i, byte) in hash.finalize().iter().enumerate() { + key[i * 2] = digits[(byte >> 4) as usize]; + key[i * 2 + 1] = digits[(byte & 15) as usize]; + } + if observed.binding.key != key { + return Ok(None); + } + for (path, expected) in [ + (layout.db_path(), observed.record.database), + (layout.manifest_path(), observed.record.manifest), + ] { + let Some(name) = path.file_name() else { + return Ok(None); + }; + match root.open_readonly_file(name.as_encoded_bytes()) { + Ok(file) => { + if expected.is_none_or(|stamp| stamp.recheck(&file).is_err()) { + return Ok(None); + } + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} + Err(_) => return Ok(None), + } + } + Ok(match observed.record.phase { + legacy::LegacyPhase::Retiring => Some(DirectDisposition::Retiring), + legacy::LegacyPhase::Removed => Some(DirectDisposition::Retired), + _ => None, + }) +} + +/// Existing-only observation. Unknown/corrupt/missing evidence does not acquire +/// provenance, repair controls or change the primary legacy-read outcome. +pub(crate) fn observe_direct(layout: &DirectLayout) -> Result { + // Actual source retirement already persists the full associated source + // lifecycle under native/writer/root exclusion. Reading that disposition + // grants no origin, migration or deletion authority. + if let Some(disposition) = observed_source_disposition(layout)? { + return Ok(disposition); + } + let anchor = match root_anchor(layout, false) { + Ok(anchor) => anchor, + Err(_) => return Ok(DirectDisposition::Unknown), + }; + let (name, key) = component(layout); + let mut file = match anchor.open_readonly_file(&name) { + Ok(file) => file, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(DirectDisposition::Untracked) + } + Err(_) => return Ok(DirectDisposition::Unknown), + }; + let header = match bound_header(&anchor, layout, &mut file, key) { + Ok(Some(header)) => header, + _ => return Ok(DirectDisposition::Unknown), + }; + let length = file.metadata()?.len(); + let Some(record) = last_record(&mut file, header).ok().flatten() else { + return Ok(DirectDisposition::Unknown); + }; + if file.metadata()?.len() != length { + return Ok(DirectDisposition::Unknown); + } + let db = regular_identity(layout.db_path())?; + let manifest = regular_identity(layout.manifest_path())?; + // A new public incarnation is never addressed by an old retirement record. + // The explicit publisher writes ACTIVE for it; an untracked replacement is + // conservatively Unknown and still receives the ordinary primary read. + if db.is_some_and(|id| id != record.database) + || manifest.is_some_and(|id| id != record.manifest) + { + return Ok(DirectDisposition::Unknown); + } + Ok(match record.phase { + Phase::Active if db == Some(record.database) && manifest == Some(record.manifest) => { + DirectDisposition::Active + } + Phase::Active => DirectDisposition::Unknown, + Phase::Retiring => DirectDisposition::Retiring, + Phase::Retired => DirectDisposition::Retired, + }) +} +/// Called only after a normal explicit Direct publication while retaining the +/// ACTUAL exclusive writer. The new pair is ACTIVE even after an older pair's +/// retirement. Unknown control collisions are preserved, never overwritten. +pub(crate) fn mark_explicit_direct_rebuild_active( + layout: &DirectLayout, + writer: &WriterLease, +) -> Result<(), NativeError> { + if !writer.verifies(&layout.writer_lock_path(), LockMode::Exclusive)? { + return Err(invalid("direct_active_requires_actual_writer")); + } + let Some(database) = regular_identity(layout.db_path())? else { + return Err(invalid("direct_active_database_missing")); + }; + let Some(manifest) = regular_identity(layout.manifest_path())? else { + return Err(invalid("direct_active_manifest_missing")); + }; + let anchor = root_anchor(layout, true)?; + if named_writer(&anchor, layout)? != writer.identity()? { + return Err(invalid("direct_active_writer_changed")); + } + let (name, key) = component(layout); + let mut fresh = false; + let mut file = match anchor.open_existing(&name) { + Ok(file) => file, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + let file = anchor.create_new(&name)?; + fresh = true; + file + } + Err(e) => return Err(e.into()), + }; + let header = if fresh { + let mut nonce = [0; 32]; + getrandom::fill(&mut nonce).map_err(|_| invalid("direct_marker_entropy_unavailable"))?; + let header = Header { + root: anchor.identity(), + writer: writer.identity()?, + control: file_identity(&file)?, + key, + nonce, + }; + super::locks::set_created_file_nonce(&file, &header.nonce)?; + file.write_all(&encode_header(header))?; + file.write_all(&[0; 2 * BLOCK])?; + file.sync_all()?; + anchor.sync_namespace()?; + header + } else { + let Some(header) = bound_header(&anchor, layout, &mut file, key)? else { + return Err(invalid("direct_marker_unknown_preserved")); + }; + header + }; + let mut incarnation = [0; 32]; + getrandom::fill(&mut incarnation).map_err(|_| invalid("direct_active_entropy_unavailable"))?; + append_record( + &mut file, + header, + Record { + phase: Phase::Active, + generation: 0, + database, + manifest, + incarnation, + }, + )?; + if !writer.verifies(&layout.writer_lock_path(), LockMode::Exclusive)? + || regular_identity(layout.db_path())? != Some(database) + || regular_identity(layout.manifest_path())? != Some(manifest) + { + return Err(invalid("direct_active_publication_changed")); + } + Ok(()) +} +fn append_record(file: &mut File, header: Header, mut record: Record) -> Result<(), NativeError> { + if file.metadata()?.len() != 3 * BLOCK as u64 { + return Err(invalid("direct_marker_size_unknown_preserved")); + } + let old = last_record(file, header)?; + record.generation = old + .map_or(Some(1), |record| record.generation.checked_add(1)) + .ok_or_else(|| invalid("direct_marker_generation_exhausted"))?; + let slot = ((record.generation - 1) % 2) as usize; + let bytes = encode_record(header, record); + let offset = ((slot + 1) * BLOCK) as u64; + file.seek(SeekFrom::Start(offset))?; + file.write_all(&bytes)?; + file.sync_all()?; + if read_block(file, offset)? != bytes { + return Err(invalid("direct_marker_readback_changed")); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn direct_marker_fixed_slots_preserve_full_native_pair_identity() { + let a = DirectLayout::new("/a/db.ldb", "/a/manifest.json"); + let b = DirectLayout::new("/a/db.ldb", "/b/manifest.json"); + let (an, ak) = component(&a); + let (bn, bk) = component(&b); + assert_ne!(an, bn); + assert_ne!(ak, bk); + assert!(an.len() <= 255); + #[cfg(unix)] + { + use std::os::unix::ffi::OsStringExt; + let p = + std::path::PathBuf::from(std::ffi::OsString::from_vec(b"/a/db\xff.ldb".to_vec())); + let q = + std::path::PathBuf::from(std::ffi::OsString::from_vec(b"/a/db\xfe.ldb".to_vec())); + assert_ne!( + component(&DirectLayout::new(p, "/a/manifest.json")).1, + component(&DirectLayout::new(q, "/a/manifest.json")).1 + ); + } + } + #[test] + fn direct_marker_record_generation_and_corruption_are_checked() { + let id = FileIdentity::from_fixed_words(1, 2); + let header = Header { + root: id, + writer: id, + control: id, + key: [b'a'; 64], + nonce: [3; 32], + }; + let record = Record { + phase: Phase::Active, + generation: 7, + database: id, + manifest: id, + incarnation: [4; 32], + }; + let bytes = encode_record(header, record); + let read = decode_record(header, &bytes).unwrap(); + assert_eq!(read.generation, 7); + assert_eq!(read.database, id); + let mut bad = bytes; + bad[16] ^= 1; + assert!(decode_record(header, &bad).is_none()); + } +} diff --git a/src/storage/maintenance_scheduler.rs b/src/storage/maintenance_scheduler.rs new file mode 100644 index 00000000..075e013f --- /dev/null +++ b/src/storage/maintenance_scheduler.rs @@ -0,0 +1,2177 @@ +//! Joined advisory-driver foundation. This module performs no storage collection. +//! Drivers must reserve every dynamic allocation from the supplied lifetime account. +use super::maintenance_admission::{LegacyConfirmationSource, PreparedLegacyConfirmationSource}; +use super::maintenance_budget::{ + CleanupMemory, MemoryBox, MemoryReservation, MutationGrant, ResourceLimit, +}; +use std::alloc::Layout; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread::{self, JoinHandle}; +use std::time::{Duration, Instant}; + +const DEFAULT_INTERVAL: Duration = Duration::from_secs(60); +const THREAD_STACK: usize = 256 * 1024; +// Requested-layout allowance, not allocator slack/RSS. Rust 1.96 std/thread +// lifecycle.rs allocates Packet, rust_start and ThreadInit, and thread.rs owns +// the Thread handle. The native allocation probe observed <256 bytes beyond +// Shared/captures without a name; reserve 4 KiB for those fixed blocks and +// platform hooks/TLS scaffolding. Closure layout/padding is charged separately. +// Toolchain/platform qualification must rerun the instrumented boundary probe. +const THREAD_STARTUP_ALLOWANCE: usize = 4096; + +/// Validated opaque target identity; paths/proofs belong to the accounted driver. +/// Equality must include the installation and physical destination incarnation. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct TargetBinding(pub(crate) [u8; 32]); + +/// A borrowed, one-use operator demand captured only by the post-permit +/// mailbox gate. Neither a bool nor stored options can construct this token. +/// Source/version evidence remains in the separately rechecked preparation. +pub(crate) struct LegacyQuiescenceConfirmation<'a> { + binding: &'a TargetBinding, + generation: &'a u64, + source: Option, + _same_thread: std::marker::PhantomData>, +} +impl LegacyQuiescenceConfirmation<'_> { + pub(crate) fn binding_digest(&self) -> [u8; 32] { + self.binding.0 + } + pub(crate) fn generation(&self) -> u64 { + *self.generation + } + pub(crate) fn source(&self) -> Option<&LegacyConfirmationSource> { + self.source.as_ref() + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum DriverError { + ResourceLimit, + Preparation, + Mutation, + Panicked, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum AttemptOutcome { + Complete, + Pending, + Deferred, + Cancelled, + Failed(DriverError), +} + +/// Fixed-size control evidence, deliberately distinct from measured storage health. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct DriverSnapshot { + pub(crate) binding: TargetBinding, + pub(crate) generation: u64, + pub(crate) requested: bool, + pub(crate) pending: bool, + pub(crate) running: bool, + pub(crate) liveness: DriverLiveness, + pub(crate) last_attempt: Option, + pub(crate) failure: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct AttemptRecord { + pub(crate) binding: TargetBinding, + pub(crate) generation: u64, + pub(crate) request_sequence: u64, + pub(crate) triggers: u8, + pub(crate) outcome: AttemptOutcome, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum DriverLiveness { + Running, + Stopping, + Stopped, + Failed, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum RequestDisposition { + Enqueued, + UnboundConfirmation, + Stopped, + Failed, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum Trigger { + Startup = 1, + Completion = 2, + Health = 4, + Continuation = 8, +} + +/// Only this interface may acquire the existing actual dispatcher slot. Its permit +/// must release admission on Drop and cannot contain any advisory/bulk work. +pub(crate) trait MaintenanceAdmission: Send + Sync + 'static { + type Permit; + fn try_acquire(&self) -> Option; + fn stop_admission(&self); + + // Integration fixtures exercise the post-disposal dispatcher seam; + // shipping recovery remains in the actual producer/read entry points. + #[cfg(test)] + fn run_recovery( + &self, + _operation: &mut dyn FnMut() -> Result<(), DriverError>, + ) -> Result { + Ok(false) + } +} + +/// Constructed on the dedicated thread; neither Driver nor Work need Send/Sync. +/// Work must own no storage authority, and Drop must perform no filesystem effects. +/// mutate only immutably borrows bulk Work; no bulk ownership may be moved into +/// the callback or destroyed there (including driver-held buffers). +/// Storage admission and all authority stay within +/// mutate, and only fixed, bounded hot frames may be consumed by inner callbacks. +pub(crate) trait MaintenanceDriver { + type Work; + fn desired_binding(&self) -> Option { + None + } + /// Fixed status publication only, after all work/disposal/recovery ends. + fn completed(&mut self, _outcome: AttemptOutcome) {} + fn prepare( + &mut self, + binding: TargetBinding, + cancellation: &Cancellation<'_>, + memory: &CleanupMemory, + ) -> Result, DriverError>; + fn requires_legacy_confirmation(&self, _work: &Self::Work) -> bool { + false + } + fn source_payloads_parked(&self) -> bool { + false + } + fn mutate( + &mut self, + work: &Self::Work, + grant: &mut MutationGrant, + confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result; + /// One advisory validation/disposal continuation, after the real permit and + /// every storage authority have been released. Bulk Work remains owned by + /// the driver loop throughout this call and its eventual destruction. + fn finish( + &mut self, + _work: &mut Self::Work, + outcome: AttemptOutcome, + _cancellation: &Cancellation<'_>, + _memory: &CleanupMemory, + ) -> Result { + Ok(outcome) + } + /// Follow-up producer recovery starts only after every prepared payload and + /// its source handles have been disposed outside the collector permit. + fn after_disposal( + &mut self, + outcome: AttemptOutcome, + _cancellation: &Cancellation<'_>, + _memory: &CleanupMemory, + ) -> Result { + Ok(outcome) + } +} + +/// Pure coalescing clock state. New requests never slide an existing due time. +struct MaintenanceScheduler { + interval: Duration, + next_due: Duration, + pending: u8, + sequence: u64, +} +impl MaintenanceScheduler { + fn new(interval: Duration) -> Self { + Self { + interval, + next_due: Duration::ZERO, + pending: Trigger::Startup as u8, + sequence: 0, + } + } + fn request(&mut self, trigger: Trigger) { + self.pending |= trigger as u8; + self.sequence = self.sequence.saturating_add(1); + } + fn begin(&mut self, now: Duration) -> Option<(u8, u64)> { + if now < self.next_due { + return None; + } + let triggers = std::mem::take(&mut self.pending); + self.next_due = now.saturating_add(self.interval); + Some((triggers, self.sequence)) + } + fn complete(&mut self, now: Duration, outcome: AttemptOutcome) { + // Cooldown starts after actual work/disposal, not after preparation start. + self.next_due = now.saturating_add(self.interval); + if outcome != AttemptOutcome::Complete { + self.pending |= Trigger::Continuation as u8; + } + } +} + +struct Control { + binding: TargetBinding, + generation: u64, + confirmation: bool, + confirmation_source: Option>, + claimed_source: Option>, + capturing_source: bool, + source_payloads_parked: bool, + stopping: bool, + scheduler: MaintenanceScheduler, + snapshot: DriverSnapshot, +} + +struct Shared { + control: Mutex, + wake: Condvar, + admission: A, + memory: CleanupMemory, + // Retains runtime/control/closure/explicit stack layout charge through join + // and all driver/work disposal. This is requested-layout accounting, not RSS. + _reservation: MemoryReservation, +} + +pub(crate) struct Cancellation<'a> { + check: &'a dyn Fn() -> bool, + legacy_interest: &'a dyn Fn() -> bool, + legacy_metadata: &'a dyn Fn() -> Option<(u64, Option)>, +} +impl Cancellation<'_> { + pub(crate) fn is_cancelled(&self) -> bool { + (self.check)() + } + /// Advisory scheduling interest only; this cannot authorize enrollment. + pub(crate) fn legacy_confirmation_pending(&self) -> bool { + (self.legacy_interest)() + } + /// Advisory bytes for off-permit preparation. Only the actual claimed + /// non-Send token can authorize the matching mutation later. + pub(crate) fn legacy_confirmation_metadata( + &self, + ) -> Option<(u64, Option)> { + (self.legacy_metadata)() + } +} + +/// Clone owns only bounded control state. It never owns Runtime, API or refresh. +pub(crate) struct MaintenanceHandle { + shared: Arc>, +} +impl Clone for MaintenanceHandle { + fn clone(&self) -> Self { + Self { + shared: Arc::clone(&self.shared), + } + } +} +/// A fixed reservation prevents two request threads from opening duplicate +/// source pins. The worker cannot begin another preparation until it is dropped. +pub(crate) struct SourceCapture { + shared: Arc>, +} +impl Drop for SourceCapture { + fn drop(&mut self) { + self.shared + .control + .lock() + .unwrap_or_else(|error| error.into_inner()) + .capturing_source = false; + self.shared.wake.notify_all(); + } +} +impl MaintenanceHandle { + pub(crate) fn begin_source_capture(&self) -> Option> { + let previous = { + let mut control = self + .shared + .control + .lock() + .unwrap_or_else(|error| error.into_inner()); + if control.stopping + || control.snapshot.liveness == DriverLiveness::Failed + || control.snapshot.running + || control.source_payloads_parked + || control.claimed_source.is_some() + || control.capturing_source + { + return None; + } + control.capturing_source = true; + control.confirmation = false; + control.confirmation_source.take() + }; + // This request owns no execution authority. Old pins are gone BEFORE + // the replacement request opens its source pair. + drop(previous); + Some(SourceCapture { + shared: Arc::clone(&self.shared), + }) + } + pub(crate) fn snapshot(&self) -> DriverSnapshot { + self.shared + .control + .lock() + .unwrap_or_else(|e| e.into_inner()) + .snapshot + } + /// A transient owner may wait for its startup opportunity without polling + /// or imposing a deadline on preparation, validation or disposal. The + /// returned attempt is published only after bulk work has been dropped. + /// Rebinding returns the new snapshot so its owner can discard old demand; + /// stopping waits for actual worker disposal and terminal publication. + pub(crate) fn wait_for_first_attempt(&self) -> DriverSnapshot { + let mut control = self + .shared + .control + .lock() + .unwrap_or_else(|e| e.into_inner()); + let binding = control.binding; + let generation = control.generation; + loop { + if control.binding != binding + || control.generation != generation + || matches!( + control.snapshot.liveness, + DriverLiveness::Stopped | DriverLiveness::Failed + ) + || control.snapshot.last_attempt.is_some_and(|attempt| { + attempt.binding == binding && attempt.generation == generation + }) + { + return control.snapshot; + } + control = self + .shared + .wake + .wait(control) + .unwrap_or_else(|e| e.into_inner()); + } + } + + /// Confirmation: busy/preparation-error retains the exact current demand; + /// the post-permit gate takes it once, including callback error/panic or an + /// expired grant. Rebind/stop/failure discard it. A new in-flight demand stays + /// pending for a later gate. False never creates or clears confirmation. + pub(crate) fn request( + &self, + trigger: Trigger, + confirmation: Option, + ) -> RequestDisposition { + self.request_with_source(trigger, confirmation, None) + } + pub(crate) fn request_with_source( + &self, + trigger: Trigger, + confirmation: Option, + source: Option>, + ) -> RequestDisposition { + let mut control = self + .shared + .control + .lock() + .unwrap_or_else(|e| e.into_inner()); + if control.snapshot.liveness == DriverLiveness::Failed { + return RequestDisposition::Failed; + } + if control.stopping { + return RequestDisposition::Stopped; + } + if confirmation.is_some_and(|binding| binding != control.binding) { + return RequestDisposition::UnboundConfirmation; + } + if confirmation.is_some() { + control.confirmation_source = source; + } + control.confirmation |= confirmation.is_some(); + control.scheduler.request(trigger); + control.snapshot.requested = true; + control.snapshot.pending = true; + self.shared.wake.notify_one(); + RequestDisposition::Enqueued + } + pub(crate) fn request_and_wait( + &self, + trigger: Trigger, + confirmation: Option, + ) -> DriverSnapshot { + self.request_and_wait_with_source(trigger, confirmation, None) + } + pub(crate) fn request_and_wait_with_source( + &self, + trigger: Trigger, + confirmation: Option, + source: Option>, + ) -> DriverSnapshot { + let mut control = self + .shared + .control + .lock() + .unwrap_or_else(|e| e.into_inner()); + if control.stopping + || control.snapshot.liveness == DriverLiveness::Failed + || confirmation.is_some_and(|binding| binding != control.binding) + { + return control.snapshot; + } + if confirmation.is_some() { + control.confirmation_source = source; + } + control.confirmation |= confirmation.is_some(); + control.scheduler.request(trigger); + control.snapshot.requested = true; + control.snapshot.pending = true; + let binding = control.binding; + let generation = control.generation; + let sequence = control.scheduler.sequence; + self.shared.wake.notify_all(); + loop { + if control.binding != binding + || control.generation != generation + || matches!( + control.snapshot.liveness, + DriverLiveness::Stopped | DriverLiveness::Failed + ) + || control.snapshot.last_attempt.is_some_and(|attempt| { + attempt.binding == binding + && attempt.generation == generation + && attempt.request_sequence >= sequence + }) + { + return control.snapshot; + } + control = self + .shared + .wake + .wait(control) + .unwrap_or_else(|e| e.into_inner()); + } + } + + pub(crate) fn rebind(&self, binding: TargetBinding) -> RequestDisposition { + let mut control = self + .shared + .control + .lock() + .unwrap_or_else(|e| e.into_inner()); + if control.snapshot.liveness == DriverLiveness::Failed { + return RequestDisposition::Failed; + } + if control.stopping { + return RequestDisposition::Stopped; + } + if binding != control.binding { + let Some(generation) = control.generation.checked_add(1) else { + control.stopping = true; + control.snapshot.liveness = DriverLiveness::Failed; + self.shared.wake.notify_one(); + return RequestDisposition::Failed; + }; + control.binding = binding; + control.generation = generation; + control.confirmation = false; + control.confirmation_source = None; + control.snapshot.binding = binding; + control.snapshot.generation = generation; + // Old evidence keeps its old binding/generation; never relabel it. + control.scheduler.request(Trigger::Startup); + control.snapshot.pending = true; + } + self.shared.wake.notify_one(); + RequestDisposition::Enqueued + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum StartError { + ResourceLimit(ResourceLimit), + SpawnFailed, +} + +/// Unique owner of the join handle. Never cloned or captured by its own worker. +pub(crate) struct MaintenanceRuntime { + handle: MaintenanceHandle, + thread: Option>, +} +impl MaintenanceRuntime { + /// The caller creates ONE account before any dynamic binding/factory data. + /// Captured owned buffers must already retain reservations on this account; + /// external infrastructure (dispatcher/fixture channels) is not cleanup data. + /// Driver allocations must use this same account; it is never replaced. + pub(crate) fn start( + memory: CleanupMemory, + binding: TargetBinding, + admission: A, + factory: F, + ) -> Result + where + D: MaintenanceDriver, + F: FnOnce(&CleanupMemory) -> Result + Send + 'static, + { + Self::start_with_interval(memory, binding, admission, factory, DEFAULT_INTERVAL) + } + + pub(crate) fn start_with_confirmation( + memory: CleanupMemory, + binding: TargetBinding, + admission: A, + factory: F, + confirmation: bool, + source: Option>, + ) -> Result + where + D: MaintenanceDriver, + F: FnOnce(&CleanupMemory) -> Result + Send + 'static, + { + Self::start_with_options( + memory, + binding, + admission, + factory, + DEFAULT_INTERVAL, + confirmation, + source, + ) + } + + fn start_with_interval( + memory: CleanupMemory, + binding: TargetBinding, + admission: A, + factory: F, + interval: Duration, + ) -> Result + where + D: MaintenanceDriver, + F: FnOnce(&CleanupMemory) -> Result + Send + 'static, + { + Self::start_with_options(memory, binding, admission, factory, interval, false, None) + } + + fn start_with_options( + memory: CleanupMemory, + binding: TargetBinding, + admission: A, + factory: F, + interval: Duration, + initial_confirmation: bool, + initial_source: Option>, + ) -> Result + where + D: MaintenanceDriver, + F: FnOnce(&CleanupMemory) -> Result + Send + 'static, + { + // The account predates every control allocation. Fixed copy snapshots + // avoid an uncharged protocol DTO clone on health's request thread. + let arc_layout = Layout::new::<[usize; 2]>() + .extend(Layout::new::>()) + .map_err(|_| StartError::ResourceLimit(ResourceLimit::ArithmeticOverflow))? + .0 + .pad_to_align(); + // Box the factory before spawn, so over-alignment cannot multiply + // through std's by-value startup/catch frames on the bounded stack. + // Charge that actual heap layout plus the fixed pointer capture layout. + let factory_layout = Layout::new::(); + let capture_layout = Layout::new::>>() + .extend(Layout::new::>()) + .map_err(|_| StartError::ResourceLimit(ResourceLimit::ArithmeticOverflow))? + .0 + .pad_to_align(); + let charge = arc_layout + .size() + .checked_add(THREAD_STACK) + .and_then(|n| n.checked_add(THREAD_STARTUP_ALLOWANCE)) + .and_then(|n| n.checked_add(factory_layout.size())) + .and_then(|n| n.checked_add(capture_layout.size())) + .and_then(|n| n.checked_add(capture_layout.align() - 1)) + .and_then(|n| n.checked_add(std::mem::size_of::())) + .and_then(|n| n.checked_add(std::mem::size_of::>>())) + .and_then(|n| n.checked_add(std::mem::size_of::())) + .ok_or(StartError::ResourceLimit(ResourceLimit::ArithmeticOverflow))?; + let reservation = memory.reserve(charge).map_err(StartError::ResourceLimit)?; + let shared = Arc::new(Shared { + control: Mutex::new(Control { + binding, + generation: 0, + confirmation: initial_confirmation, + confirmation_source: initial_source, + claimed_source: None, + capturing_source: false, + source_payloads_parked: false, + stopping: false, + scheduler: MaintenanceScheduler::new(interval), + snapshot: DriverSnapshot { + binding, + generation: 0, + requested: false, + pending: true, + running: false, + liveness: DriverLiveness::Running, + last_attempt: None, + failure: None, + }, + }), + wake: Condvar::new(), + admission, + memory, + _reservation: reservation, + }); + let worker = Arc::clone(&shared); + let factory = Box::new(factory); + let thread = spawn_worker(move || { + // Declared first: runs last during normal return or unwinding, + // after driver/factory/work and caught panic-payload destruction. + let _exit = WorkerExit { shared: &worker }; + let result = catch_unwind(AssertUnwindSafe(|| { + match prepare_driver_boxed(factory, &worker.memory) { + Ok(mut driver) => { + let execution = catch_unwind(AssertUnwindSafe(|| { + run_driver(&worker, &mut *driver); + })); + if execution.is_err() { + fail_worker(&worker, DriverError::Panicked); + } + // Catch execution separately so driver destruction is + // not itself performed during an existing unwind. + drop(execution); + // Driver destruction can block or panic. Remain Stopping + // until it, factory captures and all Work are disposed. + drop(driver); + } + Err(error) => fail_worker(&worker, error), + } + })); + if result.is_err() { + fail_worker(&worker, DriverError::Panicked); + } + drop(result); + }) + .map_err(|_| StartError::SpawnFailed)?; + Ok(Self { + handle: MaintenanceHandle { shared }, + thread: Some(thread), + }) + } + + pub(crate) fn handle(&self) -> MaintenanceHandle { + self.handle.clone() + } + + pub(crate) fn request_stop(&self) { + // Dispatcher lock order never overlaps the mailbox. Stop winning either + // actual admission or the later mailbox gate prevents new storage work. + self.handle.shared.admission.stop_admission(); + let mut control = self + .handle + .shared + .control + .lock() + .unwrap_or_else(|e| e.into_inner()); + control.stopping = true; + control.confirmation = false; + control.confirmation_source = None; + if control.snapshot.liveness == DriverLiveness::Running { + control.snapshot.liveness = DriverLiveness::Stopping; + } + self.handle.shared.wake.notify_all(); + } + + pub(crate) fn stop_and_join(&mut self) { + self.request_stop(); + // No shared mutex (including a join-handle mutex) is held while joining. + if let Some(thread) = self.thread.take() { + let _ = thread.join(); + } + } +} +impl Drop for MaintenanceRuntime { + fn drop(&mut self) { + self.stop_and_join(); + } +} + +#[cfg(test)] +thread_local! { + static INJECT_SPAWN_FAILURE: std::cell::Cell = const { std::cell::Cell::new(false) }; +} + +fn spawn_worker(body: F) -> std::io::Result> { + #[cfg(test)] + if INJECT_SPAWN_FAILURE.with(|flag| flag.replace(false)) { + // Exercise the same closure-disposal ownership as Builder::spawn's Err. + drop(body); + return Err(std::io::Error::other("fixture thread creation failure")); + } + thread::Builder::new().stack_size(THREAD_STACK).spawn(body) +} + +// This finalizer owns no payload and performs only fixed control updates. In +// particular, dropping a caught panic payload may itself unwind past the outer +// catch; terminal publication must still happen without catching/retrying or +// leaking that payload. All bulk owners are declared/consumed after this guard. +struct WorkerExit<'a, A> { + shared: &'a Shared, +} +impl Drop for WorkerExit<'_, A> { + fn drop(&mut self) { + let mut control = self + .shared + .control + .lock() + .unwrap_or_else(|e| e.into_inner()); + control.stopping = true; + control.confirmation = false; + control.confirmation_source = None; + control.claimed_source = None; + control.snapshot.running = false; + if thread::panicking() { + control.snapshot.failure = Some(DriverError::Panicked); + control.snapshot.pending = true; + } + control.snapshot.liveness = if control.snapshot.failure.is_some() { + DriverLiveness::Failed + } else { + DriverLiveness::Stopped + }; + self.shared.wake.notify_all(); + } +} + +fn fail_worker(shared: &Shared, error: DriverError) { + let mut control = shared.control.lock().unwrap_or_else(|e| e.into_inner()); + control.stopping = true; + control.confirmation = false; + control.confirmation_source = None; + control.claimed_source = None; + control.snapshot.running = false; + control.snapshot.pending = true; + // Failure and completed-attempt evidence are separate. A later prepare or + // destructor panic cannot rewrite a prior attempt's binding/result. + control.snapshot.liveness = DriverLiveness::Stopping; + control.snapshot.failure = Some(error); + shared.wake.notify_all(); +} + +/// The startup reservation already includes the exact D heap layout. Keep +/// construction on a separate frame so D is not copied above every work call. +#[inline(never)] +fn prepare_driver_boxed( + factory: Box, + memory: &CleanupMemory, +) -> Result, DriverError> +where + D: MaintenanceDriver, + F: FnOnce(&CleanupMemory) -> Result, +{ + factory(memory).map(Box::new) +} + +fn run_driver(shared: &Shared, driver: &mut D) { + let origin = Instant::now(); + loop { + let (binding, generation, triggers, sequence) = { + let mut control = shared.control.lock().unwrap_or_else(|e| e.into_inner()); + loop { + if control.stopping { + control.snapshot.running = false; + return; + } + if control.capturing_source { + control = shared + .wake + .wait(control) + .unwrap_or_else(|error| error.into_inner()); + continue; + } + let now = origin.elapsed(); + if let Some((triggers, sequence)) = control.scheduler.begin(now) { + control.snapshot.running = true; + break (control.binding, control.generation, triggers, sequence); + } + let wait = control.scheduler.next_due.saturating_sub(now); + control = shared + .wake + .wait_timeout(control, wait) + .unwrap_or_else(|e| e.into_inner()) + .0; + } + }; + let is_cancelled = || { + let control = shared.control.lock().unwrap_or_else(|e| e.into_inner()); + control.stopping || control.generation != generation + }; + let legacy_interest = || { + let control = shared.control.lock().unwrap_or_else(|e| e.into_inner()); + !control.stopping + && control.generation == generation + && control.binding == binding + && control.confirmation + }; + let legacy_metadata = || { + let control = shared.control.lock().unwrap_or_else(|e| e.into_inner()); + if !control.stopping + && control.generation == generation + && control.binding == binding + && control.confirmation + { + Some(( + generation, + control + .confirmation_source + .as_ref() + .map(|owner| owner.facts()), + )) + } else { + None + } + }; + let cancellation = Cancellation { + check: &is_cancelled, + legacy_interest: &legacy_interest, + legacy_metadata: &legacy_metadata, + }; + let prepared_legacy_metadata = legacy_metadata(); + let prepared = driver.prepare(binding, &cancellation, &shared.memory); + let mut panicked = false; + let outcome = match prepared { + Err(_) if cancellation.is_cancelled() => AttemptOutcome::Cancelled, + Err(error) => AttemptOutcome::Failed(error), + Ok(None) if cancellation.is_cancelled() => AttemptOutcome::Cancelled, + Ok(None) => AttemptOutcome::Complete, + Ok(Some(mut work)) => { + // Bulk work lives OUTSIDE this scope and the caught callback. + // Every success/error/unwind drops permit before work disposal. + let requires_confirmation = driver.requires_legacy_confirmation(&work); + let result = { + let mut grant = MutationGrant::start(); + match shared.admission.try_acquire() { + None => Ok(Ok(AttemptOutcome::Deferred)), + Some(_permit) => { + let confirmation = { + let mut control = + shared.control.lock().unwrap_or_else(|e| e.into_inner()); + let current_legacy_metadata = control.confirmation.then(|| { + ( + generation, + control + .confirmation_source + .as_ref() + .map(|owner| owner.facts()), + ) + }); + if control.stopping + || control.generation != generation + || control.binding != binding + || (requires_confirmation + && control.confirmation + && current_legacy_metadata != prepared_legacy_metadata) + { + None + } else { + let confirmed = requires_confirmation + && control.claimed_source.is_none() + && std::mem::take(&mut control.confirmation); + let source = if confirmed { + control.claimed_source = control.confirmation_source.take(); + control.claimed_source.as_ref().map(|owner| owner.facts()) + } else { + None + }; + Some((confirmed, source)) + } + }; + match confirmation { + None => Ok(Ok(AttemptOutcome::Deferred)), + Some((confirmed, source)) if grant.check().is_ok() => { + let confirmation = + confirmed.then_some(LegacyQuiescenceConfirmation { + binding: &binding, + generation: &generation, + source, + _same_thread: std::marker::PhantomData, + }); + catch_unwind(AssertUnwindSafe(|| { + driver.mutate(&work, &mut grant, confirmation.as_ref()) + })) + } + Some(_) => Ok(Ok(AttemptOutcome::Deferred)), + } + } + } + }; + // No permit or S2 authority is live here, even on panic/Err. + // The request's payload pins were parked in a separate fixed + // slot through the callback; dispose them before source replay. + let source = shared + .control + .lock() + .unwrap_or_else(|error| error.into_inner()) + .claimed_source + .take(); + drop(source); + let outcome = match result { + Ok(Ok(outcome)) => outcome, + Ok(Err(error)) => AttemptOutcome::Failed(error), + Err(_) => { + panicked = true; + AttemptOutcome::Failed(DriverError::Panicked) + } + }; + let outcome = if panicked { + outcome + } else { + match catch_unwind(AssertUnwindSafe(|| { + driver.finish(&mut work, outcome, &cancellation, &shared.memory) + })) { + Ok(Ok(outcome)) => outcome, + Ok(Err(error)) => AttemptOutcome::Failed(error), + Err(_) => { + panicked = true; + AttemptOutcome::Failed(DriverError::Panicked) + } + } + }; + drop(work); + if panicked { + outcome + } else { + match catch_unwind(AssertUnwindSafe(|| { + driver.after_disposal(outcome, &cancellation, &shared.memory) + })) { + Ok(Ok(outcome)) => outcome, + Ok(Err(error)) => AttemptOutcome::Failed(error), + Err(_) => { + panicked = true; + AttemptOutcome::Failed(DriverError::Panicked) + } + } + } + } + }; + driver.completed(outcome); + let desired_binding = driver.desired_binding(); + let source_payloads_parked = driver.source_payloads_parked(); + { + let mut control = shared.control.lock().unwrap_or_else(|e| e.into_inner()); + control.snapshot.running = false; + control.source_payloads_parked = source_payloads_parked; + if control.generation == generation { + control.snapshot.last_attempt = Some(AttemptRecord { + binding, + generation, + request_sequence: sequence, + triggers, + outcome, + }); + } + let completed_at = origin.elapsed(); + control.scheduler.complete(completed_at, outcome); + if let Some(binding) = desired_binding + .filter(|binding| control.generation == generation && *binding != control.binding) + { + if let Some(generation) = control.generation.checked_add(1) { + control.binding = binding; + control.generation = generation; + control.confirmation = false; + control.confirmation_source = None; + control.snapshot.binding = binding; + control.snapshot.generation = generation; + control.scheduler.request(Trigger::Startup); + // Resolving a placeholder binding completed preparation, + // not this new target's promised startup pass. Only this + // worker-owned handoff bypasses the old target's cooldown. + control.scheduler.next_due = completed_at; + } else { + control.stopping = true; + control.snapshot.failure = Some(DriverError::Preparation); + } + } + if control.confirmation { + control.scheduler.pending |= Trigger::Continuation as u8; + } + control.snapshot.pending = control.scheduler.pending != 0; + shared.wake.notify_all(); + } + if panicked { + fail_worker(shared, DriverError::Panicked); + return; + } + } +} + +/// Integration-test adapter: preparation stays offslot on the joined worker; +/// the callback receives a token only after real enqueue, admission and claim. +/// Callers qualifying dispatcher behavior supply the actual ExecutionDispatcher. +#[cfg(test)] +pub(crate) fn with_claimed_legacy_confirmation( + source_target: Option<&super::inventory::MaintenanceTarget>, + admission: A, + prepare: P, + callback: C, +) -> DriverSnapshot +where + A: MaintenanceAdmission, + W: 'static, + P: FnMut( + &CleanupMemory, + Option<(u64, Option)>, + Option, + ) -> Result<(TargetBinding, W), DriverError> + + Send + + 'static, + C: for<'claim> FnMut( + &W, + Option<&LegacyQuiescenceConfirmation<'claim>>, + &mut MutationGrant, + ) -> Result + + Send + + 'static, +{ + struct ConfirmedDriver { + prepare: P, + callback: C, + binding: Option, + parked: Option, + continuing: bool, + complete: bool, + } + impl MaintenanceDriver for ConfirmedDriver + where + P: FnMut( + &CleanupMemory, + Option<(u64, Option)>, + Option, + ) -> Result<(TargetBinding, W), DriverError>, + C: for<'claim> FnMut( + &W, + Option<&LegacyQuiescenceConfirmation<'claim>>, + &mut MutationGrant, + ) -> Result, + { + type Work = Option; + fn desired_binding(&self) -> Option { + self.binding + } + fn prepare( + &mut self, + binding: TargetBinding, + cancellation: &Cancellation<'_>, + memory: &CleanupMemory, + ) -> Result, DriverError> { + if self.complete { + return Ok(None); + } + let request = cancellation.legacy_confirmation_metadata(); + let (actual, work) = (self.prepare)(memory, request, self.parked.take())?; + self.binding = Some(actual); + // The first observation establishes the genuine target binding. + // No absent advisory request can be promoted into a claimed token. + if actual != binding || (request.is_none() && !self.continuing) { + drop(work); + return Ok(None); + } + Ok(Some(Some(work))) + } + fn requires_legacy_confirmation(&self, _: &Self::Work) -> bool { + !self.continuing + } + fn mutate( + &mut self, + work: &Self::Work, + grant: &mut MutationGrant, + confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + if !self.continuing && confirmation.is_none() { + return Ok(AttemptOutcome::Deferred); + } + (self.callback)( + work.as_ref().ok_or(DriverError::Preparation)?, + confirmation, + grant, + ) + } + fn finish( + &mut self, + work: &mut Self::Work, + outcome: AttemptOutcome, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result { + if matches!(outcome, AttemptOutcome::Pending | AttemptOutcome::Deferred) { + self.parked = work.take(); + } + if outcome == AttemptOutcome::Pending { + self.continuing = true; + } + self.complete = outcome == AttemptOutcome::Complete; + Ok(outcome) + } + } + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let source = source_target.map(|target| { + let source = + super::maintenance_admission::prepare_legacy_confirmation_source(target, &memory) + .unwrap(); + MemoryBox::new(source, &memory).unwrap() + }); + let mut runtime = MaintenanceRuntime::start_with_interval( + memory, + TargetBinding([0; 32]), + admission, + move |_| { + Ok(ConfirmedDriver:: { + prepare, + callback, + binding: None, + parked: None, + continuing: false, + complete: false, + }) + }, + Duration::from_millis(1), + ) + .unwrap(); + let handle = runtime.handle(); + let prepared = handle.wait_for_first_attempt(); + let mut attempted = + handle.request_and_wait_with_source(Trigger::Health, Some(prepared.binding), source); + // These are fresh dispatcher callbacks, with no replacement TRUE request. + // The real accepted source capsule alone authorizes each continuation. + for _ in 0..64 { + if attempted.last_attempt.is_some_and(|attempt| { + attempt.binding == attempted.binding + && attempt.generation == attempted.generation + && matches!( + attempt.outcome, + AttemptOutcome::Failed(_) | AttemptOutcome::Cancelled + ) + }) || (attempted.last_attempt.is_some_and(|attempt| { + attempt.binding == attempted.binding + && attempt.generation == attempted.generation + && attempt.outcome == AttemptOutcome::Complete + }) && !attempted.pending) + { + break; + } + attempted = handle.request_and_wait(Trigger::Continuation, None); + } + runtime.stop_and_join(); + attempted +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn queued_confirmation_keeps_request_time_source_after_pair_replacement() { + let root = std::env::temp_dir().join(format!( + "confirmation-source-version-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + let state = root.join(".codebaseGraph"); + std::fs::create_dir_all(&state).unwrap(); + let target = super::super::inventory::MaintenanceTarget { + repository_root: root.clone(), + state_root: state.clone(), + storage_root: None, + direct_pair: None, + config_path: None, + }; + let pair = super::super::inventory::default_direct(&target); + std::fs::write(&pair.database, b"old database incarnation").unwrap(); + std::fs::write(&pair.manifest, b"old manifest incarnation").unwrap(); + let request_target = target.clone(); + let current_target = target.clone(); + let prepare_pair = pair.clone(); + let snapshot = with_claimed_legacy_confirmation( + Some(&request_target), + Arc::new(crate::coordinator::ExecutionDispatcher::default()), + move |memory,_,_:Option| { + // A real new pair is published AFTER the operator source pins + // were captured, before that queued demand can be claimed. + let database = prepare_pair.database.with_extension("replacement"); + let manifest = prepare_pair.manifest.with_extension("replacement"); + std::fs::write(&database, b"fresh database incarnation").unwrap(); + std::fs::write(&manifest, b"fresh manifest incarnation").unwrap(); + std::fs::rename(database, &prepare_pair.database).unwrap(); + std::fs::rename(manifest, &prepare_pair.manifest).unwrap(); + let current=super::super::maintenance_admission::capture_legacy_confirmation_source(¤t_target,memory).unwrap(); + Ok((TargetBinding([73;32]),current)) + }, + move |current,token,_| { + let token=token.expect("the dispatcher must claim the actual queued request"); + assert!(token.source().is_some());assert_ne!(token.source(),Some(current)); + Ok(AttemptOutcome::Complete) + }, + ); + assert_eq!( + snapshot.last_attempt.unwrap().outcome, + AttemptOutcome::Complete + ); + std::fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn dc_scheduler_sustained_requests_do_not_slide_due_or_lose_inflight_demand() { + let mut scheduler = MaintenanceScheduler::new(DEFAULT_INTERVAL); + assert_eq!(scheduler.begin(Duration::ZERO), Some((1, 0))); + for second in 1..=60 { + scheduler.request(Trigger::Health); + if second < 60 { + assert!(scheduler.begin(Duration::from_secs(second)).is_none()); + } + } + let (bits, sequence) = scheduler.begin(Duration::from_secs(60)).unwrap(); + assert_eq!(bits, Trigger::Health as u8); + assert_eq!(sequence, 60); + // A request during preparation survives completion of this attempt. + scheduler.request(Trigger::Completion); + scheduler.complete(Duration::from_secs(61), AttemptOutcome::Complete); + assert!(scheduler.begin(Duration::from_secs(120)).is_none()); + assert_eq!(scheduler.begin(Duration::from_secs(121)), Some((2, 61))); + } + + #[test] + fn dc_scheduler_idle_busy_error_and_continuation_are_paced() { + let mut scheduler = MaintenanceScheduler::new(DEFAULT_INTERVAL); + for (step, outcome) in [ + AttemptOutcome::Complete, + AttemptOutcome::Deferred, + AttemptOutcome::Failed(DriverError::Preparation), + AttemptOutcome::Pending, + ] + .into_iter() + .enumerate() + { + let now = Duration::from_secs(step as u64 * 60); + assert!(scheduler.begin(now).is_some()); + scheduler.complete(now, outcome); + assert!(scheduler.begin(now + Duration::from_secs(59)).is_none()); + } + assert_eq!( + scheduler.begin(Duration::from_secs(240)).unwrap().0, + Trigger::Continuation as u8 + ); + } + + struct NoAdmission; + impl MaintenanceAdmission for NoAdmission { + type Permit = (); + fn try_acquire(&self) -> Option<()> { + Some(()) + } + fn stop_admission(&self) {} + } + struct StartupBindingDriver { + desired: TargetBinding, + prepares: usize, + callbacks: std::sync::mpsc::Sender<(TargetBinding, usize)>, + } + impl MaintenanceDriver for StartupBindingDriver { + type Work = TargetBinding; + fn desired_binding(&self) -> Option { + Some(self.desired) + } + fn prepare( + &mut self, + binding: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + self.prepares += 1; + if self.prepares == 1 { + self.desired = TargetBinding([82; 32]); + return Ok(None); + } + Ok(Some(binding)) + } + fn mutate( + &mut self, + work: &TargetBinding, + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + self.callbacks.send((*work, self.prepares)).unwrap(); + Ok(AttemptOutcome::Complete) + } + } + + #[test] + fn dc_driver_resolved_startup_binding_runs_actual_dispatcher_without_default_cooldown() { + let (callbacks, received) = std::sync::mpsc::channel(); + // Use the actual 60-second runtime interval and dispatcher. The first + // callback resolves binding only; the second must enter real admission + // promptly, while ordinary subsequent Health requests remain paced. + let mut runtime = MaintenanceRuntime::start( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([0; 32]), + Arc::new(crate::coordinator::ExecutionDispatcher::default()), + move |_| { + Ok(StartupBindingDriver { + desired: TargetBinding([0; 32]), + prepares: 0, + callbacks, + }) + }, + ) + .unwrap(); + let callback = received.recv_timeout(Duration::from_secs(3)); + let health = if callback.is_ok() { + runtime.handle().request(Trigger::Health, None); + Some(received.recv_timeout(Duration::from_millis(100))) + } else { + None + }; + // Stop before asserting so a failed handoff never leaves a worker + // sleeping through the default cooldown or blocks cleanup on failure. + runtime.stop_and_join(); + assert_eq!(callback.unwrap(), (TargetBinding([82; 32]), 2)); + assert!(matches!( + health, + Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) + )); + } + + struct PausedDriver { + entered: std::sync::mpsc::Sender<()>, + release: std::sync::mpsc::Receiver<()>, + _retained: MemoryReservation, + } + impl MaintenanceDriver for PausedDriver { + type Work = (); + fn prepare( + &mut self, + _: TargetBinding, + cancellation: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + self.entered.send(()).unwrap(); + self.release.recv().unwrap(); + assert!(cancellation.is_cancelled()); + Ok(None) + } + fn mutate( + &mut self, + _: &(), + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + unreachable!() + } + } + + #[test] + fn dc_driver_one_account_covers_rebind_snapshots_and_retained_handle_lifetime() { + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let floor = memory.used(); + let (entered, enter_rx) = std::sync::mpsc::channel(); + let (release_tx, release) = std::sync::mpsc::channel(); + let mut runtime = MaintenanceRuntime::start( + memory.clone(), + TargetBinding([1; 32]), + NoAdmission, + move |memory| { + Ok(PausedDriver { + entered, + release, + _retained: memory.reserve(512).unwrap(), + }) + }, + ) + .unwrap(); + enter_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + let handle = runtime.handle(); + let used = memory.used(); + for value in 2..=100 { + let clone = handle.clone(); + clone.rebind(TargetBinding([value; 32])); + clone.request(Trigger::Health, None); + assert_eq!(clone.snapshot().binding, TargetBinding([value; 32])); + assert_eq!(memory.used(), used); // fixed-copy control, no DTO allocations + } + runtime.request_stop(); + release_tx.send(()).unwrap(); + runtime.stop_and_join(); + assert_eq!(memory.used(), used - 512); // driver disposed, mailbox still alive + drop(runtime); + assert!(memory.used() > floor); // a handle does not escape its account + drop(handle); + assert_eq!(memory.used(), floor); + assert!(memory.peak() <= memory.limit()); + } + + struct CompletionWork { + disposing: std::sync::mpsc::Sender<()>, + release: std::sync::mpsc::Receiver<()>, + } + impl Drop for CompletionWork { + fn drop(&mut self) { + self.disposing.send(()).unwrap(); + self.release.recv().unwrap(); + } + } + struct CompletionDriver(Option); + impl MaintenanceDriver for CompletionDriver { + type Work = CompletionWork; + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + Ok(self.0.take()) + } + fn mutate( + &mut self, + _: &CompletionWork, + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + Ok(AttemptOutcome::Complete) + } + } + + #[test] + fn dc_driver_transient_wait_observes_completed_disposal_and_factory_failure() { + let (disposing, disposing_rx) = std::sync::mpsc::channel(); + let (release_tx, release) = std::sync::mpsc::channel(); + let mut runtime = MaintenanceRuntime::start( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + NoAdmission, + move |_| { + Ok(CompletionDriver(Some(CompletionWork { + disposing, + release, + }))) + }, + ) + .unwrap(); + disposing_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + let handle = runtime.handle(); + assert!(handle.snapshot().last_attempt.is_none()); + let waiter = thread::spawn(move || handle.wait_for_first_attempt()); + release_tx.send(()).unwrap(); + let snapshot = waiter.join().unwrap(); + assert_eq!( + snapshot.last_attempt.unwrap().outcome, + AttemptOutcome::Complete + ); + assert!(!snapshot.running); + runtime.stop_and_join(); + + let mut failed = MaintenanceRuntime::start::( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([2; 32]), + NoAdmission, + |_| Err(DriverError::Panicked), + ) + .unwrap(); + let snapshot = failed.handle().wait_for_first_attempt(); + assert_eq!(snapshot.liveness, DriverLiveness::Failed); + assert_eq!(snapshot.failure, Some(DriverError::Panicked)); + assert!(snapshot.last_attempt.is_none()); + failed.stop_and_join(); + } + + struct ConfirmDriver { + prepared: std::sync::mpsc::Sender<()>, + prepare_release: std::sync::mpsc::Receiver<()>, + mutated: std::sync::mpsc::Sender, + mutate_release: std::sync::mpsc::Receiver<()>, + } + impl MaintenanceDriver for ConfirmDriver { + type Work = (); + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + self.prepared.send(()).unwrap(); + self.prepare_release + .recv_timeout(Duration::from_secs(5)) + .unwrap(); + Ok(Some(())) + } + fn requires_legacy_confirmation(&self, _: &()) -> bool { + true + } + fn mutate( + &mut self, + _: &(), + _: &mut MutationGrant, + confirmation: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + if let Some(confirmation) = confirmation { + assert_eq!(confirmation.binding_digest(), [1; 32]); + assert_eq!(confirmation.generation(), 0); + } + self.mutated.send(confirmation.is_some()).unwrap(); + self.mutate_release + .recv_timeout(Duration::from_secs(5)) + .unwrap(); + Ok(AttemptOutcome::Complete) + } + } + + #[test] + fn dc_driver_confirmation_arriving_inflight_survives_prior_completion() { + let (prepared, prepare_rx) = std::sync::mpsc::channel(); + let (prepare_tx, prepare_release) = std::sync::mpsc::channel(); + let (mutated, mutate_rx) = std::sync::mpsc::channel(); + let (mutate_tx, mutate_release) = std::sync::mpsc::channel(); + let mut runtime = MaintenanceRuntime::start_with_interval( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + NoAdmission, + move |_| { + Ok(ConfirmDriver { + prepared, + prepare_release, + mutated, + mutate_release, + }) + }, + Duration::from_millis(1), + ) + .unwrap(); + let handle = runtime.handle(); + prepare_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + handle.request(Trigger::Health, Some(TargetBinding([1; 32]))); + prepare_tx.send(()).unwrap(); + // A new confirmation arrived after preparation captured its source + // version. Preserve it for fresh preparation instead of claiming stale + // work; the deferred callback must not consume that demand. + prepare_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + assert_eq!( + handle.snapshot().last_attempt.unwrap().outcome, + AttemptOutcome::Deferred + ); + assert!(handle.snapshot().pending); + assert!(matches!( + mutate_rx.try_recv(), + Err(std::sync::mpsc::TryRecvError::Empty) + )); + prepare_tx.send(()).unwrap(); + assert!(mutate_rx.recv_timeout(Duration::from_secs(5)).unwrap()); + // Gate already took the first demand. New true belongs to the next gate. + handle.request(Trigger::Health, Some(TargetBinding([1; 32]))); + handle.request(Trigger::Health, None); + mutate_tx.send(()).unwrap(); + prepare_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + assert!(handle.snapshot().pending); + prepare_tx.send(()).unwrap(); + assert!(mutate_rx.recv_timeout(Duration::from_secs(5)).unwrap()); + runtime.request_stop(); + mutate_tx.send(()).unwrap(); + runtime.stop_and_join(); + assert_eq!( + handle.request(Trigger::Health, Some(TargetBinding([1; 32]))), + RequestDisposition::Stopped + ); + } +} + +#[cfg(test)] +mod spawn_failure_test { + use super::*; + struct Admission; + impl MaintenanceAdmission for Admission { + type Permit = (); + fn try_acquire(&self) -> Option<()> { + unreachable!() + } + fn stop_admission(&self) {} + } + struct NeverCreated; + impl MaintenanceDriver for NeverCreated { + type Work = (); + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + unreachable!() + } + fn mutate( + &mut self, + _: &(), + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + unreachable!() + } + } + #[test] + fn dc_driver_spawn_failure_releases_all_startup_reservations_without_factory() { + let memory = CleanupMemory::new(1024 * 1024).unwrap(); + let floor = memory.used(); + INJECT_SPAWN_FAILURE.with(|flag| flag.set(true)); + let result = MaintenanceRuntime::start::( + memory.clone(), + TargetBinding([1; 32]), + Admission, + |_| unreachable!("spawn failed before factory"), + ); + assert!(matches!(result, Err(StartError::SpawnFailed))); + assert_eq!(memory.used(), floor); + } +} + +#[cfg(test)] +mod independent_review_probes { + use super::*; + use std::sync::mpsc; + struct Admission; + impl MaintenanceAdmission for Admission { + type Permit = (); + fn try_acquire(&self) -> Option<()> { + Some(()) + } + fn stop_admission(&self) {} + } + struct PreparePanic { + attempts: usize, + second: mpsc::Sender<()>, + release: mpsc::Receiver<()>, + } + impl MaintenanceDriver for PreparePanic { + type Work = (); + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + self.attempts += 1; + if self.attempts == 1 { + return Ok(None); + } + self.second.send(()).unwrap(); + self.release.recv().unwrap(); + panic!("independent prepare failure"); + } + fn mutate( + &mut self, + _: &(), + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + unreachable!() + } + } + #[test] + fn independent_outer_panic_preserves_prior_success() { + let (second, second_rx) = mpsc::channel(); + let (release, release_rx) = mpsc::channel(); + let mut runtime = MaintenanceRuntime::start_with_interval( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + Admission, + move |_| { + Ok(PreparePanic { + attempts: 0, + second, + release: release_rx, + }) + }, + Duration::from_millis(1), + ) + .unwrap(); + let handle = runtime.handle(); + second_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + let before = handle.snapshot(); + assert_eq!( + before.last_attempt.unwrap().outcome, + AttemptOutcome::Complete + ); + handle.rebind(TargetBinding([2; 32])); + release.send(()).unwrap(); + let deadline = Instant::now() + Duration::from_secs(5); + while handle.snapshot().liveness != DriverLiveness::Failed { + assert!(Instant::now() < deadline); + thread::yield_now(); + } + let after = handle.snapshot(); + runtime.stop_and_join(); + println!( + "prior_success_before={before:?}\nafter_different_generation_prepare_panic={after:?}" + ); + assert_eq!(after.last_attempt.unwrap().binding, TargetBinding([1; 32])); + assert_eq!( + after.last_attempt.unwrap().outcome, + AttemptOutcome::Complete + ); + assert_eq!(before.last_attempt, after.last_attempt); + } + struct SlowDriverDrop { + prepared: mpsc::Sender<()>, + prepare_release: mpsc::Receiver<()>, + drop_enter: mpsc::Sender<()>, + drop_release: mpsc::Receiver<()>, + } + impl MaintenanceDriver for SlowDriverDrop { + type Work = (); + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + self.prepared.send(()).unwrap(); + self.prepare_release.recv().unwrap(); + Ok(None) + } + fn mutate( + &mut self, + _: &(), + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + unreachable!() + } + } + impl Drop for SlowDriverDrop { + fn drop(&mut self) { + self.drop_enter.send(()).unwrap(); + self.drop_release.recv().unwrap(); + } + } + #[test] + fn independent_stopping_lasts_until_driver_disposal_finishes() { + let (prepared, prepared_rx) = mpsc::channel(); + let (prepare_release, prepare_rx) = mpsc::channel(); + let (drop_enter, drop_enter_rx) = mpsc::channel(); + let (drop_release, drop_release_rx) = mpsc::channel(); + let mut runtime = MaintenanceRuntime::start( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + Admission, + move |_| { + Ok(SlowDriverDrop { + prepared, + prepare_release: prepare_rx, + drop_enter, + drop_release: drop_release_rx, + }) + }, + ) + .unwrap(); + let handle = runtime.handle(); + prepared_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + runtime.request_stop(); + prepare_release.send(()).unwrap(); + drop_enter_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + let observed = handle.snapshot(); + let (joined, joined_rx) = mpsc::channel(); + let joiner = thread::spawn(move || { + runtime.stop_and_join(); + joined.send(()).unwrap(); + }); + assert!(joined_rx.try_recv().is_err()); + println!("during_blocked_driver_drop={observed:?}; join_is_pending=true"); + drop_release.send(()).unwrap(); + joined_rx.recv_timeout(Duration::from_secs(5)).unwrap(); + joiner.join().unwrap(); + assert_eq!(observed.liveness, DriverLiveness::Stopping); + } + + struct DropPanic; + impl MaintenanceDriver for DropPanic { + type Work = (); + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + Ok(None) + } + fn mutate( + &mut self, + _: &(), + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + unreachable!() + } + } + impl Drop for DropPanic { + fn drop(&mut self) { + panic!("fixture driver destructor"); + } + } + + #[test] + fn dc_driver_destructor_panic_preserves_success_and_runtime_drop_can_join_while_unwinding() { + let runtime = MaintenanceRuntime::start( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + Admission, + |_| Ok(DropPanic), + ) + .unwrap(); + let handle = runtime.handle(); + let deadline = Instant::now() + Duration::from_secs(5); + while handle.snapshot().last_attempt.is_none() { + assert!(Instant::now() < deadline); + thread::yield_now(); + } + let successful = handle.snapshot().last_attempt; + let unwind = catch_unwind(AssertUnwindSafe(move || { + let _runtime = runtime; + panic!("fixture caller unwinds through Runtime::drop"); + })); + assert!(unwind.is_err()); + assert_eq!(handle.snapshot().liveness, DriverLiveness::Failed); + assert_eq!(handle.snapshot().failure, Some(DriverError::Panicked)); + assert_eq!(handle.snapshot().last_attempt, successful); + assert_eq!(successful.unwrap().outcome, AttemptOutcome::Complete); + } + + #[test] + fn dc_driver_factory_panic_records_failure_without_inventing_attempt() { + let mut runtime = MaintenanceRuntime::start::( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + Admission, + |_| panic!("fixture factory before preparation"), + ) + .unwrap(); + let handle = runtime.handle(); + let deadline = Instant::now() + Duration::from_secs(5); + while handle.snapshot().liveness != DriverLiveness::Failed { + assert!(Instant::now() < deadline); + thread::yield_now(); + } + runtime.stop_and_join(); + assert_eq!(handle.snapshot().last_attempt, None); + assert_eq!(handle.snapshot().failure, Some(DriverError::Panicked)); + } +} + +#[cfg(test)] +pub(crate) mod allocation_boundary_test { + // Execute the actual integrated scheduler in an isolated child of this + // very test binary, with the same allocator and source-ticket types. This + // keeps unrelated parallel tests outside the allocation observation. + #[test] + fn dc_driver_real_requested_allocations_fit_tight_ceiling_and_aligned_factory() { + const CHILD: &str = "CODEBASE_GRAPH_ALLOCATION_PROBE_CHILD"; + if std::env::var_os(CHILD).is_some() { + run_probe(); + return; + } + let binary = std::env::current_exe().unwrap(); + let output = + std::env::temp_dir().join(format!("cleanup-driver-allocation-{}", std::process::id())); + std::fs::create_dir(&output).unwrap(); + std::fs::write( + output.join("binary-path.txt"), + binary.as_os_str().as_encoded_bytes(), + ) + .unwrap(); + let run = std::process::Command::new(&binary) + .args([ + "--exact", + "storage::maintenance_scheduler::allocation_boundary_test::dc_driver_real_requested_allocations_fit_tight_ceiling_and_aligned_factory", + "--nocapture", + "--test-threads=1", + ]) + .env(CHILD, "1") + .output() + .unwrap(); + std::fs::write(output.join("probe.stdout"), &run.stdout).unwrap(); + std::fs::write(output.join("probe.stderr"), &run.stderr).unwrap(); + assert!( + run.status.success(), + "{}", + String::from_utf8_lossy(&run.stderr) + ); + let stdout = String::from_utf8_lossy(&run.stdout); + assert!(stdout.contains("1 passed; 0 failed"), "{stdout}"); + assert_eq!( + stdout + .matches("one-byte-under rejected before allocation") + .count(), + 2, + "{stdout}" + ); + println!("allocation probe: {}\n{stdout}", output.display()); + } + use super::*; + use std::alloc::{GlobalAlloc, Layout}; + use std::cell::UnsafeCell; + use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + #[derive(Copy, Clone)] + struct Entry { + ptr: usize, + size: usize, + live: bool, + } + const EMPTY: Entry = Entry { + ptr: 0, + size: 0, + live: false, + }; + struct Record(UnsafeCell<[Entry; 128]>); + unsafe impl Sync for Record {} + static RECORD: Record = Record(UnsafeCell::new([EMPTY; 128])); + static LOCK: AtomicBool = AtomicBool::new(false); + static TRACK: AtomicBool = AtomicBool::new(false); + static LIVE: AtomicUsize = AtomicUsize::new(0); + static PEAK: AtomicUsize = AtomicUsize::new(0); + static COUNT: AtomicUsize = AtomicUsize::new(0); + static PREPARED: AtomicBool = AtomicBool::new(false); + fn lock() { + while LOCK + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + std::hint::spin_loop(); + } + } + unsafe fn note_alloc(p: *mut u8, l: Layout) { + if p.is_null() || !TRACK.load(Ordering::Relaxed) { + return; + } + lock(); + let i = COUNT.fetch_add(1, Ordering::Relaxed); + if i < 128 { + (*RECORD.0.get())[i] = Entry { + ptr: p as usize, + size: l.size(), + live: true, + }; + } + let live = LIVE.fetch_add(l.size(), Ordering::Relaxed) + l.size(); + PEAK.fetch_max(live, Ordering::Relaxed); + LOCK.store(false, Ordering::Release); + } + unsafe fn note_dealloc(p: *mut u8) { + if !TRACK.load(Ordering::Relaxed) && LIVE.load(Ordering::Relaxed) == 0 { + return; + } + lock(); + for e in (*RECORD.0.get()).iter_mut() { + if e.live && e.ptr == p as usize { + e.live = false; + LIVE.fetch_sub(e.size, Ordering::Relaxed); + break; + } + } + LOCK.store(false, Ordering::Release); + } + pub(crate) struct Allocator; + unsafe impl GlobalAlloc for Allocator { + unsafe fn alloc(&self, l: Layout) -> *mut u8 { + let p = mimalloc::MiMalloc.alloc(l); + note_alloc(p, l); + p + } + unsafe fn alloc_zeroed(&self, l: Layout) -> *mut u8 { + let p = mimalloc::MiMalloc.alloc_zeroed(l); + note_alloc(p, l); + p + } + unsafe fn dealloc(&self, p: *mut u8, l: Layout) { + note_dealloc(p); + mimalloc::MiMalloc.dealloc(p, l); + } + unsafe fn realloc(&self, p: *mut u8, l: Layout, n: usize) -> *mut u8 { + let q = mimalloc::MiMalloc.realloc(p, l, n); + if !q.is_null() { + note_dealloc(p); + note_alloc(q, Layout::from_size_align_unchecked(n, l.align())); + } + q + } + } + struct Admission; + impl MaintenanceAdmission for Admission { + type Permit = (); + fn try_acquire(&self) -> Option<()> { + Some(()) + } + fn stop_admission(&self) {} + } + struct Driver; + impl MaintenanceDriver for Driver { + type Work = (); + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + PREPARED.store(true, Ordering::Release); + Ok(None) + } + fn mutate( + &mut self, + _: &(), + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + unreachable!() + } + } + + fn reset() { + assert_eq!(LIVE.load(Ordering::Relaxed), 0); + lock(); + unsafe { + *RECORD.0.get() = [EMPTY; 128]; + } + LOCK.store(false, Ordering::Release); + COUNT.store(0, Ordering::Relaxed); + PEAK.store(0, Ordering::Relaxed); + PREPARED.store(false, Ordering::Release); + } + fn measure(limit: usize, factory: F) -> (usize, usize, usize) + where + F: Copy + FnOnce(&CleanupMemory) -> Result + Send + 'static, + { + reset(); + let memory = CleanupMemory::new(limit).unwrap(); + let floor = memory.used(); + TRACK.store(true, Ordering::Relaxed); + let mut runtime = + MaintenanceRuntime::start(memory.clone(), TargetBinding([1; 32]), Admission, factory) + .unwrap(); + while !PREPARED.load(Ordering::Acquire) { + std::thread::yield_now(); + } + let charge = memory.used() - floor; + runtime.stop_and_join(); + drop(runtime); + TRACK.store(false, Ordering::Relaxed); + let peak = PEAK.load(Ordering::Relaxed); + assert!( + COUNT.load(Ordering::Relaxed) < 128, + "allocation recorder overflow" + ); + assert_eq!(LIVE.load(Ordering::Relaxed), 0); + assert!( + peak + 262144 <= charge, + "unreserved requested allocation: heap={peak}, stack=262144, charge={charge}" + ); + (floor, charge, peak) + } + fn check(factory: F) + where + F: Copy + FnOnce(&CleanupMemory) -> Result + Send + 'static, + { + let (floor, charge, peak) = measure(1024 * 1024, factory); + let (_, tight, tight_peak) = measure(floor + charge, factory); + assert_eq!(tight, charge); + reset(); + let memory = CleanupMemory::new(floor + charge - 1).unwrap(); + TRACK.store(true, Ordering::Relaxed); + let rejected = + MaintenanceRuntime::start(memory.clone(), TargetBinding([1; 32]), Admission, factory); + TRACK.store(false, Ordering::Relaxed); + assert!(matches!(rejected, Err(StartError::ResourceLimit(_)))); + assert_eq!( + COUNT.load(Ordering::Relaxed), + 0, + "tight rejection allocated before reserve" + ); + assert_eq!(memory.used(), floor); + println!("charge={charge} heap_peak={peak} tight_peak={tight_peak} stack=262144; exact ceiling passed, one-byte-under rejected before allocation"); + } + #[derive(Clone, Copy)] + #[repr(align(4096))] + struct Aligned([u8; 1]); + impl Aligned { + fn value(self) -> u8 { + self.0[0] + } + } + fn run_probe() { + let _caller_thread = std::thread::current(); + check(|_| Ok(Driver)); + let captured = Aligned([7]); + check(move |_| { + assert_eq!(captured.value(), 7); + Ok(Driver) + }); + } +} + +#[cfg(test)] +mod panic_payload_probe { + use super::*; + struct Admission; + impl MaintenanceAdmission for Admission { + type Permit = (); + fn try_acquire(&self) -> Option<()> { + Some(()) + } + fn stop_admission(&self) {} + } + struct Driver; + impl MaintenanceDriver for Driver { + type Work = (); + fn prepare( + &mut self, + _: TargetBinding, + _: &Cancellation<'_>, + _: &CleanupMemory, + ) -> Result, DriverError> { + unreachable!() + } + fn mutate( + &mut self, + _: &(), + _: &mut MutationGrant, + _: Option<&LegacyQuiescenceConfirmation<'_>>, + ) -> Result { + unreachable!() + } + } + struct Payload; + impl Drop for Payload { + fn drop(&mut self) { + panic!("independent payload destructor panic"); + } + } + #[test] + fn independent_panicking_factory_payload_publishes_terminal_failure() { + let mut runtime = MaintenanceRuntime::start::( + CleanupMemory::new(1024 * 1024).unwrap(), + TargetBinding([1; 32]), + Admission, + |_| std::panic::panic_any(Payload), + ) + .unwrap(); + let handle = runtime.handle(); + let deadline = Instant::now() + Duration::from_secs(5); + while !runtime.thread.as_ref().unwrap().is_finished() { + assert!(Instant::now() < deadline); + thread::yield_now(); + } + runtime.stop_and_join(); + let observed = handle.snapshot(); + println!("after_joined_panicking_panic_payload={observed:?}"); + assert_eq!(observed.failure, Some(DriverError::Panicked)); + assert_eq!(observed.liveness, DriverLiveness::Failed); + } +} diff --git a/src/storage/maintenance_validation.rs b/src/storage/maintenance_validation.rs new file mode 100644 index 00000000..620d384f --- /dev/null +++ b/src/storage/maintenance_validation.rs @@ -0,0 +1,3381 @@ +//! Pure validation facts. Nothing here constructs ownership, loader usability, +//! publication or deletion authority. Sources must remain immutable per replay. +use super::maintenance_budget::{CleanupMemory, MemoryReservation, ReservedBytes, ResourceLimit}; +use super::maintenance_json::{JsonFailure, JsonToken, MaintenanceJsonReader, TokenKind}; +use sha2::{Digest, Sha256}; +use std::cell::Cell; +use std::cmp::Ordering; +#[cfg(test)] +use std::fs::File; +use std::io::{self, Read}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ValidationIssue { + Resource(ResourceLimit), + Json(JsonFailure), + InvalidSpan, + Schema, + NonProducer, + Numeric, + RawRowsDiffer, + Overflow, + Io(io::ErrorKind), + #[cfg(not(any(unix, windows)))] + UnsupportedPositionedRead, +} +impl From for ValidationIssue { + fn from(e: ResourceLimit) -> Self { + Self::Resource(e) + } +} +impl From for ValidationIssue { + fn from(e: JsonFailure) -> Self { + Self::Json(e) + } +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub(crate) struct Span { + pub(crate) start: u64, + pub(crate) end: u64, +} +impl Span { + fn len(self) -> Result { + self.end + .checked_sub(self.start) + .ok_or(ValidationIssue::InvalidSpan) + } +} + +/// Read-only random access. Any backing buffers are the caller's responsibility +/// in the SAME account; this interface never owns/copies a whole source. +pub(crate) trait ByteSource { + fn len(&self) -> u64; + fn read_at(&self, offset: u64, output: &mut [u8]) -> Result; +} +impl ByteSource for [u8] { + fn len(&self) -> u64 { + self.len() as u64 + } + fn read_at(&self, offset: u64, output: &mut [u8]) -> Result { + let offset = usize::try_from(offset).map_err(|_| ValidationIssue::InvalidSpan)?; + let source = self.get(offset..).ok_or(ValidationIssue::InvalidSpan)?; + let count = source.len().min(output.len()); + output[..count].copy_from_slice(&source[..count]); + Ok(count) + } +} +/// Borrows a dedicated, already-open preparation handle; no path is allocated +/// or opened and no file identity/association/immutability is certified. +/// +/// The mutable borrow excludes concurrent ordinary cursor use through this +/// handle. The caller must also exclude cursor-sharing clones. On Windows, +/// seek_read moves the native cursor; Unix read_at does not. Never borrow a +/// producer's sequential-writing handle here. File/type/stamp authority remains +/// the caller's later noncreating S2 preparation, not this length observation. +#[cfg(test)] +pub(crate) struct BorrowedFileSource<'a> { + file: &'a File, + length: u64, + _floor: MemoryReservation, +} +#[cfg(test)] +impl<'a> BorrowedFileSource<'a> { + pub(crate) fn new(file: &'a mut File, memory: &CleanupMemory) -> Result { + let floor = std::mem::size_of::() + .checked_add(std::mem::size_of::()) + .and_then(|n| n.checked_add(128)) + .ok_or(ValidationIssue::Overflow)?; + let reservation = memory.reserve(floor)?; + let metadata = file.metadata().map_err(|e| ValidationIssue::Io(e.kind()))?; + if !metadata.is_file() { + return Err(ValidationIssue::Schema); + } + Ok(Self { + file, + length: metadata.len(), + _floor: reservation, + }) + } +} +#[cfg(test)] +impl ByteSource for BorrowedFileSource<'_> { + fn len(&self) -> u64 { + self.length + } + fn read_at(&self, offset: u64, output: &mut [u8]) -> Result { + let remaining = self + .length + .checked_sub(offset) + .ok_or(ValidationIssue::InvalidSpan)?; + let count = remaining.min(output.len() as u64) as usize; + let output = &mut output[..count]; + loop { + #[cfg(unix)] + let result = std::os::unix::fs::FileExt::read_at(self.file, output, offset); + #[cfg(windows)] + let result = std::os::windows::fs::FileExt::seek_read(self.file, output, offset); + #[cfg(not(any(unix, windows)))] + return Err(ValidationIssue::UnsupportedPositionedRead); + #[cfg(any(unix, windows))] + match result { + Err(e) if e.kind() == io::ErrorKind::Interrupted => continue, + Err(e) => return Err(ValidationIssue::Io(e.kind())), + Ok(n) => return Ok(n), + } + } + } +} +struct SourceReader<'a, S: ?Sized> { + source: &'a S, + offset: u64, +} +impl Read for SourceReader<'_, S> { + fn read(&mut self, output: &mut [u8]) -> io::Result { + let n = self + .source + .read_at(self.offset, output) + .map_err(|_| io::ErrorKind::InvalidData)?; + self.offset = self + .offset + .checked_add(n as u64) + .ok_or(io::ErrorKind::InvalidData)?; + Ok(n) + } +} +struct SpanReader<'a, S: ?Sized> { + source: &'a S, + offset: u64, + end: u64, + bytes: [u8; 256], + at: usize, + filled: usize, +} +impl<'a, S: ByteSource + ?Sized> SpanReader<'a, S> { + fn new(source: &'a S, span: Span) -> Result { + if span.start > span.end || span.end > source.len() { + return Err(ValidationIssue::InvalidSpan); + } + Ok(Self { + source, + offset: span.start, + end: span.end, + bytes: [0; 256], + at: 0, + filled: 0, + }) + } + fn next(&mut self) -> Result, ValidationIssue> { + if self.at == self.filled { + let len = (self.end - self.offset).min(256) as usize; + if len == 0 { + return Ok(None); + } + self.filled = self.source.read_at(self.offset, &mut self.bytes[..len])?; + self.at = 0; + if self.filled == 0 || self.filled > len { + return Err(ValidationIssue::InvalidSpan); + } + self.offset += self.filled as u64; + } + let byte = self.bytes[self.at]; + self.at += 1; + Ok(Some(byte)) + } + fn required(&mut self) -> Result { + self.next()?.ok_or(ValidationIssue::InvalidSpan) + } +} +impl Read for SpanReader<'_, S> { + fn read(&mut self, output: &mut [u8]) -> io::Result { + let mut count = 0; + for byte in output { + match self.next().map_err(|_| io::ErrorKind::InvalidData)? { + Some(value) => { + *byte = value; + count += 1; + } + None => break, + } + } + Ok(count) + } +} +struct Decoded<'a, S: ?Sized> { + raw: SpanReader<'a, S>, + pending: [u8; 4], + at: usize, + filled: usize, + ended: bool, +} +impl<'a, S: ByteSource + ?Sized> Decoded<'a, S> { + fn new(source: &'a S, span: Span) -> Result { + let mut raw = SpanReader::new(source, span)?; + if raw.required()? != b'"' { + return Err(ValidationIssue::InvalidSpan); + } + Ok(Self { + raw, + pending: [0; 4], + at: 0, + filled: 0, + ended: false, + }) + } + fn hex(&mut self) -> Result { + let mut value = 0; + for _ in 0..4 { + let b = self.raw.required()?; + let v = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => b - b'a' + 10, + b'A'..=b'F' => b - b'A' + 10, + _ => return Err(ValidationIssue::NonProducer), + }; + value = value * 16 + u32::from(v); + } + Ok(value) + } + fn next(&mut self) -> Result, ValidationIssue> { + if self.at < self.filled { + let b = self.pending[self.at]; + self.at += 1; + return Ok(Some(b)); + } + if self.ended { + return Ok(None); + } + let byte = self.raw.required()?; + if byte == b'"' { + self.ended = true; + if self.raw.next()?.is_some() { + return Err(ValidationIssue::InvalidSpan); + } + return Ok(None); + } + if byte != b'\\' { + if byte < 32 { + return Err(ValidationIssue::NonProducer); + } + if byte >= 0x80 { + self.filled = match byte { + 0xc2..=0xdf => 2, + 0xe0..=0xef => 3, + 0xf0..=0xf4 => 4, + _ => return Err(ValidationIssue::NonProducer), + }; + self.pending[0] = byte; + for value in &mut self.pending[1..self.filled] { + *value = self.raw.required()?; + } + std::str::from_utf8(&self.pending[..self.filled]) + .map_err(|_| ValidationIssue::NonProducer)?; + self.at = 1; + } + return Ok(Some(byte)); + } + let escape = self.raw.required()?; + Ok(Some(match escape { + b'"' | b'\\' | b'/' => escape, + b'b' => 8, + b'f' => 12, + b'n' => 10, + b'r' => 13, + b't' => 9, + b'u' => { + let first = self.hex()?; + let value = if (0xd800..=0xdbff).contains(&first) { + if self.raw.required()? != b'\\' || self.raw.required()? != b'u' { + return Err(ValidationIssue::NonProducer); + } + let low = self.hex()?; + if !(0xdc00..=0xdfff).contains(&low) { + return Err(ValidationIssue::NonProducer); + } + 0x10000 + ((first - 0xd800) << 10) + low - 0xdc00 + } else { + first + }; + let c = char::from_u32(value).ok_or(ValidationIssue::NonProducer)?; + self.filled = c.encode_utf8(&mut self.pending).len(); + self.at = 1; + self.pending[0] + } + _ => return Err(ValidationIssue::NonProducer), + })) + } +} +fn compare( + source: &S, + a: Span, + b: Span, +) -> Result { + compare_complete(Decoded::new(source, a)?, Decoded::new(source, b)?) +} +trait KeyBytes { + fn next_key_byte(&mut self) -> Result, ValidationIssue>; +} +impl KeyBytes for Decoded<'_, S> { + fn next_key_byte(&mut self) -> Result, ValidationIssue> { + self.next() + } +} +struct NativeUtf8<'a, S: ByteSource + ?Sized> { + raw: SpanReader<'a, S>, + carry: [u8; 4], + at: usize, + filled: usize, +} +impl<'a, S: ByteSource + ?Sized> NativeUtf8<'a, S> { + fn new(source: &'a S, span: Span) -> Result { + Ok(Self { + raw: SpanReader::new(source, span)?, + carry: [0; 4], + at: 0, + filled: 0, + }) + } +} +impl KeyBytes for NativeUtf8<'_, S> { + fn next_key_byte(&mut self) -> Result, ValidationIssue> { + if self.at < self.filled { + let byte = self.carry[self.at]; + self.at += 1; + return Ok(Some(byte)); + } + let Some(byte) = self.raw.next()? else { + return Ok(None); + }; + if byte >= 0x80 { + self.filled = match byte { + 0xc2..=0xdf => 2, + 0xe0..=0xef => 3, + 0xf0..=0xf4 => 4, + _ => return Err(ValidationIssue::NonProducer), + }; + self.carry[0] = byte; + for value in &mut self.carry[1..self.filled] { + *value = self.raw.required()?; + } + std::str::from_utf8(&self.carry[..self.filled]) + .map_err(|_| ValidationIssue::NonProducer)?; + self.at = 1; + } + Ok(Some(byte)) + } +} +fn compare_complete( + mut left: A, + mut right: B, +) -> Result { + let mut ordering = Ordering::Equal; + loop { + let pair = (left.next_key_byte()?, right.next_key_byte()?); + if pair == (None, None) { + return Ok(ordering); + } + // Ordering is decided by the first difference, but both complete spans + // must still validate before that ordering can become a successful fact. + if ordering == Ordering::Equal { + ordering = match pair { + (None, Some(_)) => Ordering::Less, + (Some(_), None) => Ordering::Greater, + (Some(a), Some(b)) => a.cmp(&b), + (None, None) => unreachable!(), + }; + } + } +} +/// Native spans address a LOGICAL UTF8 view, not compressed physical bytes. +/// Source/encoding/locator identity remains explicit and confers no origin. +pub(crate) fn compare_native_json( + native: &A, + native_span: Span, + json: &B, + json_span: Span, + memory: &CleanupMemory, +) -> Result { + let floor = std::mem::size_of::>() + .checked_add(std::mem::size_of::>()) + .and_then(|n| n.checked_add(128)) + .ok_or(ValidationIssue::Overflow)?; + let _reservation = memory.reserve(floor)?; + compare_complete( + NativeUtf8::new(native, native_span)?, + Decoded::new(json, json_span)?, + ) +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum KeyEncoding { + NativeUtf8, + JsonDecoded, +} +/// Exact complete-key comparison for independently encoded/bound sources. +/// Ordering is returned only after both entire encodings validate. +pub(crate) fn compare_encoded_keys( + left: &A, + left_span: Span, + left_encoding: KeyEncoding, + right: &B, + right_span: Span, + right_encoding: KeyEncoding, + memory: &CleanupMemory, +) -> Result { + let floor = std::mem::size_of::>() + .checked_add(std::mem::size_of::>()) + .and_then(|n| n.checked_add(std::mem::size_of::>())) + .and_then(|n| n.checked_add(std::mem::size_of::>())) + .and_then(|n| n.checked_add(128)) + .ok_or(ValidationIssue::Overflow)?; + let _reservation = memory.reserve(floor)?; + match (left_encoding, right_encoding) { + (KeyEncoding::NativeUtf8, KeyEncoding::NativeUtf8) => compare_complete( + NativeUtf8::new(left, left_span)?, + NativeUtf8::new(right, right_span)?, + ), + (KeyEncoding::NativeUtf8, KeyEncoding::JsonDecoded) => compare_complete( + NativeUtf8::new(left, left_span)?, + Decoded::new(right, right_span)?, + ), + (KeyEncoding::JsonDecoded, KeyEncoding::NativeUtf8) => compare_complete( + Decoded::new(left, left_span)?, + NativeUtf8::new(right, right_span)?, + ), + (KeyEncoding::JsonDecoded, KeyEncoding::JsonDecoded) => compare_complete( + Decoded::new(left, left_span)?, + Decoded::new(right, right_span)?, + ), + } +} +/// Domain separation and decoded bytes are shared across encodings. A digest +/// is an accelerator only; callers still compare complete exact key streams. +pub(crate) fn key_stream_hash( + source: &S, + span: Span, + encoding: KeyEncoding, + domain: u64, + memory: &CleanupMemory, +) -> Result<[u8; 32], ValidationIssue> { + let floor = std::mem::size_of::>() + .checked_add(std::mem::size_of::>()) + .and_then(|n| n.checked_add(std::mem::size_of::())) + .and_then(|n| n.checked_add(128)) + .ok_or(ValidationIssue::Overflow)?; + let _reservation = memory.reserve(floor)?; + fn hash(mut reader: impl KeyBytes, domain: u64) -> Result<[u8; 32], ValidationIssue> { + let mut hash = Sha256::new(); + hash.update(domain.to_le_bytes()); + while let Some(byte) = reader.next_key_byte()? { + hash.update([byte]); + } + Ok(hash.finalize().into()) + } + match encoding { + KeyEncoding::NativeUtf8 => hash(NativeUtf8::new(source, span)?, domain), + KeyEncoding::JsonDecoded => hash(Decoded::new(source, span)?, domain), + } +} +/// Decode into caller-owned fixed storage. Oversized keys are fully validated +/// before Schema is returned; no truncated prefix can become a key witness. +pub(crate) fn copy_decoded_key( + source: &S, + span: Span, + encoding: KeyEncoding, + out: &mut [u8], + memory: &CleanupMemory, +) -> Result { + let floor = std::mem::size_of::>() + .checked_add(std::mem::size_of::>()) + .and_then(|n| n.checked_add(128)) + .ok_or(ValidationIssue::Overflow)?; + let _reservation = memory.reserve(floor)?; + fn copy(mut reader: impl KeyBytes, out: &mut [u8]) -> Result { + let mut length = 0usize; + while let Some(byte) = reader.next_key_byte()? { + if length < out.len() { + out[length] = byte; + } + length = length.checked_add(1).ok_or(ValidationIssue::Overflow)?; + } + if length > out.len() { + Err(ValidationIssue::Schema) + } else { + Ok(length) + } + } + match encoding { + KeyEncoding::NativeUtf8 => copy(NativeUtf8::new(source, span)?, out), + KeyEncoding::JsonDecoded => copy(Decoded::new(source, span)?, out), + } +} +/// Complete SHA1 of the strict decoded stream. Graph stable IDs use the first +/// ten digest bytes; the returned twenty bytes are never equality authority. +pub(crate) fn key_sha1_prefix20( + source: &S, + span: Span, + encoding: KeyEncoding, + memory: &CleanupMemory, +) -> Result<[u8; 20], ValidationIssue> { + let floor = std::mem::size_of::>() + .checked_add(std::mem::size_of::>()) + .and_then(|n| n.checked_add(768)) + .ok_or(ValidationIssue::Overflow)?; + let _reservation = memory.reserve(floor)?; + fn hash(mut input: impl KeyBytes) -> Result<[u8; 20], ValidationIssue> { + let mut state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; + let mut block = [0; 64]; + let mut used = 0usize; + let mut len = 0u64; + while let Some(byte) = input.next_key_byte()? { + block[used] = byte; + used += 1; + len = len.checked_add(1).ok_or(ValidationIssue::Overflow)?; + if used == 64 { + sha1_block(&mut state, &block); + used = 0; + } + } + let bits = len.checked_mul(8).ok_or(ValidationIssue::Overflow)?; + block[used] = 0x80; + used += 1; + if used > 56 { + block[used..].fill(0); + sha1_block(&mut state, &block); + block.fill(0); + } else { + block[used..56].fill(0); + } + block[56..].copy_from_slice(&bits.to_be_bytes()); + sha1_block(&mut state, &block); + let mut digest = [0; 20]; + for (i, word) in state.iter().enumerate() { + digest[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes()); + } + Ok(digest) + } + match encoding { + KeyEncoding::NativeUtf8 => hash(NativeUtf8::new(source, span)?), + KeyEncoding::JsonDecoded => hash(Decoded::new(source, span)?), + } +} + +/// Exact decoded bytes across independently bound sources. Digest equality is +/// never substituted for equality; long strings stream through fixed buffers. +pub(crate) fn compare_decoded_spans( + left: &A, + left_span: Span, + right: &B, + right_span: Span, + memory: &CleanupMemory, +) -> Result { + let bytes = std::mem::size_of::>() + .checked_add(std::mem::size_of::>()) + .and_then(|n| n.checked_add(128)) + .ok_or(ValidationIssue::Overflow)?; + let _floor = memory.reserve(bytes)?; + compare_complete( + Decoded::new(left, left_span)?, + Decoded::new(right, right_span)?, + ) +} +fn equals( + source: &S, + span: Span, + expected: &[u8], +) -> Result { + let mut decoded = Decoded::new(source, span)?; + for b in expected { + if decoded.next()? != Some(*b) { + return Ok(false); + } + } + Ok(decoded.next()?.is_none()) +} +fn producer_string( + source: &S, + span: Span, +) -> Result { + let mut decoded = Decoded::new(source, span)?; + let mut raw = SpanReader::new(source, span)?; + if raw.required()? != b'"' { + return Ok(false); + } + while let Some(b) = decoded.next()? { + let mut encoded = [0u8; 6]; + let bytes: &[u8] = match b { + b'"' => b"\\\"", + b'\\' => b"\\\\", + 8 => b"\\b", + 12 => b"\\f", + 10 => b"\\n", + 13 => b"\\r", + 9 => b"\\t", + 0..=31 => { + encoded[..4].copy_from_slice(b"\\u00"); + encoded[4] = b"0123456789abcdef"[(b >> 4) as usize]; + encoded[5] = b"0123456789abcdef"[(b & 15) as usize]; + &encoded + } + _ => { + encoded[0] = b; + &encoded[..1] + } + }; + for b in bytes { + if raw.next()? != Some(*b) { + return Ok(false); + } + } + } + Ok(raw.next()? == Some(b'"') && raw.next()?.is_none()) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum NumberKind { + F64, + Value, + I64, + U64, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct NumberFacts { + pub(crate) typed: bool, + pub(crate) stable: bool, +} +/// The pinned parser/serializer is the oracle for EVERY float and Value number. +/// Finite alone is insufficient when float_roundtrip is disabled. +pub(crate) fn number_facts( + bytes: &[u8], + kind: NumberKind, + memory: &CleanupMemory, +) -> Result { + // Fixed token/output buffers, pinned scalar parser/serializer scratch and + // its bounded error allocation. No arbitrary diagnostic is formatted. + let _workspace = memory.reserve(1024)?; + if bytes.len() > 64 || !number_token(bytes) { + return Ok(NumberFacts { + typed: false, + stable: false, + }); + } + let mut output = [0u8; 64]; + let mut cursor = io::Cursor::new(&mut output[..]); + let result = match kind { + NumberKind::F64 => serde_json::from_slice::(bytes) + .ok() + .filter(|v| v.is_finite()) + .and_then(|v| serde_json::to_writer(&mut cursor, &v).ok()), + NumberKind::Value => serde_json::from_slice::(bytes) + .ok() + .and_then(|v| serde_json::to_writer(&mut cursor, &v).ok()), + NumberKind::I64 => serde_json::from_slice::(bytes) + .ok() + .and_then(|v| serde_json::to_writer(&mut cursor, &v).ok()), + NumberKind::U64 => serde_json::from_slice::(bytes) + .ok() + .and_then(|v| serde_json::to_writer(&mut cursor, &v).ok()), + }; + let len = cursor.position() as usize; + Ok(NumberFacts { + typed: result.is_some(), + stable: result.is_some() && output.get(..len) == Some(bytes), + }) +} +fn number_token(bytes: &[u8]) -> bool { + let mut index = 0; + if bytes.first() == Some(&b'-') { + index += 1; + } + match bytes.get(index) { + Some(b'0') => index += 1, + Some(b'1'..=b'9') => { + index += 1; + while bytes.get(index).is_some_and(u8::is_ascii_digit) { + index += 1; + } + } + _ => return false, + } + if bytes.get(index) == Some(&b'.') { + index += 1; + let start = index; + while bytes.get(index).is_some_and(u8::is_ascii_digit) { + index += 1; + } + if start == index { + return false; + } + } + if matches!(bytes.get(index), Some(b'e' | b'E')) { + index += 1; + if matches!(bytes.get(index), Some(b'+' | b'-')) { + index += 1; + } + let start = index; + while bytes.get(index).is_some_and(u8::is_ascii_digit) { + index += 1; + } + if start == index { + return false; + } + } + index == bytes.len() +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum JoinKind { + Raw { ordinal: u64, kind: Span }, + DeclaredId, + DeclaredType { kind: Span }, + UniqueKey, +} +#[derive(Debug, Clone, Copy)] +pub(crate) struct JoinEvent { + pub(crate) domain: u64, + pub(crate) id: Span, + pub(crate) kind: JoinKind, +} +/// Domain 0=nodes,1=edges; >=2 is a separate metadata-object key namespace. +pub(crate) trait ReplayEvents: ByteSource { + fn replay( + &self, + memory: &CleanupMemory, + emit: &mut dyn FnMut(JoinEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue>; +} +#[derive(Debug, Clone, Copy, Default)] +pub(crate) struct RawRowsMatch { + pub(crate) passes: u64, + pub(crate) events: u64, + pub(crate) collision_batches: u64, + pub(crate) lookup_probes: u64, +} + +const SLOT: usize = 128; +#[derive(Clone, Copy, Default)] +struct Row { + occupied: bool, + domain: u64, + hash: [u8; 32], + id: Span, + raw: u64, + declared: u64, + maps: u64, + ordinal: u64, + raw_kind: Span, + map_kind: Span, +} +struct Table { + bytes: ReservedBytes, + capacity: usize, + probes: Cell, +} +impl Table { + fn new(memory: &CleanupMemory, capacity: usize) -> Result { + if capacity == 0 { + return Err(ValidationIssue::Resource(ResourceLimit::CeilingExceeded)); + } + let len = capacity + .checked_mul(SLOT) + .ok_or(ValidationIssue::Overflow)?; + Ok(Self { + bytes: memory.reserve(len)?.bytes(len)?, + capacity, + probes: Cell::new(0), + }) + } + fn clear(&mut self) { + self.bytes.as_mut_slice().fill(0); + } + fn row(&self, index: usize) -> Row { + let b = &self.bytes.as_slice()[index * SLOT..(index + 1) * SLOT]; + let u = |o| u64::from_le_bytes(b[o..o + 8].try_into().unwrap()); + Row { + occupied: b[0] != 0, + domain: u(1), + hash: b[9..41].try_into().unwrap(), + id: Span { + start: u(41), + end: u(49), + }, + raw: u(57), + declared: u(65), + maps: u(73), + ordinal: u(81), + raw_kind: Span { + start: u(89), + end: u(97), + }, + map_kind: Span { + start: u(105), + end: u(113), + }, + } + } + fn put(&mut self, index: usize, r: Row) { + let b = &mut self.bytes.as_mut_slice()[index * SLOT..(index + 1) * SLOT]; + b.fill(0); + // Insert/replace only. There is no single-slot deletion operation that + // could invalidate first-empty termination of an open-address probe. + b[0] = 1; + b[9..41].copy_from_slice(&r.hash); + for (o, v) in [ + (1, r.domain), + (41, r.id.start), + (49, r.id.end), + (57, r.raw), + (65, r.declared), + (73, r.maps), + (81, r.ordinal), + (89, r.raw_kind.start), + (97, r.raw_kind.end), + (105, r.map_kind.start), + (113, r.map_kind.end), + ] { + b[o..o + 8].copy_from_slice(&v.to_le_bytes()); + } + } + fn find( + &self, + source: &S, + event: JoinEvent, + hash: [u8; 32], + ) -> Result, ValidationIssue> { + let start = + (u64::from_le_bytes(hash[..8].try_into().unwrap()) % self.capacity as u64) as usize; + for delta in 0..self.capacity { + self.probes.set( + self.probes + .get() + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?, + ); + let index = (start + delta) % self.capacity; + let row = self.row(index); + if !row.occupied { + // Buckets have no deletion holes: insertion uses this first + // empty slot, and collision selection replaces only FULL + // tables. Therefore the key cannot occur beyond this empty. + return Ok(Err(index)); + } + if row.domain == event.domain + && row.hash == hash + && compare(source, row.id, event.id)? == Ordering::Equal + { + return Ok(Ok(index)); + } + } + Ok(Err(self.capacity)) + } + fn apply( + &mut self, + index: usize, + event: JoinEvent, + hash: [u8; 32], + ) -> Result<(), ValidationIssue> { + let mut row = self.row(index); + if !row.occupied { + row = Row { + occupied: true, + domain: event.domain, + hash, + id: event.id, + ..Row::default() + }; + } + match event.kind { + JoinKind::Raw { ordinal, kind } => { + row.raw = row.raw.checked_add(1).ok_or(ValidationIssue::Overflow)?; + if row.raw == 1 || ordinal >= row.ordinal { + row.ordinal = ordinal; + row.raw_kind = kind; + } + } + JoinKind::DeclaredId => { + row.declared = row + .declared + .checked_add(1) + .ok_or(ValidationIssue::Overflow)? + } + JoinKind::DeclaredType { kind } => { + row.maps = row.maps.checked_add(1).ok_or(ValidationIssue::Overflow)?; + row.map_kind = kind; + } + JoinKind::UniqueKey => { + row.raw = row.raw.checked_add(1).ok_or(ValidationIssue::Overflow)? + } + } + self.put(index, row); + Ok(()) + } + fn verify(&self, source: &S) -> Result<(), ValidationIssue> { + for i in 0..self.capacity { + let r = self.row(i); + if !r.occupied { + continue; + } + if r.domain >= 2 { + if r.raw != 1 || r.declared != 0 || r.maps != 0 { + return Err(ValidationIssue::RawRowsDiffer); + } + } else if r.raw == 0 + || r.raw != r.declared + || r.maps != 1 + || compare(source, r.raw_kind, r.map_kind)? != Ordering::Equal + { + return Err(ValidationIssue::RawRowsDiffer); + } + } + Ok(()) + } +} +fn key_compare( + source: &S, + a: Row, + b: Row, +) -> Result { + if a.domain != b.domain { + return Ok(a.domain.cmp(&b.domain)); + } + compare(source, a.id, b.id) +} +fn digest( + source: &S, + event: JoinEvent, + forced: bool, +) -> Result<[u8; 32], ValidationIssue> { + if forced { + return Ok([0; 32]); + } + let mut hash = Sha256::new(); + hash.update(event.domain.to_le_bytes()); + let mut decoded = Decoded::new(source, event.id)?; + while let Some(byte) = decoded.next()? { + hash.update([byte]); + } + Ok(hash.finalize().into()) +} +fn matches_prefix(hash: &[u8; 32], prefix: &[u8; 32], bits: usize) -> bool { + let whole = bits / 8; + hash[..whole] == prefix[..whole] + && (bits % 8 == 0 || ((hash[whole] ^ prefix[whole]) & (0xff << (8 - bits % 8))) == 0) +} +fn replay( + source: &S, + memory: &CleanupMemory, + stats: &mut RawRowsMatch, + mut emit: impl FnMut(JoinEvent) -> Result<(), ValidationIssue>, +) -> Result<(), ValidationIssue> { + stats.passes = stats + .passes + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + source.replay(memory, &mut |event| { + stats.events = stats + .events + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + emit(event) + }) +} + +/// All raw IDs, declared IDs AND declared map keys participate. Extra map-only +/// entries cannot disappear in a hash bucket or collision fallback. +pub(crate) fn join_raw_rows( + source: &S, + memory: &CleanupMemory, + capacity: usize, +) -> Result { + join_inner(source, memory, capacity, false) +} +fn join_inner( + source: &S, + memory: &CleanupMemory, + capacity: usize, + forced: bool, +) -> Result { + let _floor = memory.reserve(8192)?; + let mut table = Table::new(memory, capacity)?; + let mut stats = RawRowsMatch::default(); + let mut prefix = [0u8; 32]; + let mut bits = 0; + loop { + table.clear(); + let mut overflow = false; + let mut first_hash = None; + let mut all_same_hash = true; + replay(source, memory, &mut stats, |event| { + let hash = digest(source, event, forced)?; + if !matches_prefix(&hash, &prefix, bits) { + return Ok(()); + } + if let Some(first) = first_hash { + all_same_hash &= hash == first; + } else { + first_hash = Some(hash); + } + match table.find(source, event, hash)? { + Ok(index) => table.apply(index, event, hash)?, + Err(index) if index < capacity => table.apply(index, event, hash)?, + Err(_) => overflow = true, + } + Ok(()) + })?; + if overflow && !all_same_hash && bits < 256 { + bits += 1; + prefix[(bits - 1) / 8] &= !(0x80 >> ((bits - 1) % 8)); + continue; + } + if overflow { + // A complete replay observed ONE exact hash for the whole bucket: + // skip 256 empty sibling scans, while exact spans still separate + // distinct keys. This is evidence from all events, not a guess. + let hash = first_hash.ok_or(ValidationIssue::RawRowsDiffer)?; + collision_batches(source, memory, &mut table, &hash, forced, &mut stats)?; + } else { + table.verify(source)?; + } + while bits > 0 && (prefix[(bits - 1) / 8] & (0x80 >> ((bits - 1) % 8))) != 0 { + prefix[(bits - 1) / 8] &= !(0x80 >> ((bits - 1) % 8)); + bits -= 1; + } + if bits == 0 { + break; + } + prefix[(bits - 1) / 8] |= 0x80 >> ((bits - 1) % 8); + } + stats.lookup_probes = table.probes.get(); + Ok(stats) +} +fn collision_batches( + source: &S, + memory: &CleanupMemory, + table: &mut Table, + prefix: &[u8; 32], + forced: bool, + stats: &mut RawRowsMatch, +) -> Result<(), ValidationIssue> { + let mut lower: Option = None; + loop { + table.clear(); + let mut count = 0; + replay(source, memory, stats, |event| { + let hash = digest(source, event, forced)?; + if &hash != prefix { + return Ok(()); + } + let key = Row { + occupied: true, + domain: event.domain, + id: event.id, + hash, + ..Row::default() + }; + if let Some(lower) = lower { + if key_compare(source, key, lower)? != Ordering::Greater { + return Ok(()); + } + } + match table.find(source, event, hash)? { + Ok(_) => {} + Err(index) if index < table.capacity => { + table.put(index, key); + count += 1; + } + Err(_) => { + let mut largest = 0; + for i in 1..table.capacity { + if key_compare(source, table.row(i), table.row(largest))? + == Ordering::Greater + { + largest = i; + } + } + if key_compare(source, key, table.row(largest))? == Ordering::Less { + table.put(largest, key); + } + } + } + Ok(()) + })?; + if count == 0 { + return Ok(()); + } + let mut upper = None; + for i in 0..table.capacity { + let r = table.row(i); + if r.occupied { + let replace = match upper { + None => true, + Some(u) => key_compare(source, r, u)? == Ordering::Greater, + }; + if replace { + upper = Some(r); + } + } + } + // A separate replay aggregates only the selected exact keys; selection + // never loses multiplicity when replacing a temporary largest key. + replay(source, memory, stats, |event| { + let hash = digest(source, event, forced)?; + if &hash == prefix { + if let Ok(index) = table.find(source, event, hash)? { + table.apply(index, event, hash)?; + } + } + Ok(()) + })?; + table.verify(source)?; + stats.collision_batches = stats + .collision_batches + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + lower = upper; + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct ArtifactFacts { + pub(crate) key: [u8; 32], + pub(crate) payload_digest: [u8; 32], + pub(crate) partition: Span, + pub(crate) producer_tokens: bool, + pub(crate) typed_numbers: bool, + pub(crate) stable_numbers: bool, + /// Independent ordering fact: later loader compatibility must consult the + /// actual resolved serde_json Map backend, not infer it from Cargo.lock. + pub(crate) metadata_keys_sorted: bool, + pub(crate) declared_nodes: u64, + pub(crate) declared_edges: u64, + pub(crate) actual_nodes: u64, + pub(crate) actual_edges: u64, + pub(crate) path: Span, + pub(crate) partition_id: Span, +} +impl Default for ArtifactFacts { + fn default() -> Self { + Self { + key: [0; 32], + payload_digest: [0; 32], + partition: Span::default(), + producer_tokens: true, + typed_numbers: true, + stable_numbers: true, + metadata_keys_sorted: true, + declared_nodes: 0, + declared_edges: 0, + actual_nodes: 0, + actual_edges: 0, + path: Span::default(), + partition_id: Span::default(), + } + } +} + +/// Replayable, borrowed source adapter. Facts and join results are intentionally +/// separate, and neither is a LoaderUsable or origin capability. +pub(crate) struct ArtifactEvents<'a, S: ?Sized> { + pub(crate) source: &'a S, +} +impl ByteSource for ArtifactEvents<'_, S> { + fn len(&self) -> u64 { + self.source.len() + } + fn read_at(&self, o: u64, b: &mut [u8]) -> Result { + self.source.read_at(o, b) + } +} +impl ReplayEvents for ArtifactEvents<'_, S> { + fn replay( + &self, + memory: &CleanupMemory, + emit: &mut dyn FnMut(JoinEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue> { + scan_artifact(self.source, memory, emit).map(|_| ()) + } +} + +struct Scanner<'a, S: ?Sized> { + source: &'a S, + memory: &'a CleanupMemory, + reader: MaintenanceJsonReader>, + peeked: Option, + facts: ArtifactFacts, + emit: &'a mut dyn FnMut(JoinEvent) -> Result<(), ValidationIssue>, + _floor: MemoryReservation, +} +impl<'a, S: ByteSource + ?Sized> Scanner<'a, S> { + fn token(&mut self) -> Result { + self.peeked.take().map(Ok).unwrap_or_else(|| { + self.reader + .next_token() + .map_err(ValidationIssue::from)? + .ok_or(ValidationIssue::Schema) + }) + } + fn peek(&mut self) -> Result { + if self.peeked.is_none() { + self.peeked = Some(self.reader.next_token()?.ok_or(ValidationIssue::Schema)?); + } + Ok(self.peeked.unwrap()) + } + fn expect(&mut self, kind: TokenKind) -> Result { + let t = self.token()?; + if t.kind != kind { + return Err(ValidationIssue::Schema); + } + Ok(t) + } + fn string(&mut self) -> Result { + let t = self.token()?; + if !matches!(t.kind, TokenKind::String { key: false, .. }) { + return Err(ValidationIssue::Schema); + } + let span = Span { + start: t.start, + end: t.end, + }; + self.facts.producer_tokens &= producer_string(self.source, span)?; + Ok(span) + } + fn optional_string(&mut self) -> Result<(), ValidationIssue> { + if self.peek()?.kind == TokenKind::Null { + self.token()?; + self.facts.producer_tokens = false; + } else { + self.string()?; + } + Ok(()) + } + fn raw_number(&self, span: Span, output: &mut [u8; 64]) -> Result { + let len = usize::try_from(span.len()?).map_err(|_| ValidationIssue::Numeric)?; + if len > 64 { + return Err(ValidationIssue::Numeric); + } + let mut reader = SpanReader::new(self.source, span)?; + for b in &mut output[..len] { + *b = reader.required()?; + } + Ok(len) + } + fn number(&mut self, kind: NumberKind, nullable: bool) -> Result, ValidationIssue> { + if self.peek()?.kind == TokenKind::Null { + self.token()?; + if !nullable { + self.facts.typed_numbers = false; + self.facts.stable_numbers = false; + } + return Ok(None); + } + let t = self.expect(TokenKind::Number)?; + let mut bytes = [0u8; 64]; + let len = self.raw_number( + Span { + start: t.start, + end: t.end, + }, + &mut bytes, + )?; + let facts = number_facts(&bytes[..len], kind, self.memory)?; + self.facts.typed_numbers &= facts.typed; + self.facts.stable_numbers &= facts.stable; + if kind == NumberKind::U64 { + return serde_json::from_slice(&bytes[..len]) + .map(Some) + .map_err(|_| ValidationIssue::Numeric); + } + Ok(None) + } + fn object( + &mut self, + names: &[&str], + required: u64, + mut field: impl FnMut(&mut Self, usize) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue> { + self.expect(TokenKind::ObjectStart)?; + let mut seen = 0u64; + let mut previous = None; + while self.peek()?.kind != TokenKind::ObjectEnd { + let key = self.token()?; + if !matches!(key.kind, TokenKind::String { key: true, .. }) { + return Err(ValidationIssue::Schema); + } + let span = Span { + start: key.start, + end: key.end, + }; + self.facts.producer_tokens &= producer_string(self.source, span)?; + let mut index = None; + for (i, name) in names.iter().enumerate() { + if equals(self.source, span, name.as_bytes())? { + index = Some(i); + break; + } + } + let index = index.ok_or(ValidationIssue::Schema)?; + if seen & (1 << index) != 0 { + return Err(ValidationIssue::NonProducer); + } + if previous.is_some_and(|p| index < p) { + self.facts.producer_tokens = false; + } + previous = Some(index); + seen |= 1 << index; + field(self, index)?; + } + self.token()?; + if seen & required != required { + return Err(ValidationIssue::Schema); + } + Ok(()) + } + fn hex_string(&mut self) -> Result<[u8; 32], ValidationIssue> { + let span = self.string()?; + let mut decoded = Decoded::new(self.source, span)?; + let mut result = [0u8; 32]; + for b in &mut result { + let mut value = 0; + for _ in 0..2 { + let d = decoded.next()?.ok_or(ValidationIssue::Schema)?; + value = value * 16 + + match d { + b'0'..=b'9' => d - b'0', + b'a'..=b'f' => d - b'a' + 10, + _ => return Err(ValidationIssue::Schema), + }; + } + *b = value; + } + if decoded.next()?.is_some() { + return Err(ValidationIssue::Schema); + } + Ok(result) + } + fn metadata(&mut self) -> Result<(), ValidationIssue> { + let mut objects = [None; 127]; + let _previous_key_floor = self + .memory + .reserve(std::mem::size_of::<[Option; 127]>())?; + let mut previous = [None; 127]; + let mut depth = 0usize; + loop { + let t = self.token()?; + match t.kind { + TokenKind::ObjectStart | TokenKind::ArrayStart => { + previous[depth] = None; + objects[depth] = if t.kind == TokenKind::ObjectStart { + Some(t.start.checked_add(2).ok_or(ValidationIssue::Overflow)?) + } else { + None + }; + depth += 1; + } + TokenKind::ObjectEnd | TokenKind::ArrayEnd => { + depth -= 1; + } + TokenKind::String { key, .. } => { + let span = Span { + start: t.start, + end: t.end, + }; + self.facts.producer_tokens &= producer_string(self.source, span)?; + if key { + let domain = objects[depth - 1].ok_or(ValidationIssue::Schema)?; + if let Some(old) = previous[depth - 1] { + self.facts.metadata_keys_sorted &= + compare(self.source, old, span)? == Ordering::Less; + } + previous[depth - 1] = Some(span); + (self.emit)(JoinEvent { + domain, + id: span, + kind: JoinKind::UniqueKey, + })?; + } + } + TokenKind::Number => { + let mut bytes = [0u8; 64]; + let n = self.raw_number( + Span { + start: t.start, + end: t.end, + }, + &mut bytes, + )?; + let f = number_facts(&bytes[..n], NumberKind::Value, self.memory)?; + self.facts.typed_numbers &= f.typed; + self.facts.stable_numbers &= f.stable; + } + _ => {} + } + if depth == 0 { + break; + } + } + Ok(()) + } + fn ids(&mut self, domain: u64) -> Result<(), ValidationIssue> { + self.expect(TokenKind::ArrayStart)?; + let mut previous = None; + let mut count = 0u64; + while self.peek()?.kind != TokenKind::ArrayEnd { + let id = self.string()?; + if let Some(old) = previous { + if compare(self.source, old, id)? == Ordering::Greater { + return Err(ValidationIssue::RawRowsDiffer); + } + } + previous = Some(id); + (self.emit)(JoinEvent { + domain, + id, + kind: JoinKind::DeclaredId, + })?; + count = count.checked_add(1).ok_or(ValidationIssue::Overflow)?; + } + self.token()?; + if count == 0 { + self.facts.producer_tokens = false; + } + Ok(()) + } + fn types(&mut self, domain: u64) -> Result<(), ValidationIssue> { + self.expect(TokenKind::ObjectStart)?; + let mut previous = None; + let mut count = 0u64; + while self.peek()?.kind != TokenKind::ObjectEnd { + let key = self.token()?; + if !matches!(key.kind, TokenKind::String { key: true, .. }) { + return Err(ValidationIssue::Schema); + } + let id = Span { + start: key.start, + end: key.end, + }; + self.facts.producer_tokens &= producer_string(self.source, id)?; + if let Some(old) = previous { + if compare(self.source, old, id)? != Ordering::Less { + return Err(ValidationIssue::NonProducer); + } + } + previous = Some(id); + let kind = self.string()?; + (self.emit)(JoinEvent { + domain, + id, + kind: JoinKind::DeclaredType { kind }, + })?; + count += 1; + } + self.token()?; + if count == 0 { + self.facts.producer_tokens = false; + } + Ok(()) + } + fn entry(&mut self) -> Result<(), ValidationIssue> { + const N: &[&str] = &[ + "path", + "content_hash", + "language", + "partition_id", + "artifact_key", + "node_ids", + "edge_ids", + "node_types", + "edge_types", + "node_count", + "edge_count", + "materialized_at", + ]; + self.object( + N, + 0b111 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 10) | (1 << 11), + |s, i| { + match i { + 0 => s.facts.path = s.string()?, + 1 | 2 | 3 | 11 => { + let span = s.string()?; + if i == 3 { + s.facts.partition_id = span; + } + } + 4 => { + let key = s.hex_string()?; + if key != s.facts.key { + return Err(ValidationIssue::Schema); + } + } + 5 => s.ids(0)?, + 6 => s.ids(1)?, + 7 => s.types(0)?, + 8 => s.types(1)?, + 9 => { + s.facts.declared_nodes = s + .number(NumberKind::U64, false)? + .ok_or(ValidationIssue::Numeric)? + } + 10 => { + s.facts.declared_edges = s + .number(NumberKind::U64, false)? + .ok_or(ValidationIssue::Numeric)? + } + _ => unreachable!(), + } + Ok(()) + }, + ) + } + fn row(&mut self, edge: bool, ordinal: u64) -> Result<(), ValidationIssue> { + const NODE: &[&str] = &[ + "id", + "table", + "label", + "kind", + "language", + "grammar_version", + "path", + "qualified_name", + "scope_id", + "line_start", + "line_end", + "byte_start", + "byte_end", + "tree_sitter_node_type", + "capture_name", + "summary", + "metadata", + ]; + const EDGE: &[&str] = &[ + "id", + "edge_type", + "source_id", + "target_id", + "kind", + "confidence", + "field_name", + "child_index", + "line_start", + "line_end", + "byte_start", + "byte_end", + "metadata", + ]; + let names = if edge { EDGE } else { NODE }; + let optional = if edge { (1 << 6) | (1 << 7) } else { 1 << 5 }; + let required = ((1u64 << names.len()) - 1) & !optional; + let mut id = None; + let mut kind = None; + self.object(names, required, |s, i| { + if i == 0 { + id = Some(s.string()?); + } else if i == 1 { + kind = Some(s.string()?); + } else if (edge && i == 12) || (!edge && i == 16) { + s.metadata()?; + } else if edge && i == 5 { + s.number(NumberKind::F64, false)?; + } else if (edge && (7..=11).contains(&i)) || (!edge && (9..=12).contains(&i)) { + if edge && i == 7 && s.peek()?.kind == TokenKind::Null { + s.facts.producer_tokens = false; + } + s.number(NumberKind::I64, true)?; + } else if (edge && i == 6) || (!edge && i == 5) { + s.optional_string()?; + } else { + s.string()?; + } + Ok(()) + })?; + (self.emit)(JoinEvent { + domain: u64::from(edge), + id: id.ok_or(ValidationIssue::Schema)?, + kind: JoinKind::Raw { + ordinal, + kind: kind.ok_or(ValidationIssue::Schema)?, + }, + }) + } + fn rows(&mut self, edge: bool) -> Result { + self.expect(TokenKind::ArrayStart)?; + let mut count = 0u64; + while self.peek()?.kind != TokenKind::ArrayEnd { + self.row(edge, count)?; + count = count.checked_add(1).ok_or(ValidationIssue::Overflow)?; + } + self.token()?; + Ok(count) + } + fn partition(&mut self) -> Result<(), ValidationIssue> { + let start = self.peek()?.start; + self.object(&["entry", "nodes", "edges"], 7, |s, i| { + match i { + 0 => s.entry()?, + 1 => s.facts.actual_nodes = s.rows(false)?, + 2 => s.facts.actual_edges = s.rows(true)?, + _ => unreachable!(), + } + Ok(()) + })?; + let end = self.reader.position(); + self.facts.partition = Span { start, end }; + Ok(()) + } +} + +pub(crate) fn scan_artifact( + source: &S, + memory: &CleanupMemory, + emit: &mut dyn FnMut(JoinEvent) -> Result<(), ValidationIssue>, +) -> Result { + let floor = memory.reserve(8192)?; + let reader = MaintenanceJsonReader::new(SourceReader { source, offset: 0 }, memory)?; + let mut scanner = Scanner { + source, + memory, + reader, + peeked: None, + facts: ArtifactFacts::default(), + emit, + _floor: floor, + }; + scanner.object( + &[ + "format_version", + "artifact_key", + "partition_payload_sha256", + "partition", + ], + 15, + |s, i| { + match i { + 0 => { + if s.number(NumberKind::U64, false)? != Some(2) { + return Err(ValidationIssue::Schema); + } + } + 1 => s.facts.key = s.hex_string()?, + 2 => s.facts.payload_digest = s.hex_string()?, + 3 => s.partition()?, + _ => unreachable!(), + } + Ok(()) + }, + )?; + if scanner.reader.next_token()?.is_some() { + return Err(ValidationIssue::Schema); + } + if scanner.facts.declared_nodes != scanner.facts.actual_nodes + || scanner.facts.declared_edges != scanner.facts.actual_edges + { + return Err(ValidationIssue::RawRowsDiffer); + } + if !partition_id_matches(source, scanner.facts.path, scanner.facts.partition_id)? { + return Err(ValidationIssue::RawRowsDiffer); + } + usize::try_from(scanner.facts.declared_nodes).map_err(|_| ValidationIssue::Numeric)?; + usize::try_from(scanner.facts.declared_edges).map_err(|_| ValidationIssue::Numeric)?; + Ok(scanner.facts) +} + +/// Whitespace-compacted wire checksum fact. Typed loader equivalence also needs +/// stable scalar forms, complete schema/defaults, the actual loader Map order, +/// and raw-row equality. Cargo.lock's indexmap alone does not establish that +/// the root runtime (rather than a build dependency) enables preserve_order. +pub(crate) fn wire_checksum_matches( + source: &S, + facts: &ArtifactFacts, + memory: &CleanupMemory, +) -> Result { + let reader = MaintenanceJsonReader::new(SpanReader::new(source, facts.partition)?, memory)?; + Ok(reader.finish()?.1 == facts.payload_digest) +} + +fn sha1_block(state: &mut [u32; 5], block: &[u8; 64]) { + let mut words = [0u32; 80]; + for (i, word) in words[..16].iter_mut().enumerate() { + *word = u32::from_be_bytes(block[i * 4..i * 4 + 4].try_into().unwrap()); + } + for i in 16..80 { + words[i] = (words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16]).rotate_left(1); + } + let [mut a, mut b, mut c, mut d, mut e] = *state; + for (i, word) in words.iter().enumerate() { + let (f, k) = match i { + 0..=19 => ((b & c) | (!b & d), 0x5a827999), + 20..=39 => (b ^ c ^ d, 0x6ed9eba1), + 40..=59 => ((b & c) | (b & d) | (c & d), 0x8f1bbcdc), + _ => (b ^ c ^ d, 0xca62c1d6), + }; + let temp = a + .rotate_left(5) + .wrapping_add(f) + .wrapping_add(e) + .wrapping_add(k) + .wrapping_add(*word); + e = d; + d = c; + c = b.rotate_left(30); + b = a; + a = temp; + } + for (s, v) in state.iter_mut().zip([a, b, c, d, e]) { + *s = s.wrapping_add(v); + } +} +fn partition_id_matches( + source: &S, + path: Span, + id: Span, +) -> Result { + let mut state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; + let mut block = [0u8; 64]; + let mut used = 0; + let mut len = 0u64; + let mut input = Decoded::new(source, path)?; + while let Some(byte) = input.next()? { + block[used] = byte; + used += 1; + len = len.checked_add(1).ok_or(ValidationIssue::Overflow)?; + if used == 64 { + sha1_block(&mut state, &block); + used = 0; + } + } + let bit_len = len.checked_mul(8).ok_or(ValidationIssue::Overflow)?; + block[used] = 0x80; + used += 1; + if used > 56 { + block[used..].fill(0); + sha1_block(&mut state, &block); + block.fill(0); + } else { + block[used..56].fill(0); + } + block[56..].copy_from_slice(&bit_len.to_be_bytes()); + sha1_block(&mut state, &block); + let mut expected = [0u8; 20]; + for i in 0..10 { + let byte = state[i / 4].to_be_bytes()[i % 4]; + expected[2 * i] = b"0123456789abcdef"[(byte >> 4) as usize]; + expected[2 * i + 1] = b"0123456789abcdef"[(byte & 15) as usize]; + } + equals(source, id, &expected) +} + +/// Fixed, caller-owned key addresses (including source incarnation) only. A +/// replay must include every expected/native selected ID, including extras. +#[derive(Debug, Clone, Copy)] +pub(crate) struct WitnessEvent { + pub(crate) domain: u64, + pub(crate) native: bool, + /// Producer source order; lowest nonempty field wins independently. + pub(crate) ordinal: u64, + pub(crate) fields: [Option; 5], +} +pub(crate) trait ReplayWitnesses { + type Key: Copy; + /// Validate each complete row/key before emission, including later + /// duplicate fields that FIRSTNONEMPTY will not retain. + fn replay( + &self, + memory: &CleanupMemory, + emit: &mut dyn FnMut(WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue>; + /// Validate complete decoded keys, including suffixes, in all three calls. + /// Reopening must check the captured incarnation, never refresh its stamp. + fn hash( + &self, + key: Self::Key, + domain: u64, + memory: &CleanupMemory, + ) -> Result<[u8; 32], ValidationIssue>; + fn compare( + &self, + left: Self::Key, + right: Self::Key, + memory: &CleanupMemory, + ) -> Result; + fn empty(&self, key: Self::Key, memory: &CleanupMemory) -> Result; +} +#[derive(Clone, Copy)] +struct WitnessSlot { + key: Option, + domain: u64, + hash: [u8; 32], + native_count: u64, + expected_count: u64, + native: [Option; 4], + expected: [Option; 4], + ordinal: [u64; 4], +} +impl WitnessSlot { + fn empty() -> Self { + Self { + key: None, + domain: 0, + hash: [0; 32], + native_count: 0, + expected_count: 0, + native: [None; 4], + expected: [None; 4], + ordinal: [u64::MAX; 4], + } + } +} +struct WitnessTable { + pointer: std::ptr::NonNull>, + capacity: usize, + layout: std::alloc::Layout, + probes: Cell, + _charge: MemoryReservation, +} +impl WitnessTable { + fn new(memory: &CleanupMemory, capacity: usize) -> Result { + if capacity == 0 { + return Err(ValidationIssue::Resource(ResourceLimit::CeilingExceeded)); + } + let layout = std::alloc::Layout::array::>(capacity) + .map_err(|_| ValidationIssue::Overflow)?; + let charge = memory.reserve(layout.size())?; + // SAFETY: the exact typed layout is precharged; every slot is initialized + // before reading and no references escape table mutation or disposal. + let pointer = + std::ptr::NonNull::new(unsafe { std::alloc::alloc(layout) }.cast::>()) + .ok_or(ValidationIssue::Resource(ResourceLimit::AllocationFailed))?; + let mut table = Self { + pointer, + capacity, + layout, + probes: Cell::new(0), + _charge: charge, + }; + table.clear(); + Ok(table) + } + fn clear(&mut self) { + for i in 0..self.capacity { + self.put(i, WitnessSlot::empty()); + } + } + fn get(&self, i: usize) -> WitnessSlot { + assert!(i < self.capacity); + // SAFETY: i is bounded and every allocated slot was initialized. + unsafe { self.pointer.as_ptr().add(i).read() } + } + fn put(&mut self, i: usize, row: WitnessSlot) { + assert!(i < self.capacity); + // SAFETY: i is bounded; Copy slots own no destructors and no borrow escapes. + unsafe { + self.pointer.as_ptr().add(i).write(row); + } + } + fn find + ?Sized>( + &self, + r: &R, + memory: &CleanupMemory, + event: WitnessEvent, + hash: [u8; 32], + ) -> Result, ValidationIssue> { + let key = event.fields[0].ok_or(ValidationIssue::Schema)?; + let start = + (u64::from_le_bytes(hash[..8].try_into().unwrap()) % self.capacity as u64) as usize; + for delta in 0..self.capacity { + self.probes.set( + self.probes + .get() + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?, + ); + let i = (start + delta) % self.capacity; + let slot = self.get(i); + let Some(old) = slot.key else { + return Ok(Err(i)); + }; + if slot.domain == event.domain + && slot.hash == hash + && r.compare(old, key, memory)? == Ordering::Equal + { + return Ok(Ok(i)); + } + } + Ok(Err(self.capacity)) + } + fn apply + ?Sized>( + &mut self, + r: &R, + memory: &CleanupMemory, + i: usize, + event: WitnessEvent, + hash: [u8; 32], + ) -> Result<(), ValidationIssue> { + let mut slot = self.get(i); + if slot.key.is_none() { + slot.key = Some(event.fields[0].ok_or(ValidationIssue::Schema)?); + slot.domain = event.domain; + slot.hash = hash; + } + if event.native { + slot.native_count = slot + .native_count + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + slot.native.copy_from_slice(&event.fields[1..]); + } else { + slot.expected_count = slot + .expected_count + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + for j in 0..4 { + if let Some(key) = event.fields[j + 1] { + if (slot.expected[j].is_none() || event.ordinal < slot.ordinal[j]) + && !r.empty(key, memory)? + { + slot.expected[j] = Some(key); + slot.ordinal[j] = event.ordinal; + } + } + } + } + self.put(i, slot); + Ok(()) + } + fn verify + ?Sized>( + &self, + r: &R, + memory: &CleanupMemory, + ) -> Result<(), ValidationIssue> { + for i in 0..self.capacity { + let slot = self.get(i); + if slot.key.is_none() { + continue; + } + if slot.native_count != 1 || slot.expected_count == 0 { + return Err(ValidationIssue::RawRowsDiffer); + } + for j in 0..4 { + let native = slot.native[j].ok_or(ValidationIssue::RawRowsDiffer)?; + let equal = if let Some(expected) = slot.expected[j] { + r.compare(native, expected, memory)? == Ordering::Equal + } else { + r.empty(native, memory)? + }; + if !equal { + return Err(ValidationIssue::RawRowsDiffer); + } + } + } + Ok(()) + } +} +impl Drop for WitnessTable { + fn drop(&mut self) { + // SAFETY: this owner holds the original pointer and exact allocation + // layout; Copy slots need no element drops. The charge drops afterward. + unsafe { + std::alloc::dealloc(self.pointer.as_ptr().cast(), self.layout); + } + } +} +fn witness_order( + r: &R, + memory: &CleanupMemory, + left: (u64, R::Key), + right: (u64, R::Key), +) -> Result { + if left.0 != right.0 { + Ok(left.0.cmp(&right.0)) + } else { + r.compare(left.1, right.1, memory) + } +} +/// Exact requested join layout, excluding the caller's source/resolver and +/// decoder owners. Use the real retained Key type, including its full stamp. +pub(crate) fn witness_join_requested_bytes( + capacity: usize, +) -> Result { + if capacity == 0 { + return Err(ValidationIssue::Resource(ResourceLimit::CeilingExceeded)); + } + let floor = std::mem::size_of::>() + .checked_add( + std::mem::size_of::>() + .checked_mul(6) + .ok_or(ValidationIssue::Overflow)?, + ) + .and_then(|n| n.checked_add(std::mem::size_of::>() * 3)) + .and_then(|n| n.checked_add(1024)) + .ok_or(ValidationIssue::Overflow)?; + let layout = std::alloc::Layout::array::>(capacity) + .map_err(|_| ValidationIssue::Overflow)?; + floor + .checked_add(layout.size()) + .ok_or(ValidationIssue::Overflow) +} +/// Hash partitioning handles ordinary inventories; complete-hash collisions use +/// finite lexicographic batches and exact equality. Capacity one makes progress. +/// No key/source inventory, file registry, input-sized decoder or spill is owned. +pub(crate) fn join_selected_witnesses( + r: &R, + memory: &CleanupMemory, + capacity: usize, +) -> Result { + let total = witness_join_requested_bytes::(capacity)?; + let slots = std::alloc::Layout::array::>(capacity) + .map_err(|_| ValidationIssue::Overflow)? + .size(); + let floor = total.checked_sub(slots).ok_or(ValidationIssue::Overflow)?; + let _floor = memory.reserve(floor)?; + let mut table = WitnessTable::new(memory, capacity)?; + let mut stats = RawRowsMatch::default(); + let mut prefix = [0; 32]; + let mut bits = 0; + loop { + table.clear(); + let mut overflow = false; + let mut first = None; + let mut same = true; + stats.passes = stats + .passes + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + r.replay(memory, &mut |event| { + stats.events = stats + .events + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + let hash = r.hash( + event.fields[0].ok_or(ValidationIssue::Schema)?, + event.domain, + memory, + )?; + if !matches_prefix(&hash, &prefix, bits) { + return Ok(()); + } + if let Some(h) = first { + same &= h == hash; + } else { + first = Some(hash); + } + match table.find(r, memory, event, hash)? { + Ok(i) => table.apply(r, memory, i, event, hash)?, + Err(i) if i < capacity => table.apply(r, memory, i, event, hash)?, + _ => overflow = true, + } + Ok(()) + })?; + if overflow && !same && bits < 256 { + bits += 1; + prefix[(bits - 1) / 8] &= !(0x80 >> ((bits - 1) % 8)); + continue; + } + if overflow { + let bucket = first.ok_or(ValidationIssue::Schema)?; + let mut lower = None; + loop { + table.clear(); + stats.passes = stats + .passes + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + let mut selected = 0usize; + r.replay(memory, &mut |event| { + stats.events = stats + .events + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + let key = event.fields[0].ok_or(ValidationIssue::Schema)?; + let hash = r.hash(key, event.domain, memory)?; + if hash != bucket { + return Ok(()); + } + if let Some(bound) = lower { + if witness_order(r, memory, (event.domain, key), bound)? + != Ordering::Greater + { + return Ok(()); + } + } + for i in 0..selected { + table.probes.set( + table + .probes + .get() + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?, + ); + let old = table.get(i); + if old.domain == event.domain + && r.compare(old.key.unwrap(), key, memory)? == Ordering::Equal + { + return Ok(()); + } + } + if selected < capacity { + let mut slot = WitnessSlot::empty(); + slot.key = Some(key); + slot.domain = event.domain; + slot.hash = hash; + table.put(selected, slot); + selected += 1; + } else { + let mut largest = 0; + for i in 1..capacity { + let a = table.get(i); + let b = table.get(largest); + if witness_order( + r, + memory, + (a.domain, a.key.unwrap()), + (b.domain, b.key.unwrap()), + )? == Ordering::Greater + { + largest = i; + } + } + let old = table.get(largest); + if witness_order( + r, + memory, + (event.domain, key), + (old.domain, old.key.unwrap()), + )? == Ordering::Less + { + let mut slot = WitnessSlot::empty(); + slot.key = Some(key); + slot.domain = event.domain; + slot.hash = hash; + table.put(largest, slot); + } + } + Ok(()) + })?; + if selected == 0 { + break; + } + // Selection uses dense slots, so aggregate by exact scan rather + // than open addressing (replacement would introduce holes). + stats.passes = stats + .passes + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + r.replay(memory, &mut |event| { + stats.events = stats + .events + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + let key = event.fields[0].ok_or(ValidationIssue::Schema)?; + let hash = r.hash(key, event.domain, memory)?; + if hash != bucket { + return Ok(()); + } + for i in 0..selected { + table.probes.set( + table + .probes + .get() + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?, + ); + let slot = table.get(i); + if slot.domain == event.domain + && r.compare(slot.key.unwrap(), key, memory)? == Ordering::Equal + { + table.apply(r, memory, i, event, hash)?; + break; + } + } + Ok(()) + })?; + table.verify(r, memory)?; + let mut largest = table.get(0); + for i in 1..selected { + let slot = table.get(i); + if witness_order( + r, + memory, + (slot.domain, slot.key.unwrap()), + (largest.domain, largest.key.unwrap()), + )? == Ordering::Greater + { + largest = slot; + } + } + lower = Some((largest.domain, largest.key.unwrap())); + stats.collision_batches = stats + .collision_batches + .checked_add(1) + .ok_or(ValidationIssue::Overflow)?; + } + } else { + table.verify(r, memory)?; + } + while bits > 0 && (prefix[(bits - 1) / 8] & (0x80 >> ((bits - 1) % 8))) != 0 { + prefix[(bits - 1) / 8] &= !(0x80 >> ((bits - 1) % 8)); + bits -= 1; + } + if bits == 0 { + break; + } + prefix[(bits - 1) / 8] |= 0x80 >> ((bits - 1) % 8); + } + stats.lookup_probes = table.probes.get(); + Ok(stats) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::partition_builder::GraphPartition; + use crate::protocol::ManifestEntry; + use crate::syntax_materializer::{GraphEdgeRow, GraphNodeRow}; + use std::collections::BTreeMap; + use std::io::{Seek, SeekFrom, Write}; + struct TestFile { + path: std::path::PathBuf, + file: Option, + } + impl TestFile { + fn new(bytes: &[u8]) -> Self { + static SEQUENCE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); + let nonce = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos(); + let seq = SEQUENCE.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let path = std::env::temp_dir().join(format!( + "cleanup-validation-source-{}-{nonce}-{seq}", + std::process::id() + )); + let mut file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .open(&path) + .unwrap(); + file.write_all(bytes).unwrap(); + file.sync_all().unwrap(); + Self { + path, + file: Some(file), + } + } + } + impl Drop for TestFile { + fn drop(&mut self) { + drop(self.file.take()); + let _ = std::fs::remove_file(&self.path); + } + } + fn node(id: &str, kind: &str) -> GraphNodeRow { + GraphNodeRow { + id: id.into(), + table: kind.into(), + label: "label".into(), + kind: "definition".into(), + language: "rust".into(), + grammar_version: None, + path: "src/lib.rs".into(), + qualified_name: id.into(), + scope_id: "scope".into(), + line_start: None, + line_end: None, + byte_start: None, + byte_end: None, + tree_sitter_node_type: "identifier".into(), + capture_name: "capture".into(), + summary: "summary".into(), + metadata: serde_json::json!({"z":1,"a":["text",null]}), + } + } + fn edge(id: &str, kind: &str, confidence: f64) -> GraphEdgeRow { + GraphEdgeRow { + id: id.into(), + edge_type: kind.into(), + source_id: "n1".into(), + target_id: "n2".into(), + kind: "reference".into(), + confidence, + field_name: None, + child_index: None, + line_start: None, + line_end: None, + byte_start: None, + byte_end: None, + metadata: serde_json::json!({"z":true,"a":false}), + } + } + fn partition(nodes: Vec, edges: Vec) -> GraphPartition { + let mut node_ids: Vec<_> = nodes.iter().map(|r| r.id.clone()).collect(); + node_ids.sort(); + let mut edge_ids: Vec<_> = edges.iter().map(|r| r.id.clone()).collect(); + edge_ids.sort(); + let entry = ManifestEntry { + path: "src/lib.rs".into(), + content_hash: "hash".into(), + language: "rust".into(), + partition_id: crate::hash::partition_id("src/lib.rs"), + artifact_key: Some("a".repeat(64)), + node_ids, + edge_ids, + node_types: nodes + .iter() + .map(|r| (r.id.clone(), r.table.clone())) + .collect::>(), + edge_types: edges + .iter() + .map(|r| (r.id.clone(), r.edge_type.clone())) + .collect::>(), + node_count: nodes.len(), + edge_count: edges.len(), + materialized_at: "stamp".into(), + }; + GraphPartition { + entry, + nodes, + edges, + } + } + fn envelope(partition: &GraphPartition) -> Vec { + #[derive(serde::Serialize)] + struct Envelope<'a> { + format_version: u64, + artifact_key: &'a str, + partition_payload_sha256: String, + partition: &'a GraphPartition, + } + let checksum = Sha256::digest(serde_json::to_vec(partition).unwrap()); + serde_json::to_vec_pretty(&Envelope { + format_version: 2, + artifact_key: partition.entry.artifact_key.as_deref().unwrap(), + partition_payload_sha256: checksum.iter().map(|b| format!("{b:02x}")).collect(), + partition, + }) + .unwrap() + } + fn facts(input: &[u8], memory: &CleanupMemory) -> Result { + scan_artifact(input, memory, &mut |_| Ok(())) + } + fn joined(partition: &GraphPartition, capacity: usize, force: bool) -> bool { + let bytes = envelope(partition); + let memory = CleanupMemory::new(131072).unwrap(); + join_inner( + &ArtifactEvents { + source: bytes.as_slice(), + }, + &memory, + capacity, + force, + ) + .is_ok() + } + + #[test] + fn dc_validation_exact_join_matches_raw_rows_duplicates_last_wins_and_extras() { + let p = partition( + vec![node("same", "Old"), node("z", "Z"), node("same", "New")], + vec![edge("same-edge", "Old", 0.5), edge("same-edge", "New", 1.0)], + ); + assert!(p.validate_raw_rows().is_ok()); + for forced in [false, true] { + for capacity in [1, 2, 32] { + assert!(joined(&p, capacity, forced)); + let mut bad = p.clone(); + bad.entry + .node_types + .insert("map-only".into(), "Foreign".into()); + assert!(bad.validate_raw_rows().is_err()); + assert!(!joined(&bad, capacity, forced)); + let mut bad = p.clone(); + bad.entry.node_ids.pop(); + assert!(!joined(&bad, capacity, forced)); + let mut bad = p.clone(); + bad.entry.node_ids.push("zz-array-only".into()); + bad.entry.node_ids.sort(); + assert!(!joined(&bad, capacity, forced)); + let mut bad = p.clone(); + bad.entry.node_types.insert("same".into(), "Old".into()); + assert!(!joined(&bad, capacity, forced)); + let mut bad = p.clone(); + bad.entry + .edge_types + .insert("edge-map-only".into(), "Unknown".into()); + assert!(!joined(&bad, capacity, forced)); + } + } + } + #[test] + fn dc_validation_oracle_schema_hash_numeric_and_nonfinite() { + let memory = CleanupMemory::new(131072).unwrap(); + for confidence in [ + 0.5, + 1.0, + -0.0, + f64::NAN, + f64::INFINITY, + f64::NEG_INFINITY, + 0.9966544878724393, + ] { + let p = partition(vec![node("n", "N")], vec![edge("e", "E", confidence)]); + assert!(p.validate_raw_rows().is_ok()); + let bytes = envelope(&p); + let f = facts(&bytes, &memory).unwrap(); + assert!(f.producer_tokens); + assert!(wire_checksum_matches(bytes.as_slice(), &f, &memory).unwrap()); + assert_eq!(f.typed_numbers, confidence.is_finite()); + let literal = serde_json::to_vec(&confidence).unwrap(); + let oracle = number_facts(&literal, NumberKind::F64, &memory).unwrap(); + assert_eq!(f.stable_numbers, oracle.stable); + } + let typed = number_facts(b"0.9966544878724393", NumberKind::F64, &memory).unwrap(); + let value = number_facts(b"0.9966544878724393", NumberKind::Value, &memory).unwrap(); + let parsed: f64 = serde_json::from_slice(b"0.9966544878724393").unwrap(); + let serialized = serde_json::to_string(&parsed).unwrap(); + eprintln!( + "pinned scalar oracle: {serialized}; bits={:x}", + parsed.to_bits() + ); + assert!(typed.typed && value.typed); + assert_eq!(typed.stable, serialized == "0.9966544878724393"); + assert_eq!(typed.stable, value.stable); + assert!( + !typed.stable, + "pinned default parser feature drift must be reviewed" + ); + } + #[test] + fn dc_validation_control_escape_oracle_and_unbounded_strings() { + let memory = CleanupMemory::new(131072).unwrap(); + for value in 0u8..=31 { + let text = String::from_utf8(vec![value]).unwrap(); + let bytes = serde_json::to_vec(&text).unwrap(); + assert!(producer_string( + bytes.as_slice(), + Span { + start: 0, + end: bytes.len() as u64 + } + ) + .unwrap()); + } + for text in ["é😀\u{2028}\u{2029}", "\"\\/", ""] { + let bytes = serde_json::to_vec(text).unwrap(); + assert!(producer_string( + bytes.as_slice(), + Span { + start: 0, + end: bytes.len() as u64 + } + ) + .unwrap()); + } + for bytes in [br#""\u0061""#.as_slice(), br#""\/""#, br#""\u000A""#] { + assert!(!producer_string( + bytes, + Span { + start: 0, + end: bytes.len() as u64 + } + ) + .unwrap()); + } + let mut p = partition(vec![node("long", "N")], vec![]); + p.nodes[0].summary = "é".repeat(100_000); + let bytes = envelope(&p); + let before = memory.used(); + let f = facts(&bytes, &memory).unwrap(); + assert!(f.producer_tokens && f.stable_numbers); + join_raw_rows( + &ArtifactEvents { + source: bytes.as_slice(), + }, + &memory, + 8, + ) + .unwrap(); + assert_eq!(memory.used(), before); + assert!(memory.peak() < 131072); + } + #[test] + fn dc_validation_metadata_duplicate_keys_and_preserved_order() { + let memory = CleanupMemory::new(131072).unwrap(); + let p = partition(vec![node("n", "N")], vec![]); + let bytes = envelope(&p); + let f = facts(&bytes, &memory).unwrap(); + assert!(f.producer_tokens); + let text = String::from_utf8(bytes).unwrap(); + let altered = text.replacen("\"z\": 1", "\"z\": 1, \"z\": 2", 1); + assert_ne!(text, altered); + assert!(join_raw_rows( + &ArtifactEvents { + source: altered.as_bytes() + }, + &memory, + 1 + ) + .is_err()); + } + #[test] + fn dc_validation_actual_runtime_map_oracle_and_order_fact() { + let mut map = serde_json::Map::new(); + map.insert("z".into(), serde_json::Value::Null); + map.insert("a".into(), serde_json::Value::Null); + let actual = serde_json::to_string(&map).unwrap(); + eprintln!("actual ROOT serde_json Map serialization: {actual}"); + assert_eq!( + actual, r#"{"a":null,"z":null}"#, + "runtime dependency feature drift needs explicit review" + ); + let memory = CleanupMemory::new(131072).unwrap(); + let mut p = partition(vec![node("n", "N")], vec![]); + p.nodes[0].metadata = serde_json::json!({"a":1,"z":2}); + let text = String::from_utf8(envelope(&p)).unwrap(); + assert!( + facts(text.as_bytes(), &memory) + .unwrap() + .metadata_keys_sorted + ); + let start = text.find("\"metadata\": {").unwrap(); + let end = start + text[start..].find('}').unwrap() + 1; + let mut unsorted = text.clone(); + unsorted.replace_range(start..end, "\"metadata\": {\"z\":2,\"a\":1}"); + let f = facts(unsorted.as_bytes(), &memory).unwrap(); + assert!(!f.metadata_keys_sorted); + assert!(f.stable_numbers); + assert!(!wire_checksum_matches(unsorted.as_bytes(), &f, &memory).unwrap()); + } + #[test] + fn dc_validation_hash_prefix_replay_and_capacity_one_collision_progress() { + let p = partition( + (0..64).map(|i| node(&format!("n{i:03}"), "N")).collect(), + vec![], + ); + let bytes = envelope(&p); + let memory = CleanupMemory::new(131072).unwrap(); + let stats = join_raw_rows( + &ArtifactEvents { + source: bytes.as_slice(), + }, + &memory, + 16, + ) + .unwrap(); + assert!(stats.passes > 1); + let small = partition(vec![node("a", "A"), node("b", "B")], vec![]); + let bytes = envelope(&small); + let stats = join_inner( + &ArtifactEvents { + source: bytes.as_slice(), + }, + &memory, + 1, + true, + ) + .unwrap(); + assert!(stats.collision_batches >= 2); + assert!(stats.passes < 1000); + assert!(join_raw_rows( + &ArtifactEvents { + source: bytes.as_slice() + }, + &memory, + 0 + ) + .is_err()); + } + #[test] + fn dc_validation_partition_id_streaming_oracle_and_schema_rejects() { + for len in [0, 1, 55, 56, 63, 64, 65, 127, 4097] { + let path = "é".repeat(len); + let id = crate::hash::partition_id(&path); + let a = serde_json::to_vec(&path).unwrap(); + let b = serde_json::to_vec(&id).unwrap(); + let mut source = a.clone(); + let first = Span { + start: 0, + end: a.len() as u64, + }; + let second = Span { + start: source.len() as u64, + end: (source.len() + b.len()) as u64, + }; + source.extend(b); + assert!(partition_id_matches(source.as_slice(), first, second).unwrap()); + } + let memory = CleanupMemory::new(131072).unwrap(); + let mut p = partition(vec![], vec![]); + p.entry.partition_id = "bad".into(); + assert!(p.validate_raw_rows().is_err()); + assert!(facts(&envelope(&p), &memory).is_err()); + let p = partition(vec![], vec![]); + let mut bytes = envelope(&p); + bytes.extend_from_slice(b"null"); + assert!(facts(&bytes, &memory).is_err()); + } + #[test] + fn dc_validation_metadata_value_numeric_uses_same_oracle() { + let memory = CleanupMemory::new(131072).unwrap(); + let mut p = partition(vec![node("n", "N")], vec![]); + p.nodes[0].metadata = serde_json::json!({"value":0.9966544878724393}); + let bytes = envelope(&p); + let f = facts(&bytes, &memory).unwrap(); + let token = serde_json::to_vec(&p.nodes[0].metadata["value"]).unwrap(); + assert_eq!( + f.stable_numbers, + number_facts(&token, NumberKind::Value, &memory) + .unwrap() + .stable + ); + assert!(!f.stable_numbers); + } + #[test] + fn dc_validation_full_typed_loader_depth_oracle() { + #[derive(serde::Deserialize)] + struct Envelope { + format_version: u64, + artifact_key: String, + partition_payload_sha256: String, + partition: GraphPartition, + } + let memory = CleanupMemory::new(131072).unwrap(); + let mut accepted = 0; + let mut rejected = 0; + for depth in [120, 121, 122, 123, 124, 125, 126, 127, 128] { + let mut value = serde_json::Value::Null; + for _ in 0..depth { + value = serde_json::Value::Array(vec![value]); + } + let mut p = partition(vec![node("n", "N")], vec![]); + p.nodes[0].metadata = value; + let bytes = envelope(&p); + let typed = serde_json::from_slice::(&bytes); + assert_eq!( + facts(&bytes, &memory).is_ok(), + typed.is_ok(), + "metadata depth {depth}" + ); + if let Ok(typed) = typed { + assert_eq!(typed.format_version, 2); + assert_eq!(typed.artifact_key, "a".repeat(64)); + assert_eq!(typed.partition_payload_sha256.len(), 64); + assert!(typed.partition.validate_raw_rows().is_ok()); + accepted += 1; + } else { + rejected += 1; + } + } + assert!(accepted > 0 && rejected > 0); + } + #[test] + fn dc_validation_borrowed_fd_position_and_exact_bounded_reads() { + let mut fixture = TestFile::new(b"0123456789abcdef"); + let memory = CleanupMemory::new(4096).unwrap(); + fixture + .file + .as_mut() + .unwrap() + .seek(SeekFrom::Start(3)) + .unwrap(); + { + let source = BorrowedFileSource::new(fixture.file.as_mut().unwrap(), &memory).unwrap(); + let mut bytes = [0u8; 4]; + assert_eq!(source.len(), 16); + assert_eq!(source.read_at(7, &mut bytes).unwrap(), 4); + assert_eq!(&bytes, b"789a"); + assert_eq!( + source.read_at(17, &mut bytes), + Err(ValidationIssue::InvalidSpan) + ); + } + #[cfg(unix)] + assert_eq!(fixture.file.as_mut().unwrap().stream_position().unwrap(), 3); + #[cfg(windows)] + assert_eq!( + fixture.file.as_mut().unwrap().stream_position().unwrap(), + 11 + ); + } + #[test] + fn dc_validation_borrowed_fd_schema_replay_without_payload_copy() { + let p = partition(vec![node("n", "N")], vec![edge("e", "E", 0.5)]); + let bytes = envelope(&p); + let mut fixture = TestFile::new(&bytes); + let memory = CleanupMemory::new(131072).unwrap(); + let before = memory.used(); + { + let source = BorrowedFileSource::new(fixture.file.as_mut().unwrap(), &memory).unwrap(); + let f = facts_from_source(&source, &memory).unwrap(); + assert!(f.stable_numbers && f.metadata_keys_sorted); + assert!(wire_checksum_matches(&source, &f, &memory).unwrap()); + join_raw_rows(&ArtifactEvents { source: &source }, &memory, 8).unwrap(); + } + assert_eq!(memory.used(), before); + assert_eq!(std::fs::read(&fixture.path).unwrap(), bytes); + } + fn facts_from_source( + source: &S, + memory: &CleanupMemory, + ) -> Result { + scan_artifact(source, memory, &mut |_| Ok(())) + } + #[test] + fn dc_validation_cross_source_exact_unicode_long_strings_and_memory() { + let memory = CleanupMemory::new(16384).unwrap(); + let left = br#""\uD83D\uDE00\n""#; + let right = "\"😀\\n\"".as_bytes(); + let span = |n: usize| Span { + start: 0, + end: n as u64, + }; + assert_eq!( + compare_decoded_spans( + left.as_slice(), + span(left.len()), + right, + span(right.len()), + &memory + ) + .unwrap(), + Ordering::Equal + ); + let a = serde_json::to_vec(&format!("{}a", "é".repeat(100_000))).unwrap(); + let b = serde_json::to_vec(&format!("{}b", "é".repeat(100_000))).unwrap(); + let floor = memory.used(); + assert_eq!( + compare_decoded_spans( + a.as_slice(), + span(a.len()), + b.as_slice(), + span(b.len()), + &memory + ) + .unwrap(), + Ordering::Less + ); + assert_eq!(memory.used(), floor); + assert!(memory.peak() < 16384); + let small = CleanupMemory::new(64).unwrap(); + let used = small.used(); + assert!(compare_decoded_spans( + left.as_slice(), + span(left.len()), + right, + span(right.len()), + &small + ) + .is_err()); + assert_eq!(small.used(), used); + } + #[test] + fn dc_validation_native_json_stream_keys_hash_unicode_nul_and_suffixes() { + let memory = CleanupMemory::new(4096).unwrap(); + let floor = memory.used(); + let span = |bytes: &[u8]| Span { + start: 0, + end: bytes.len() as u64, + }; + for text in [ + String::new(), + "a\0é😀".to_owned(), + format!("{}😀\0é", "x".repeat(255)), + "é".repeat(100_000), + ] { + let native = text.as_bytes(); + let json = serde_json::to_vec(&text).unwrap(); + assert_eq!( + compare_native_json(native, span(native), json.as_slice(), span(&json), &memory), + Ok(Ordering::Equal) + ); + let a = + key_stream_hash(native, span(native), KeyEncoding::NativeUtf8, 7, &memory).unwrap(); + assert_eq!( + a, + key_stream_hash( + json.as_slice(), + span(&json), + KeyEncoding::JsonDecoded, + 7, + &memory + ) + .unwrap() + ); + assert_ne!( + a, + key_stream_hash(native, span(native), KeyEncoding::NativeUtf8, 8, &memory).unwrap() + ); + } + let json = br#""b""#; + for native in [ + b"a\xff".as_slice(), + b"a\xc0\x80".as_slice(), + b"a\xed\xa0\x80".as_slice(), + b"a\xf4\x90\x80\x80".as_slice(), + b"a\xe2\x82".as_slice(), + ] { + assert!(compare_native_json( + native, + span(native), + json.as_slice(), + span(json), + &memory + ) + .is_err()); + assert!( + key_stream_hash(native, span(native), KeyEncoding::NativeUtf8, 7, &memory).is_err() + ); + } + let native = b"a"; + let malformed = br#""b\q""#; + assert!(compare_native_json( + native.as_slice(), + span(native), + malformed.as_slice(), + span(malformed), + &memory + ) + .is_err()); + assert_eq!( + compare_native_json( + b"azzz".as_slice(), + span(b"azzz"), + br#""baaa""#.as_slice(), + span(br#""baaa""#), + &memory + ), + Ok(Ordering::Less) + ); + assert_eq!(memory.used(), floor); + let small = CleanupMemory::new(64).unwrap(); + assert!(compare_native_json( + native.as_slice(), + span(native), + json.as_slice(), + span(json), + &small + ) + .is_err()); + } + #[test] + fn dc_validation_cross_source_propagates_late_read_failure_after_ordering() { + struct LateFailure<'a>(&'a [u8]); + impl ByteSource for LateFailure<'_> { + fn len(&self) -> u64 { + self.0.len() as u64 + } + fn read_at(&self, offset: u64, output: &mut [u8]) -> Result { + if offset >= 256 { + return Err(ValidationIssue::Io(io::ErrorKind::PermissionDenied)); + } + self.0.read_at(offset, output) + } + } + let memory = CleanupMemory::new(4096).unwrap(); + let bytes = serde_json::to_vec(&format!("a{}", "x".repeat(512))).unwrap(); + let failing = LateFailure(&bytes); + let long = Span { + start: 0, + end: bytes.len() as u64, + }; + let short = Span { start: 0, end: 3 }; + assert_eq!( + compare_decoded_spans(&failing, long, br#""b""#.as_slice(), short, &memory), + Err(ValidationIssue::Io(io::ErrorKind::PermissionDenied)) + ); + assert_eq!( + compare_decoded_spans(br#""b""#.as_slice(), short, &failing, long, &memory), + Err(ValidationIssue::Io(io::ErrorKind::PermissionDenied)) + ); + } + #[test] + fn dc_validation_cross_source_rejects_invalid_utf8_and_incomplete_spans() { + let memory = CleanupMemory::new(4096).unwrap(); + let invalid = b"\"\xff\""; + let span = Span { start: 0, end: 3 }; + assert!( + compare_decoded_spans(invalid.as_slice(), span, invalid.as_slice(), span, &memory) + .is_err() + ); + let valid = br#""a""#; + assert!(compare_decoded_spans( + valid.as_slice(), + Span { start: 0, end: 2 }, + valid.as_slice(), + span, + &memory + ) + .is_err()); + } + #[test] + fn dc_validation_cross_source_validates_suffixes_after_first_difference() { + let memory = CleanupMemory::new(4096).unwrap(); + let floor = memory.used(); + let span = |s: &[u8]| Span { + start: 0, + end: s.len() as u64, + }; + for (left, right, error) in [ + ( + b"\"a\xff\"".as_slice(), + b"\"b\"".as_slice(), + ValidationIssue::NonProducer, + ), + ( + b"\"a".as_slice(), + b"\"b\"".as_slice(), + ValidationIssue::InvalidSpan, + ), + ( + b"\"a\"trailing".as_slice(), + b"\"b\"".as_slice(), + ValidationIssue::InvalidSpan, + ), + ( + b"\"\"".as_slice(), + b"\"a\xff\"".as_slice(), + ValidationIssue::NonProducer, + ), + ( + br#""a\q""#.as_slice(), + br#""b""#.as_slice(), + ValidationIssue::NonProducer, + ), + ( + b"\"a\x01\"".as_slice(), + br#""b""#.as_slice(), + ValidationIssue::NonProducer, + ), + ( + br#""a\uD800""#.as_slice(), + br#""b""#.as_slice(), + ValidationIssue::NonProducer, + ), + ] { + assert_eq!( + compare_decoded_spans(left, span(left), right, span(right), &memory), + Err(error) + ); + assert_eq!( + compare_decoded_spans(right, span(right), left, span(left), &memory), + Err(error) + ); + } + for (left, right, expected) in [ + ( + br#""azzz""#.as_slice(), + br#""baaa""#.as_slice(), + Ordering::Less, + ), + ( + br#""azzz""#.as_slice(), + br#""a""#.as_slice(), + Ordering::Greater, + ), + (br#""""#.as_slice(), br#""a""#.as_slice(), Ordering::Less), + ] { + assert_eq!( + compare_decoded_spans(left, span(left), right, span(right), &memory).unwrap(), + expected + ); + } + assert_eq!(memory.used(), floor); + } + #[test] + fn dc_validation_sparse_lookup_work_and_forced_collision_probe() { + let memory = CleanupMemory::new(4 * 1024 * 1024).unwrap(); + let p = partition( + (0..64).map(|i| node(&format!("n{i:03}"), "N")).collect(), + vec![], + ); + let bytes = envelope(&p); + let started = std::time::Instant::now(); + let stats = join_raw_rows( + &ArtifactEvents { + source: bytes.as_slice(), + }, + &memory, + 16384, + ) + .unwrap(); + eprintln!("normal join bytes={} capacity=16384 passes={} events={} lookup_probes={} elapsed_us={}",bytes.len(),stats.passes,stats.events,stats.lookup_probes,started.elapsed().as_micros()); + assert_eq!(stats.passes, 1); + assert!( + stats.lookup_probes < stats.events * 3, + "a missing key must stop at the first empty slot, not scan all16384 slots" + ); + assert!(memory.peak() < 4 * 1024 * 1024); + let small = partition( + vec![node("a", "A"), node("b", "B"), node("a", "Latest")], + vec![], + ); + let bytes = envelope(&small); + let stats = join_inner( + &ArtifactEvents { + source: bytes.as_slice(), + }, + &memory, + 1, + true, + ) + .unwrap(); + eprintln!( + "forced collision passes={} events={} probes={} batches={}", + stats.passes, stats.events, stats.lookup_probes, stats.collision_batches + ); + assert!(stats.collision_batches > 0); + assert!(stats.lookup_probes > 0); + assert!(small.validate_raw_rows().is_ok()); + let mut extra = small.clone(); + extra + .entry + .node_types + .insert("map-only-extra".into(), "N".into()); + assert!(!joined(&extra, 1, true)); + } +} + +#[cfg(test)] +mod witness_tests { + use super::*; + #[derive(Clone, Copy)] + struct Key(&'static str); + struct Replay { + rows: Vec>, + collision: bool, + } + impl ReplayWitnesses for Replay { + type Key = Key; + fn replay( + &self, + _: &CleanupMemory, + emit: &mut dyn FnMut(WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue> { + for row in &self.rows { + emit(*row)?; + } + Ok(()) + } + fn hash( + &self, + key: Key, + domain: u64, + _: &CleanupMemory, + ) -> Result<[u8; 32], ValidationIssue> { + if self.collision { + Ok([0; 32]) + } else { + let mut hash = Sha256::new(); + hash.update(domain.to_le_bytes()); + hash.update(key.0.as_bytes()); + Ok(hash.finalize().into()) + } + } + fn compare( + &self, + left: Key, + right: Key, + _: &CleanupMemory, + ) -> Result { + Ok(left.0.cmp(right.0)) + } + fn empty(&self, key: Key, _: &CleanupMemory) -> Result { + Ok(key.0.is_empty()) + } + } + fn row(id: &'static str, native: bool, ordinal: u64, value: &'static str) -> WitnessEvent { + WitnessEvent { + domain: 0, + native, + ordinal, + fields: [ + Some(Key(id)), + Some(Key(value)), + Some(Key("kind")), + Some(Key("path")), + Some(Key("canonical")), + ], + } + } + #[test] + fn dc_witness_first_nonempty_all_ids_multiplicity_and_capacity_one_collisions() { + for collision in [false, true] { + for capacity in [1, 2, 10] { + let memory = CleanupMemory::new(100_000).unwrap(); + let floor = memory.used(); + let replay = Replay { + collision, + rows: vec![ + row("b", false, 0, ""), + row("a", true, 0, "first"), + row("b", true, 0, "first"), + row("b", false, 2, "later"), + row("a", false, 1, "first"), + row("b", false, 1, "first"), + ], + }; + let facts = join_selected_witnesses(&replay, &memory, capacity).unwrap(); + assert!(facts.passes >= 1); + assert_eq!(memory.used(), floor); + let mut bad = replay; + bad.rows.push(row("extra", true, 0, "first")); + assert_eq!( + join_selected_witnesses(&bad, &memory, capacity).unwrap_err(), + ValidationIssue::RawRowsDiffer + ); + bad.rows.pop(); + bad.rows.push(row("extra", false, 0, "first")); + assert_eq!( + join_selected_witnesses(&bad, &memory, capacity).unwrap_err(), + ValidationIssue::RawRowsDiffer + ); + bad.rows.pop(); + bad.rows.push(row("a", true, 0, "first")); + assert_eq!( + join_selected_witnesses(&bad, &memory, capacity).unwrap_err(), + ValidationIssue::RawRowsDiffer + ); + } + } + } + #[test] + fn dc_witness_each_field_chooses_first_nonempty_and_allocation_failure() { + let memory = CleanupMemory::new(100_000).unwrap(); + let mut first = row("id", false, 0, ""); + first.fields[2] = Some(Key("")); + let second = row("id", false, 1, "value"); + let native = row("id", true, 0, "value"); + let replay = Replay { + rows: vec![first, second, native], + collision: true, + }; + assert!(join_selected_witnesses(&replay, &memory, 1).is_ok()); + assert!(join_selected_witnesses(&replay, &memory, 0).is_err()); + let small = CleanupMemory::new(64).unwrap(); + assert!(join_selected_witnesses(&replay, &small, 1).is_err()); + assert_eq!(small.used(), CleanupMemory::new(64).unwrap().used()); + } +} + +#[cfg(test)] +mod witness_large_tests { + use super::*; + struct Large { + keys: Vec, + collision: bool, + } + impl ReplayWitnesses for Large { + type Key = usize; + fn replay( + &self, + _: &CleanupMemory, + emit: &mut dyn FnMut(WitnessEvent) -> Result<(), ValidationIssue>, + ) -> Result<(), ValidationIssue> { + for id in 4..self.keys.len() { + for native in [false, true] { + emit(WitnessEvent { + domain: 3, + native, + ordinal: id as u64, + fields: [Some(id), Some(1), Some(1), Some(2), Some(3)], + })?; + } + } + Ok(()) + } + fn hash( + &self, + key: usize, + domain: u64, + _: &CleanupMemory, + ) -> Result<[u8; 32], ValidationIssue> { + if self.collision { + Ok([0; 32]) + } else { + let mut hash = Sha256::new(); + hash.update(domain.to_le_bytes()); + hash.update(self.keys[key].as_bytes()); + Ok(hash.finalize().into()) + } + } + fn compare( + &self, + a: usize, + b: usize, + _: &CleanupMemory, + ) -> Result { + Ok(self.keys[a].cmp(&self.keys[b])) + } + fn empty(&self, key: usize, _: &CleanupMemory) -> Result { + Ok(self.keys[key].is_empty()) + } + } + #[test] + fn dc_witness_large_inventory_peak_independent_of_row_count_and_collision_progress() { + let mut peaks = Vec::new(); + for count in [17, 4097] { + let mut keys = vec!["".into(), "value".into(), "path".into(), "canonical".into()]; + keys.extend((0..count).map(|i| format!("id-{i:08}"))); + let replay = Large { + keys, + collision: false, + }; + let memory = CleanupMemory::new(100_000).unwrap(); + let before = memory.used(); + let result = join_selected_witnesses(&replay, &memory, 32).unwrap(); + assert!(result.lookup_probes > 0); + assert_eq!(memory.used(), before); + peaks.push(memory.peak()); + } + assert_eq!(peaks[0], peaks[1]); + let mut keys = vec!["".into(), "value".into(), "path".into(), "canonical".into()]; + keys.extend((0..33).map(|i| format!("id-{i:08}"))); + let replay = Large { + keys, + collision: true, + }; + let memory = CleanupMemory::new(100_000).unwrap(); + let result = join_selected_witnesses(&replay, &memory, 1).unwrap(); + assert_eq!(result.collision_batches, 33); + assert!(result.passes <= 70); + } +} + +#[cfg(test)] +mod projected_key_tests { + use super::*; + #[test] + fn sha1_decoded_native_and_json_match_actual_stable_id_digest() { + let memory = CleanupMemory::new(32768).unwrap(); + let expected = [ + 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, + 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d, + ]; + assert_eq!( + key_sha1_prefix20( + &b"abc"[..], + Span { start: 0, end: 3 }, + KeyEncoding::NativeUtf8, + &memory + ) + .unwrap(), + expected + ); + let quoted = br#""a\u0062c""#; + assert_eq!( + key_sha1_prefix20( + "ed[..], + Span { + start: 0, + end: quoted.len() as u64 + }, + KeyEncoding::JsonDecoded, + &memory + ) + .unwrap(), + expected + ); + } + #[test] + fn projected_key_never_truncates_or_skips_invalid_suffix() { + let memory = CleanupMemory::new(32768).unwrap(); + let json = br#""a\u0062c""#; + let mut out = [0; 3]; + assert_eq!( + copy_decoded_key( + &json[..], + Span { + start: 0, + end: json.len() as u64 + }, + KeyEncoding::JsonDecoded, + &mut out, + &memory + ) + .unwrap(), + 3 + ); + assert_eq!(&out, b"abc"); + let mut tiny = [0; 1]; + assert_eq!( + copy_decoded_key( + &json[..], + Span { + start: 0, + end: json.len() as u64 + }, + KeyEncoding::JsonDecoded, + &mut tiny, + &memory + ), + Err(ValidationIssue::Schema) + ); + let bad = b"\"ab\xff\""; + assert_eq!( + copy_decoded_key( + &bad[..], + Span { + start: 0, + end: bad.len() as u64 + }, + KeyEncoding::JsonDecoded, + &mut tiny, + &memory + ), + Err(ValidationIssue::NonProducer) + ); + } +} + +#[cfg(test)] +mod encoded_key_comparison_tests { + use super::*; + #[test] + fn encoded_keys_compare_all_modes_and_finish_invalid_suffixes() { + let memory = CleanupMemory::new(32768).unwrap(); + for (left, le) in [ + (&b"abc"[..], KeyEncoding::NativeUtf8), + (&br#""a\u0062c""#[..], KeyEncoding::JsonDecoded), + ] { + for (right, re) in [ + (&b"abc"[..], KeyEncoding::NativeUtf8), + (&br#""abc""#[..], KeyEncoding::JsonDecoded), + ] { + assert_eq!( + compare_encoded_keys( + left, + Span { + start: 0, + end: left.len() as u64 + }, + le, + right, + Span { + start: 0, + end: right.len() as u64 + }, + re, + &memory + ) + .unwrap(), + Ordering::Equal + ); + } + } + let bad = &b"a\xff"[..]; + let right = &b"b"[..]; + assert_eq!( + compare_encoded_keys( + bad, + Span { + start: 0, + end: bad.len() as u64 + }, + KeyEncoding::NativeUtf8, + right, + Span { start: 0, end: 1 }, + KeyEncoding::NativeUtf8, + &memory + ), + Err(ValidationIssue::NonProducer) + ); + } +} diff --git a/src/storage/maintenance_workspace_gate.rs b/src/storage/maintenance_workspace_gate.rs new file mode 100644 index 00000000..40fd8b3e --- /dev/null +++ b/src/storage/maintenance_workspace_gate.rs @@ -0,0 +1,1329 @@ +//! Stable parent controls for closed workspace removal. The parent survives +//! child removal; its fixed intent is never stored in the child being removed. +use super::locks::{ + self, FileIdentity, FixedControlError, FixedControlProblem, LockMode, RootedDirectory, +}; +use super::maintenance_budget::MemoryBox; +use super::maintenance_controls as c; +use super::maintenance_ownership::{ReferenceDomain, RootCreationAuthority}; +use crate::error::NativeError; +use sha2::{Digest, Sha256}; +use std::io::Write; +use std::path::{Path, PathBuf}; + +const DATA: [&[u8]; 2] = [b".workspace-removal.data0", b".workspace-removal.data1"]; +const SELECTOR: &[u8] = b".workspace-removal.selector"; +const BINDING: &[u8] = b".workspace-removal.binding"; +const RECORD_BYTES: usize = 2048; +const NAMES: [&[u8]; 16] = [ + b".workspace.binding", + b"lease.lock", + b".workspace-writers.lock", + b".publication.binding", + b".publication.selector", + b".publication.data0", + b".publication.data1", + b".publication.stream", + b".workspace.domain.json", + b".workspace.parent.json", + b".reference-scope.json", + b"journal.json", + b"cleanup-barrier.json", + b".workspace-origins", + b"staging", + b"candidate", +]; +fn invalid() -> NativeError { + NativeError::InvalidInput("workspace_removal_unverified".into()) +} +#[track_caller] +fn fixed_invalid() -> FixedControlError { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!( + "workspace removal rejected at {}", + std::panic::Location::caller() + ); + } + FixedControlError::Invalid(FixedControlProblem::InvalidRecord) +} +fn fixed_native(error: NativeError) -> FixedControlError { + match error { + NativeError::Io(error) => error.into(), + _ => fixed_invalid(), + } +} +fn id_into(bytes: &mut [u8], id: FileIdentity) { + let (a, b) = id.fixed_words(); + bytes[..8].copy_from_slice(&a.to_le_bytes()); + bytes[8..16].copy_from_slice(&b.to_le_bytes()); +} +fn id_from(bytes: &[u8]) -> FileIdentity { + FileIdentity::from_fixed_words( + u64::from_le_bytes(bytes[..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + ) +} +fn image(file: &std::fs::File) -> std::io::Result<[u8; N]> { + if file.metadata()?.len() != N as u64 { + return Err(std::io::ErrorKind::InvalidData.into()); + } + let mut bytes = [0; N]; + locks::read_fixed_range(file, 0, &mut bytes)?; + Ok(bytes) +} +fn create_image( + root: &RootedDirectory, + name: &[u8], + bytes: &[u8], +) -> Result { + let mut file = root.create_new(name)?; + file.write_all(bytes)?; + file.sync_all()?; + let id = locks::file_identity(&file)?; + if file.metadata()?.len() != bytes.len() as u64 { + return Err(invalid()); + } + let mut read = [0u8; c::SELECTOR_BYTES]; + locks::read_fixed_range(&file, 0, &mut read[..bytes.len()])?; + if read[..bytes.len()] != *bytes { + return Err(invalid()); + } + Ok(id) +} +/// Called only while genuine fresh destination and actual writer/root Exclusive +/// ownership are held. Existing names are never initialized, repaired or adopted. +pub(crate) fn initialize_fresh_workspace_gate( + fresh: &super::layout::FreshReferenceRoot, + writer: &locks::WriterLease, + creation: &RootCreationAuthority<'_>, +) -> Result<(), NativeError> { + if !writer.verifies(writer.path(), LockMode::Exclusive)? + || locks::path_identity(fresh.path())? != fresh.identity() + { + return Err(invalid()); + } + let destination = RootedDirectory::open(fresh.path(), fresh.identity())?; + let gate_path = fresh.path().join(".workspace-startup.lock"); + let gate = + locks::create_new_coordination_in(&destination, b".workspace-startup.lock", &gate_path)?; + let nonce = creation.incarnation()?; + gate.initialize_creation_incarnation(nonce)?; + let root = RootedDirectory::open(creation.root(), creation.root_identity())?; + let mut slot = [0; c::SLOT_BYTES]; + let fact = c::encode_slot(&mut slot, &nonce, 0, &RemovalState::Idle.encode()) + .map_err(|_| invalid())?; + let data0 = create_image(&root, DATA[0], &slot)?; + c::encode_slot(&mut slot, &nonce, 0, b"").map_err(|_| invalid())?; + let data1 = create_image(&root, DATA[1], &slot)?; + let mut selector = [0; c::SELECTOR_BYTES]; + c::encode_commit( + &mut selector[..c::COMMIT_BYTES], + &nonce, + c::CommitFact { + generation: 0, + active: 0, + slot_digest: fact.digest, + }, + ) + .map_err(|_| invalid())?; + let selector_id = create_image(&root, SELECTOR, &selector)?; + let binding = c::PublisherBinding { + root: creation.root_identity(), + coordination: creation.coordination_identity()?, + data: [data0, data1], + selector: selector_id, + stream: Some(gate.identity()?), + incarnation: nonce, + }; + let mut bytes = [0; c::BINDING_BYTES]; + binding.encode(&mut bytes).map_err(|_| invalid())?; + root.sync_namespace()?; + destination.sync_namespace()?; + create_image(&root, BINDING, &bytes)?; + root.sync_namespace()?; + Ok(()) +} + +pub(super) fn legacy_bootstrap_idle_image() -> [u8; RECORD_BYTES] { + RemovalState::Idle.encode() +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct RemovalSpec { + kind: u8, + root: FileIdentity, + parent: FileIdentity, + nonce: [u8; 32], + parent_path: [u8; 32], + name: [u8; 255], + name_len: u8, + source_epoch: u64, + admission_epoch: u64, + scope_digest: [u8; 32], + present: u16, + entries: [FileIdentity; 16], + directory_nonces: [[u8; 32]; 3], + entry_digests: [[u8; 32]; 16], +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +// Fixed inline protocol/lease state: boxing would add a separately accounted allocation. +#[allow(clippy::large_enum_variant)] +enum RemovalState { + Idle, + Removing(RemovalSpec), +} +impl RemovalState { + fn encode(self) -> [u8; RECORD_BYTES] { + let mut b = [0; RECORD_BYTES]; + b[..8].copy_from_slice(b"CGWREM01"); + if let Self::Removing(s) = self { + b[8] = 1; + b[9] = s.kind; + b[10] = s.name_len; + id_into(&mut b[16..32], s.root); + id_into(&mut b[32..48], s.parent); + b[48..80].copy_from_slice(&s.nonce); + b[80..112].copy_from_slice(&s.parent_path); + b[112..120].copy_from_slice(&s.source_epoch.to_le_bytes()); + b[120..128].copy_from_slice(&s.admission_epoch.to_le_bytes()); + b[128..160].copy_from_slice(&s.scope_digest); + b[160..162].copy_from_slice(&s.present.to_le_bytes()); + b[168..423].copy_from_slice(&s.name); + for (i, id) in s.entries.into_iter().enumerate() { + id_into(&mut b[424 + i * 16..440 + i * 16], id); + } + for (i, n) in s.directory_nonces.into_iter().enumerate() { + b[680 + i * 32..712 + i * 32].copy_from_slice(&n); + } + for (i, digest) in s.entry_digests.into_iter().enumerate() { + b[776 + i * 32..808 + i * 32].copy_from_slice(&digest); + } + } + b + } + fn decode(b: &[u8]) -> Option { + if b.len() != RECORD_BYTES || &b[..8] != b"CGWREM01" { + return None; + } + if b[8] == 0 { + return b[9..].iter().all(|x| *x == 0).then_some(Self::Idle); + } + if b[8] != 1 + || !matches!(b[9], 1..=3) + || b[10] == 0 + || b[11..16] + .iter() + .chain(&b[162..168]) + .chain(&b[423..424]) + .chain(&b[1288..]) + .any(|x| *x != 0) + { + return None; + } + let mut name = [0; 255]; + name.copy_from_slice(&b[168..423]); + let n = b[10] as usize; + if name[n..].iter().any(|x| *x != 0) + || name[..n].iter().any(|x| matches!(*x, 0 | b'/' | b'\\')) + { + return None; + } + let entries = std::array::from_fn(|i| id_from(&b[424 + i * 16..440 + i * 16])); + let directory_nonces = + std::array::from_fn(|i| b[680 + i * 32..712 + i * 32].try_into().unwrap()); + Some(Self::Removing(RemovalSpec { + kind: b[9], + root: id_from(&b[16..32]), + parent: id_from(&b[32..48]), + nonce: b[48..80].try_into().unwrap(), + parent_path: b[80..112].try_into().unwrap(), + name, + name_len: b[10], + source_epoch: u64::from_le_bytes(b[112..120].try_into().unwrap()), + admission_epoch: u64::from_le_bytes(b[120..128].try_into().unwrap()), + scope_digest: b[128..160].try_into().unwrap(), + present: u16::from_le_bytes(b[160..162].try_into().unwrap()), + entries, + directory_nonces, + entry_digests: std::array::from_fn(|i| { + b[776 + i * 32..808 + i * 32].try_into().unwrap() + }), + })) + } +} + +struct WriteStep { + file: usize, + offset: u64, + start: usize, + len: usize, +} +struct Transport { + binding: c::PublisherBinding, + source: [locks::FixedFileStamp; 4], + commit: c::CommitFact, + state: RemovalState, + steps: [Option; 8], + count: usize, + recovery_end: usize, + remove_end: usize, + bytes: super::maintenance_budget::ReservedBytes, +} +impl Transport { + fn prepare( + root: &RootedDirectory, + scope: (FileIdentity, FileIdentity, [u8; 32], FileIdentity), + startup: FileIdentity, + memory: &super::maintenance_budget::CleanupMemory, + ) -> Result { + let bytes = memory + .reserve(8 * c::SELECTOR_BYTES) + .map_err(FixedControlError::Resource)? + .bytes(8 * c::SELECTOR_BYTES) + .map_err(FixedControlError::Resource)?; + let binding_file = root.open_readonly_file(BINDING)?; + let binding = c::PublisherBinding::decode(&image::<{ c::BINDING_BYTES }>(&binding_file)?) + .map_err(|_| fixed_invalid())?; + if (binding.root, binding.coordination, binding.incarnation) != (scope.0, scope.1, scope.2) + || binding.stream != Some(startup) + { + return Err(fixed_invalid()); + } + let selector_file = root.open_readonly_file(SELECTOR)?; + let d0 = root.open_readonly_file(DATA[0])?; + let d1 = root.open_readonly_file(DATA[1])?; + if locks::file_identity(&selector_file).map_err(fixed_native)? != binding.selector + || locks::file_identity(&d0).map_err(fixed_native)? != binding.data[0] + || locks::file_identity(&d1).map_err(fixed_native)? != binding.data[1] + { + return Err(fixed_invalid()); + } + let source = [ + locks::FixedFileStamp::capture(&binding_file, locks::FixedControlKind::File)?, + locks::FixedFileStamp::capture(&selector_file, locks::FixedControlKind::File)?, + locks::FixedFileStamp::capture(&d0, locks::FixedControlKind::File)?, + locks::FixedFileStamp::capture(&d1, locks::FixedControlKind::File)?, + ]; + let selector = image::<{ c::SELECTOR_BYTES }>(&selector_file)?; + let data = [ + image::<{ c::SLOT_BYTES }>(&d0)?, + image::<{ c::SLOT_BYTES }>(&d1)?, + ]; + for (stamp, file) in source.iter().zip([&binding_file, &selector_file, &d0, &d1]) { + stamp.recheck(file)?; + } + let commit = c::decode_commit(&selector[..c::COMMIT_BYTES], &scope.2).ok(); + let intent = c::decode_intent(&selector[c::COMMIT_BYTES..], &scope.2).ok(); + let slots = [ + c::decode_slot(&data[0], &scope.2).ok(), + c::decode_slot(&data[1], &scope.2).ok(), + ]; + let recovery = c::recovery_step(commit, intent, slots); + let (current, image) = match recovery { + c::RecoveryStep::Committed(current) => (current, data[current.active]), + c::RecoveryStep::RewriteIntendedSlot(intent) + | c::RecoveryStep::CommitIntendedSlot(intent) => { + let mut intended = [0; c::SLOT_BYTES]; + intended.copy_from_slice( + &selector[c::COMMIT_BYTES + 96..c::COMMIT_BYTES + 96 + c::SLOT_BYTES], + ); + ( + c::CommitFact { + generation: intent.after, + active: intent.target, + slot_digest: intent.slot_digest, + }, + intended, + ) + } + c::RecoveryStep::Unknown => return Err(fixed_invalid()), + }; + let len = u64::from_le_bytes(image[48..56].try_into().unwrap()) as usize; + let state = RemovalState::decode(&image[56..56 + len]).ok_or_else(fixed_invalid)?; + let mut result = Self { + binding, + source, + commit: current, + state, + steps: std::array::from_fn(|_| None), + count: 0, + recovery_end: 0, + remove_end: 0, + bytes, + }; + match recovery { + c::RecoveryStep::RewriteIntendedSlot(_) => { + result.push(1 + current.active, 0, &image)?; + result.push_commit(current)?; + } + c::RecoveryStep::CommitIntendedSlot(_) => result.push_commit(current)?, + _ => {} + } + result.recovery_end = result.count; + Ok(result) + } + fn push(&mut self, file: usize, offset: u64, image: &[u8]) -> Result<(), FixedControlError> { + if self.count == 8 || image.len() > c::SELECTOR_BYTES { + return Err(fixed_invalid()); + } + let start = self.count * c::SELECTOR_BYTES; + self.bytes.as_mut_slice()[start..start + image.len()].copy_from_slice(image); + self.steps[self.count] = Some(WriteStep { + file, + offset, + start, + len: image.len(), + }); + self.count += 1; + Ok(()) + } + fn push_commit(&mut self, commit: c::CommitFact) -> Result<(), FixedControlError> { + let mut image = [0; c::COMMIT_BYTES]; + c::encode_commit(&mut image, &self.binding.incarnation, commit) + .map_err(|_| fixed_invalid())?; + self.push(0, 0, &image) + } + fn plan(&mut self, state: RemovalState) -> Result<(), FixedControlError> { + let generation = self + .commit + .generation + .checked_add(1) + .ok_or_else(fixed_invalid)?; + let mut slot = [0; c::SLOT_BYTES]; + let fact = c::encode_slot( + &mut slot, + &self.binding.incarnation, + generation, + &state.encode(), + ) + .map_err(|_| fixed_invalid())?; + let mut intent = [0; c::INTENT_BYTES]; + let next = c::encode_intent(&mut intent, &self.binding.incarnation, self.commit, &slot) + .map_err(|_| fixed_invalid())?; + self.push(0, c::COMMIT_BYTES as u64, &intent)?; + self.push(1 + next.target, 0, &slot)?; + self.commit = c::CommitFact { + generation, + active: next.target, + slot_digest: fact.digest, + }; + self.push_commit(self.commit)?; + Ok(()) + } + fn run( + &mut self, + root: &RootedDirectory, + selector: &std::fs::File, + start: usize, + end: usize, + grant: &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), FixedControlError> { + for index in start..end { + grant.check_time()?; + let step = self.steps[index].as_ref().ok_or_else(fixed_invalid)?; + let (name, id) = match step.file { + 0 => (SELECTOR, self.binding.selector), + 1 => (DATA[0], self.binding.data[0]), + 2 => (DATA[1], self.binding.data[1]), + _ => return Err(fixed_invalid()), + }; + let opened; + let file = if step.file == 0 { + if locks::file_identity(&root.open_metadata_file(name)?).map_err(fixed_native)? + != id + { + return Err(fixed_invalid()); + } + selector + } else { + opened = root.open_existing(name)?; + &opened + }; + if locks::file_identity(file).map_err(fixed_native)? != id { + return Err(fixed_invalid()); + } + locks::write_fixed_range( + file, + step.offset, + &self.bytes.as_slice()[step.start..step.start + step.len], + grant, + )?; + let mut verify = [0; c::SELECTOR_BYTES]; + locks::read_fixed_range(file, step.offset, &mut verify[..step.len])?; + if verify[..step.len] != self.bytes.as_slice()[step.start..step.start + step.len] { + return Err(fixed_invalid()); + } + } + Ok(()) + } +} + +pub(crate) struct PreparedWorkspaceRemoval { + root: PathBuf, + parent: PathBuf, + parent_root: PathBuf, + gate: PathBuf, + gate_stamp: locks::FixedFileStamp, + spec: RemovalSpec, + transport: MemoryBox, + initial: bool, + usable: bool, + observed: [Option; 16], + present_now: u16, + root_was_present: bool, + epoch: MemoryBox, + scratch: MemoryBox, + visit: super::maintenance_budget::ReservedBytes, + _root_pin: Option, + _parent_pin: std::fs::File, + _memory: super::maintenance_budget::MemoryReservation, +} +fn file_digest( + file: &std::fs::File, +) -> Result<([u8; 32], locks::FixedFileStamp), FixedControlError> { + let stamp = locks::FixedFileStamp::capture(file, locks::FixedControlKind::File)?; + if stamp.len() > 8 * 1024 * 1024 { + return Err(fixed_invalid()); + } + let mut hash = Sha256::new(); + let mut bytes = [0u8; 8192]; + let mut offset = 0; + while offset < stamp.len() { + let count = (stamp.len() - offset).min(bytes.len() as u64) as usize; + locks::read_fixed_range(file, offset, &mut bytes[..count])?; + hash.update(&bytes[..count]); + offset += count as u64; + } + stamp.recheck(file)?; + Ok((hash.finalize().into(), stamp)) +} +fn empty(directory: &RootedDirectory, bytes: &mut [u8; 4096]) -> Result<(), FixedControlError> { + let mut found = false; + directory + .visit_entries(bytes, &mut |_| { + found = true; + Err(std::io::ErrorKind::Interrupted.into()) + }) + .or_else(|error| { + if found && error.kind() == std::io::ErrorKind::Interrupted { + Ok(()) + } else { + Err(error) + } + })?; + if found { + Err(FixedControlError::Invalid(FixedControlProblem::Busy)) + } else { + Ok(()) + } +} +fn known_names( + directory: &RootedDirectory, + bytes: &mut [u8; 4096], +) -> Result<(), FixedControlError> { + directory.visit_entries(bytes, &mut |name| { + if NAMES.contains(&name) { + Ok(()) + } else { + Err(std::io::ErrorKind::InvalidData.into()) + } + })?; + Ok(()) +} +#[inline(never)] +fn boxed_removal_scratch( + memory: &super::maintenance_budget::CleanupMemory, +) -> Result, FixedControlError> { + let scratch = locks::FixedControlScratch::new( + memory + .reserve(locks::FixedControlScratch::required_bytes()) + .map_err(FixedControlError::Resource)?, + )?; + MemoryBox::new(scratch, memory).map_err(FixedControlError::Resource) +} +#[inline(never)] +fn boxed_removal_epoch( + domain: &ReferenceDomain, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result, FixedControlError> +{ + let epoch = super::maintenance_admission::prepare_scoped_invalidation(domain, memory)?; + MemoryBox::new(epoch, memory).map_err(FixedControlError::Resource) +} +#[inline(never)] +fn boxed_removal_transport( + parent: &RootedDirectory, + reference: (FileIdentity, FileIdentity, [u8; 32], FileIdentity), + gate: FileIdentity, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result, FixedControlError> { + let transport = Transport::prepare(parent, reference, gate, memory)?; + MemoryBox::new(transport, memory).map_err(FixedControlError::Resource) +} + +/// The optional terminal argument is required to create a NEW removal intent. +/// Replay accepts only the already-durable exact parent intent. Neither path +/// adopts a directory from its name, numeric identity, or an Empty observation. +// Retain the existing value API for standalone callers; joined-worker paths use the charged factory. +#[allow(dead_code)] +pub(crate) fn prepare_workspace_root_removal( + root: &Path, + domain: &ReferenceDomain, + terminal: Option<&c::PreparedTerminalWorkspace>, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result { + prepare_workspace_root_removal_boxed(root, domain, terminal, memory).map(MemoryBox::into_inner) +} + +/// The same exact source/intent preparation, returned behind its charged owner +/// before the caller enters another bounded stage on the joined worker. +#[inline(never)] +pub(crate) fn prepare_workspace_root_removal_boxed( + root: &Path, + domain: &ReferenceDomain, + terminal: Option<&c::PreparedTerminalWorkspace>, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result, FixedControlError> { + let path_bytes = root + .as_os_str() + .as_encoded_bytes() + .len() + .checked_add(domain.artifact_root().as_os_str().as_encoded_bytes().len()) + .and_then(|x| x.checked_add(2048)) + .and_then(|x| x.checked_mul(20)) + .and_then(|x| x.checked_add(32768)) + .ok_or(FixedControlError::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?; + let reservation = memory + .reserve(path_bytes) + .map_err(FixedControlError::Resource)?; + let mut scratch = boxed_removal_scratch(memory)?; + let mut visit = memory + .reserve(4096) + .map_err(FixedControlError::Resource)? + .bytes(4096) + .map_err(FixedControlError::Resource)?; + let epoch = boxed_removal_epoch(domain, memory)?; + let reference = epoch.root_facts(); + let parent_root = domain.artifact_root().to_owned(); + let parent_controls = RootedDirectory::open_readonly_root(&parent_root, reference.0)?; + let gate = domain.workspace_startup_path(); + let gate_file = + locks::open_fixed_control(&gate, locks::FixedControlKind::File, false, &mut scratch)?; + let gate_stamp = locks::FixedFileStamp::capture(&gate_file, locks::FixedControlKind::File)?; + if locks::read_coordination_incarnation(&gate_file).map_err(fixed_native)? != Some(reference.2) + { + return Err(fixed_invalid()); + } + let mut transport = + boxed_removal_transport(&parent_controls, reference, gate_stamp.identity(), memory)?; + let parent = root.parent().ok_or_else(fixed_invalid)?.to_owned(); + let parent_id = locks::path_identity(&parent).map_err(fixed_native)?; + let parent_directory = RootedDirectory::open_readonly_root(&parent, parent_id)?; + let name = root + .file_name() + .and_then(|x| x.to_str()) + .ok_or_else(fixed_invalid)? + .as_bytes(); + if name.is_empty() || name.len() > 255 || !name.is_ascii() { + return Err(fixed_invalid()); + } + let parent_path: [u8; 32] = Sha256::digest(parent.as_os_str().as_encoded_bytes()).into(); + let initial = transport.state == RemovalState::Idle; + let mut spec = match transport.state { + RemovalState::Removing(spec) => spec, + RemovalState::Idle => { + let terminal = terminal.ok_or_else(fixed_invalid)?; + let scope = terminal.removal_scope(); + if terminal.root() != root + || scope.parent_reference != reference + || scope.startup_gate != gate_stamp.identity() + || scope.parent != parent_id + { + return Err(fixed_invalid()); + } + let mut encoded_name = [0; 255]; + encoded_name[..name.len()].copy_from_slice(name); + RemovalSpec { + kind: scope.kind, + root: scope.root, + parent: parent_id, + nonce: scope.nonce, + parent_path, + name: encoded_name, + name_len: name.len() as u8, + source_epoch: terminal.source_epoch(), + admission_epoch: terminal.admission_epoch(), + scope_digest: terminal.scope_digest(), + present: 0, + entries: [FileIdentity::from_fixed_words(0, 0); 16], + directory_nonces: scope.directories.map(|x| x.map_or([0; 32], |x| x.1)), + entry_digests: [[0; 32]; 16], + } + } + }; + if spec.parent != parent_id + || spec.parent_path != parent_path + || &spec.name[..spec.name_len as usize] != name + { + return Err(fixed_invalid()); + } + let root_directory = match parent_directory.open_directory_readonly(name) { + Ok(root) => Some(root), + Err(error) if !initial && error.kind() == std::io::ErrorKind::NotFound => None, + Err(error) => return Err(error.into()), + }; + let mut observed = [None; 16]; + let mut present_now = 0u16; + if let Some(directory) = &root_directory { + if directory.identity() != spec.root { + return Err(fixed_invalid()); + } + known_names( + directory, + visit + .as_mut_slice() + .try_into() + .map_err(|_| fixed_invalid())?, + )?; + for (index, name) in NAMES.into_iter().enumerate() { + if index >= 13 { + match directory.open_directory_readonly(name) { + Ok(child) => { + present_now |= 1 << index; + let id = child.identity(); + let nonce = locks::created_file_nonce(&child.pin_handle()?)?; + if initial { + let wanted = terminal.unwrap().removal_scope().directories[index - 13] + .ok_or_else(fixed_invalid)?; + if (id, nonce) != (wanted.0, Some(wanted.1)) { + return Err(fixed_invalid()); + } + spec.present |= 1 << index; + spec.entries[index] = id; + } else if spec.present & (1 << index) == 0 + || spec.entries[index] != id + || nonce != Some(spec.directory_nonces[index - 13]) + { + return Err(fixed_invalid()); + } + empty( + &child, + visit + .as_mut_slice() + .try_into() + .map_err(|_| fixed_invalid())?, + )?; + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + if initial + && terminal.unwrap().removal_scope().directories[index - 13].is_some() + { + return Err(fixed_invalid()); + } + } + Err(error) => return Err(error.into()), + } + } else { + match directory.open_readonly_file(name) { + Ok(file) => { + present_now |= 1 << index; + let (digest, stamp) = file_digest(&file)?; + if initial { + spec.present |= 1 << index; + spec.entries[index] = stamp.identity(); + spec.entry_digests[index] = digest; + } else if spec.present & (1 << index) == 0 + || spec.entries[index] != stamp.identity() + || spec.entry_digests[index] != digest + { + return Err(fixed_invalid()); + } + observed[index] = Some(stamp); + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} + Err(error) => return Err(error.into()), + } + } + } + if initial + && (spec.present & 0x7f != 0x7f + || spec.present & (1 << 8) == 0 + || spec.present & (1 << 13) == 0) + { + return Err(fixed_invalid()); + } + } + if initial { + transport.plan(RemovalState::Removing(spec))?; + transport.remove_end = transport.count; + } else { + transport.remove_end = transport.recovery_end; + transport.plan(RemovalState::Idle)?; + } + MemoryBox::new( + PreparedWorkspaceRemoval { + root: root.to_owned(), + parent, + parent_root, + gate, + gate_stamp, + spec, + transport, + initial, + usable: true, + observed, + present_now, + root_was_present: root_directory.is_some(), + epoch, + scratch, + visit, + _root_pin: root_directory + .as_ref() + .map(|d| d.pin_handle()) + .transpose()?, + _parent_pin: parent_directory.pin_handle()?, + _memory: reservation, + }, + memory, + ) + .map_err(FixedControlError::Resource) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum WorkspaceRemovalProgress { + Pending, + Complete, +} +/// This variant is constructed ONLY after this invocation's actual empty rmdir. +pub(crate) struct RemovedWorkspace { + root: FileIdentity, + nonce: [u8; 32], + source: u64, +} +/// Recovery fact is deliberately distinct: durable exact Removing intent plus +/// the same parent namespace and an absent exact name, under actual gate Excl. +pub(crate) struct RecoveredMissingWorkspace { + root: FileIdentity, + nonce: [u8; 32], + source: u64, +} +pub(crate) enum WorkspaceRootOutcome<'a> { + Removed(&'a RemovedWorkspace), + RecoveredMissing(&'a RecoveredMissingWorkspace), +} +impl WorkspaceRootOutcome<'_> { + pub(crate) fn root_identity(&self) -> FileIdentity { + match self { + Self::Removed(x) => x.root, + Self::RecoveredMissing(x) => x.root, + } + } + pub(crate) fn incarnation(&self) -> [u8; 32] { + match self { + Self::Removed(x) => x.nonce, + Self::RecoveredMissing(x) => x.nonce, + } + } + pub(crate) fn source_epoch(&self) -> u64 { + match self { + Self::Removed(x) => x.source, + Self::RecoveredMissing(x) => x.source, + } + } +} +impl PreparedWorkspaceRemoval { + fn open_parent_controls( + path: &Path, + transport: &Transport, + scratch: &mut locks::FixedControlScratch, + grant: &mut super::maintenance_budget::MutationGrant, + ) -> Result<(RootedDirectory, locks::FixedLockedFile), FixedControlError> { + grant.take_entry()?; + let root = RootedDirectory::open_fixed(path, transport.binding.root, true, scratch)?; + grant.take_entry()?; + let file = root.open_existing(SELECTOR)?; + transport.source[1].recheck(&file)?; + let selector = locks::FixedLockedFile::try_lock(file, LockMode::Exclusive)? + .ok_or(FixedControlError::Invalid(FixedControlProblem::Busy))?; + // The held selector cannot change beneath the already prepared images. + for (index, name) in [(0, BINDING), (2, DATA[0]), (3, DATA[1])] { + grant.take_entry()?; + transport.source[index].recheck(&root.open_readonly_file(name)?)?; + } + Ok((root, selector)) + } +} +/// Initial intent is emitted only inside the real terminal callback, retaining +/// actual global startup/owner/writer/selector guards before its first byte. +pub(crate) fn begin_workspace_root_removal( + terminal: &mut c::PreparedTerminalWorkspace, + prepared: &mut PreparedWorkspaceRemoval, + grant: &mut super::maintenance_budget::MutationGrant, +) -> Result { + if !prepared.initial + || !prepared.usable + || terminal.root() != prepared.root + || terminal.scope_digest() != prepared.spec.scope_digest + { + return Err(fixed_invalid()); + } + // Fixed selector/check admission occurs BEFORE terminal's own E advance. + let (parent, _selector) = PreparedWorkspaceRemoval::open_parent_controls( + &prepared.parent_root, + &prepared.transport, + &mut prepared.scratch, + grant, + )?; + let effects = prepared + .transport + .remove_end + .checked_mul(2) + .ok_or_else(fixed_invalid)?; + let spec = prepared.spec; + c::with_terminal_workspace(terminal, grant, effects, &mut |lease, grant| { + let scope = lease.removal_scope(); + if ( + scope.root, + scope.nonce, + lease.source_epoch(), + lease.admission_epoch(), + ) != ( + spec.root, + spec.nonce, + spec.source_epoch, + spec.admission_epoch, + ) { + return Err(fixed_invalid()); + } + prepared.usable = false; + let remove_end = prepared.transport.remove_end; + prepared + .transport + .run(&parent, _selector.file(), 0, remove_end, grant) + })?; + Ok(WorkspaceRemovalProgress::Pending) +} + +/// Advance a previously durable exact Removing intent. Each call reserves at +/// most32 effects, touches only the fixed16 metadata names, and retains parent +/// controls/actual startup gate throughout. Payload/receipt removal is NOT an +/// operation this API can perform. Bulk Work remains borrowed and offslot. +pub(crate) fn advance_workspace_root_removal( + prepared: &mut PreparedWorkspaceRemoval, + grant: &mut super::maintenance_budget::MutationGrant, + done_entries: usize, + done: &mut impl FnMut( + &WorkspaceRootOutcome<'_>, + &mut super::maintenance_budget::MutationGrant, + ) -> Result<(), FixedControlError>, +) -> Result { + if prepared.initial || !prepared.usable || done_entries > 16 { + return Err(fixed_invalid()); + } + // A full root's fixed admission checks leave less than32 effect entries. + // Remove a smaller batch first; once absent, the shorter admission can + // reserve the larger completion callback and durable reset allowance. + let effect_entries = if prepared.root_was_present { 16 } else { 32 }; + let (parent_controls, _selector) = PreparedWorkspaceRemoval::open_parent_controls( + &prepared.parent_root, + &prepared.transport, + &mut prepared.scratch, + grant, + )?; + grant.take_entry()?; + let gate_file = locks::open_fixed_control( + &prepared.gate, + locks::FixedControlKind::File, + false, + &mut prepared.scratch, + )?; + prepared.gate_stamp.recheck(&gate_file)?; + let _gate = locks::FixedLockedFile::try_lock(gate_file, LockMode::Exclusive)? + .ok_or(FixedControlError::Invalid(FixedControlProblem::Busy))?; + if !_gate.matches_fixed_header(&locks::encode_coordination_incarnation( + prepared.transport.binding.incarnation, + ))? { + return Err(fixed_invalid()); + } + grant.take_entry()?; + let parent = RootedDirectory::open_fixed( + &prepared.parent, + prepared.spec.parent, + true, + &mut prepared.scratch, + )?; + grant.take_entry()?; + let name = &prepared.spec.name[..prepared.spec.name_len as usize]; + let root = match parent.open_directory(name) { + Ok(root) if prepared.root_was_present && root.identity() == prepared.spec.root => { + Some(root) + } + Err(error) + if !prepared.root_was_present && error.kind() == std::io::ErrorKind::NotFound => + { + None + } + Err(error) => return Err(error.into()), + _ => return Err(fixed_invalid()), + }; + let mut guards: [Option; 3] = std::array::from_fn(|_| None); + if let Some(root) = &root { + for (slot, index) in [1usize, 2, 4].into_iter().enumerate() { + if prepared.present_now & (1 << index) != 0 { + grant.take_entry()?; + let file = root.open_existing(NAMES[index])?; + prepared.observed[index] + .ok_or_else(fixed_invalid)? + .recheck(&file)?; + let guard = locks::FixedLockedFile::try_lock(file, LockMode::Exclusive)? + .ok_or(FixedControlError::Invalid(FixedControlProblem::Busy))?; + if matches!(index, 1 | 2) + && !guard.matches_fixed_header(&locks::encode_coordination_incarnation( + prepared.spec.nonce, + ))? + { + return Err(fixed_invalid()); + } + guards[slot] = Some(guard); + } + } + } + let PreparedWorkspaceRemoval { + spec, + present_now, + observed, + epoch, + transport, + visit, + usable, + .. + } = prepared; + let spec = *spec; + let present_now = *present_now; + let mut progress = WorkspaceRemovalProgress::Pending; + super::maintenance_admission::with_scoped_reference_invalidation( + epoch, + grant, + effect_entries, + &mut |_, grant| { + if let Some(root) = &root { + grant.take_entry()?; + let bytes: &mut [u8; 4096] = visit + .as_mut_slice() + .try_into() + .map_err(|_| fixed_invalid())?; + root.visit_entries(bytes, &mut |name| { + grant + .take_entry() + .map_err(|_| std::io::Error::from(std::io::ErrorKind::WouldBlock))?; + if NAMES.contains(&name) { + Ok(()) + } else { + Err(std::io::ErrorKind::InvalidData.into()) + } + })?; + for (index, name) in NAMES.into_iter().enumerate() { + grant.take_entry()?; + if index < 13 { + match (root.open_readonly_file(name), observed[index]) { + (Ok(file), Some(stamp)) => stamp.recheck(&file)?, + (Err(error), None) if error.kind() == std::io::ErrorKind::NotFound => {} + (Err(error), _) => return Err(error.into()), + _ => return Err(fixed_invalid()), + } + } else { + match root.open_directory_readonly(name) { + Ok(child) if present_now & (1 << index) != 0 => { + if child.identity() != spec.entries[index] + || locks::created_file_nonce(&child.pin_handle()?)? + != Some(spec.directory_nonces[index - 13]) + { + return Err(fixed_invalid()); + } + grant.take_entry()?; + empty(&child, bytes)?; + } + Err(error) + if present_now & (1 << index) == 0 + && error.kind() == std::io::ErrorKind::NotFound => {} + Err(error) => return Err(error.into()), + _ => return Err(fixed_invalid()), + } + } + } + } else { + grant.take_entry()?; + match parent.open_directory_readonly(&spec.name[..spec.name_len as usize]) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} + Err(error) => return Err(error.into()), + Ok(_) => return Err(fixed_invalid()), + } + } + Ok(()) + }, + &mut |_, grant| { + *usable = false; + let mut left = effect_entries; + let recovery = transport.recovery_end * 2; + if recovery > left { + return Err(fixed_invalid()); + } + let recovery_end = transport.recovery_end; + transport.run(&parent_controls, _selector.file(), 0, recovery_end, grant)?; + left -= recovery; + let mut removed = None; + if let Some(root) = &root { + // Empty declared directories first; selectors and stable locks last. + for index in [13usize, 14, 15, 8, 9, 10, 11, 12, 7, 5, 6, 3, 0, 4, 2, 1] { + if present_now & (1 << index) == 0 { + continue; + } + if left < 3 { + return Ok(()); + } + grant.check_time()?; + if let Some(slot) = match index { + 1 => Some(0), + 2 => Some(1), + 4 => Some(2), + _ => None, + } { + drop(guards[slot].take()); + } + if index >= 13 { + root.remove_empty_directory(NAMES[index], spec.entries[index])?; + } else { + root.unlink_file(NAMES[index], spec.entries[index])?; + } + grant.check_time()?; + root.sync_namespace()?; + left -= 3; + } + if left < 3 { + return Ok(()); + } + grant.check_time()?; + parent.remove_empty_directory(&spec.name[..spec.name_len as usize], spec.root)?; + parent.sync_namespace()?; + left -= 3; + removed = Some(RemovedWorkspace { + root: spec.root, + nonce: spec.nonce, + source: spec.source_epoch, + }); + } + // If the budget cannot cover durable S3Done + parent reset, leave the + // exact Removing intent. Fresh replay yields RecoveredMissing, never a + // fabricated actual-rmdir event. + let reset = (transport.count - transport.recovery_end) * 2; + if left < done_entries + reset { + return Ok(()); + } + grant.check_time()?; + if let Some(removed) = &removed { + done(&WorkspaceRootOutcome::Removed(removed), grant)?; + } else { + let missing = RecoveredMissingWorkspace { + root: spec.root, + nonce: spec.nonce, + source: spec.source_epoch, + }; + done(&WorkspaceRootOutcome::RecoveredMissing(&missing), grant)?; + } + let reset_start = transport.recovery_end; + let reset_end = transport.count; + transport.run( + &parent_controls, + _selector.file(), + reset_start, + reset_end, + grant, + )?; + progress = WorkspaceRemovalProgress::Complete; + Ok(()) + }, + )?; + Ok(progress) +} + +impl PreparedWorkspaceRemoval { + pub(crate) fn is_initial(&self) -> bool { + self.initial + } + pub(crate) fn root(&self) -> &Path { + &self.root + } +} +/// Main runtime supplies its actual configured StateRoot for Direct runs; +/// Managed and raw-pair worker/phase registries derive from the exact domain. +/// The durable parent-path hash+identity still have to match the resulting path. +pub(crate) fn prepare_pending_workspace_root_removal_for_target( + target: &super::inventory::MaintenanceTarget, + domain: &ReferenceDomain, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result>, FixedControlError> +{ + prepare_pending_workspace_root_removal_in(domain, Some(&target.state_root), memory) +} +#[cfg(test)] +pub(crate) fn prepare_pending_workspace_root_removal( + domain: &ReferenceDomain, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result>, FixedControlError> +{ + prepare_pending_workspace_root_removal_in(domain, None, memory) +} +#[inline(never)] +fn prepare_pending_workspace_root_removal_in( + domain: &ReferenceDomain, + state_root: Option<&Path>, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result>, FixedControlError> +{ + let Some(path) = pending_workspace_path(domain, state_root, memory)? else { + return Ok(None); + }; + prepare_workspace_root_removal_boxed(&path, domain, None, memory).map(Some) +} +#[inline(never)] +fn pending_workspace_path( + domain: &ReferenceDomain, + state_root: Option<&Path>, + memory: &super::maintenance_budget::CleanupMemory, +) -> Result, FixedControlError> { + let size = domain + .artifact_root() + .as_os_str() + .as_encoded_bytes() + .len() + .checked_add(state_root.map_or(0, |x| x.as_os_str().as_encoded_bytes().len())) + .and_then(|x| x.checked_mul(8)) + .and_then(|x| x.checked_add(16384)) + .ok_or(FixedControlError::Resource( + super::maintenance_budget::ResourceLimit::ArithmeticOverflow, + ))?; + let reservation = memory.reserve(size).map_err(FixedControlError::Resource)?; + let epoch = super::maintenance_admission::prepare_scoped_invalidation(domain, memory)?; + let reference = epoch.root_facts(); + let root = RootedDirectory::open_readonly_root(domain.artifact_root(), reference.0)?; + let binding_file = match root.open_readonly_file(BINDING) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(error.into()), + }; + let binding = c::PublisherBinding::decode(&image::<{ c::BINDING_BYTES }>(&binding_file)?) + .map_err(|_| fixed_invalid())?; + let transport = Transport::prepare( + &root, + reference, + binding.stream.ok_or_else(fixed_invalid)?, + memory, + )?; + let RemovalState::Removing(spec) = transport.state else { + return Ok(None); + }; + let parent = domain + .workspace_registry(spec.kind, state_root) + .ok_or_else(fixed_invalid)?; + if <[u8; 32]>::from(Sha256::digest(parent.as_os_str().as_encoded_bytes())) != spec.parent_path { + return Err(fixed_invalid()); + } + let name = + std::str::from_utf8(&spec.name[..spec.name_len as usize]).map_err(|_| fixed_invalid())?; + let path = parent.join(name); + drop(transport); + drop(epoch); + drop(root); + drop(reservation); + Ok(Some(path)) +} + +#[cfg(test)] +mod preparation_layout_tests { + use super::*; + #[cfg(unix)] + #[test] + fn selector_replacement_does_not_write_through_the_detached_owner() { + use super::super::maintenance_budget::{CleanupMemory, MutationGrant}; + let path = std::env::temp_dir().join(format!( + "removal-selector-{}", + super::super::layout::managed_generation_id() + )); + std::fs::create_dir(&path).unwrap(); + let root = RootedDirectory::open(&path, locks::path_identity(&path).unwrap()).unwrap(); + let mut selector = root.create_new(SELECTOR).unwrap(); + selector.write_all(b"old").unwrap(); + let stamp = + locks::FixedFileStamp::capture(&selector, locks::FixedControlKind::File).unwrap(); + let owner = locks::FixedLockedFile::try_lock(selector, LockMode::Exclusive) + .unwrap() + .unwrap(); + root.rename_file_no_replace(SELECTOR, &root, b"detached", stamp.identity()) + .unwrap(); + root.create_new(SELECTOR) + .unwrap() + .write_all(b"new") + .unwrap(); + let memory = CleanupMemory::new(32768).unwrap(); + let mut bytes = memory.reserve(1).unwrap().bytes(1).unwrap(); + bytes.as_mut_slice()[0] = 42; + // Isolate current-name validation under an actual owned lock; this + // transport test neither fabricates publication proof nor deletes data. + let mut transport = Transport { + binding: c::PublisherBinding { + root: root.identity(), + coordination: stamp.identity(), + data: [stamp.identity(); 2], + selector: stamp.identity(), + stream: None, + incarnation: [1; 32], + }, + source: [stamp; 4], + commit: c::CommitFact { + generation: 1, + active: 0, + slot_digest: [0; 32], + }, + state: RemovalState::Idle, + steps: std::array::from_fn(|i| { + (i == 0).then_some(WriteStep { + file: 0, + offset: 0, + start: 0, + len: 1, + }) + }), + count: 1, + recovery_end: 0, + remove_end: 0, + bytes, + }; + let mut grant = MutationGrant::for_test(100, std::time::Duration::from_secs(2)); + assert!(transport + .run(&root, owner.file(), 0, 1, &mut grant) + .is_err()); + assert_eq!(std::fs::read(path.join("detached")).unwrap(), b"old"); + assert_eq!( + std::fs::read(path.join(std::str::from_utf8(SELECTOR).unwrap())).unwrap(), + b"new" + ); + drop(owner); + drop(root); + std::fs::remove_dir_all(path).unwrap(); + } + #[test] + fn workspace_removal_keeps_large_images_out_of_fixed_owner_layout() { + // This absolute envelope bound rejects the original large inline owner. + // Relative sizes of the independent protocol images are not a contract. + assert!(std::mem::size_of::() <= 4096); + let _: fn(PreparedWorkspaceRemoval) = |prepared| { + let _: MemoryBox = + prepared.epoch; + let _: MemoryBox = prepared.scratch; + let _: MemoryBox = prepared.transport; + }; + } +} diff --git a/src/storage/managed.rs b/src/storage/managed.rs index f55a2587..171ce4ed 100644 --- a/src/storage/managed.rs +++ b/src/storage/managed.rs @@ -6,8 +6,8 @@ use crate::storage::layout::{ GenerationPaths, ManagedLayout, }; use crate::storage::locks::{ - open_locked, try_open_locked, try_open_locked_in_existing_parent, LockMode, LockedFile, - StateLease, WriterLease, + open_locked, try_open_existing_locked, try_open_locked, LockMode, LockedFile, StateLease, + WriterLease, }; use crate::storage::run_workspace::{ remove_run_root_confined, RunJournal, RunPhase, RunWorkspace, RunWorkspaceRecovery, @@ -77,27 +77,24 @@ struct SearchBackendEnvelope { #[derive(Debug)] pub(crate) struct GenerationLease { lock: Option, - store: ManagedStore, - generation_id: String, + _database_use: [super::maintenance_ownership::DatabaseUseLease; 2], } - impl GenerationLease { - fn new(lock: LockedFile, store: ManagedStore, generation_id: String) -> Self { + fn new( + lock: Option, + database_use: [super::maintenance_ownership::DatabaseUseLease; 2], + ) -> Self { Self { - lock: Some(lock), - store, - generation_id, + lock, + _database_use: database_use, } } } - impl Drop for GenerationLease { fn drop(&mut self) { + // Reader release is only lease release. The joined controller observes + // the completion opportunity; no unbounded janitor runs in a destructor. drop(self.lock.take()); - if self.store.layout.storage_root().exists() { - self.store - .best_effort_cleanup_generation(&self.generation_id); - } } } @@ -111,12 +108,163 @@ pub(crate) enum StorageMode { #[derive(Debug, Clone, Default)] pub(crate) struct ManagedCleanupReport { pub run_recovery: RunWorkspaceRecovery, + #[cfg(test)] pub retired_deleted: usize, pub retired_pending: usize, + #[cfg(test)] pub retired_generations_deleted: usize, pub retired_generations_pending: usize, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ManagedJanitorStage { + PreviousRetirementMarker, + PublishedRunJournal, + TerminalRunRemoval, + #[cfg(test)] + GenerationEnumeration, + RetiredGenerationRemoval, + GenerationDisposition, +} +impl ManagedJanitorStage { + pub(crate) fn reason(self) -> &'static str { + match self { + Self::PreviousRetirementMarker => "managed_previous_retirement_pending", + Self::PublishedRunJournal => "managed_run_journal_pending", + Self::TerminalRunRemoval => "managed_run_cleanup_pending", + #[cfg(test)] + Self::GenerationEnumeration => "managed_generation_observation_pending", + Self::RetiredGenerationRemoval => "managed_generation_cleanup_pending", + Self::GenerationDisposition => "managed_generation_disposition_pending", + } + } +} +#[derive(Debug)] +pub(crate) struct ManagedJanitorIssue { + pub stage: ManagedJanitorStage, + pub source: NativeError, +} +#[derive(Debug, Default)] +pub(crate) struct ManagedJanitorReport { + pub legacy: ManagedCleanupReport, + pub pending: bool, + pub observation_complete: bool, + pub issue_count: usize, + pub first_issue: Option, + pub deferred_reason: Option<&'static str>, +} +impl ManagedJanitorReport { + fn complete() -> Self { + Self { + observation_complete: true, + ..Self::default() + } + } + fn issue(&mut self, stage: ManagedJanitorStage, source: NativeError, pending: bool) { + self.issue_count = self.issue_count.saturating_add(1); + if self.first_issue.is_none() { + self.first_issue = Some(ManagedJanitorIssue { stage, source }); + } + self.pending |= pending; + } + pub(crate) fn record_generation_disposition_failure(&mut self, error: NativeError) { + #[cfg(test)] + if std::env::var_os("CGRAPH_CLEANUP_TRACE").is_some() { + eprintln!("generation disposition failed: {error:?}"); + } + self.issue(ManagedJanitorStage::GenerationDisposition, error, true); + } + + pub(crate) fn merge_latest(&mut self, mut latest: Self) { + self.issue_count = self.issue_count.saturating_add(latest.issue_count); + if self.first_issue.is_none() { + self.first_issue = latest.first_issue.take(); + } + if latest.observation_complete { + self.pending = latest.pending; + self.legacy = latest.legacy; + } else { + self.pending |= latest.pending; + self.legacy.run_recovery.skipped_locked = self + .legacy + .run_recovery + .skipped_locked + .max(latest.legacy.run_recovery.skipped_locked); + self.legacy.retired_pending = self + .legacy + .retired_pending + .max(latest.legacy.retired_pending); + self.legacy.retired_generations_pending = self.legacy.retired_pending; + } + self.observation_complete &= latest.observation_complete; + self.deferred_reason = latest.deferred_reason.or(self.deferred_reason); + } + #[cfg(test)] + fn into_legacy(mut self) -> Result { + match self.first_issue.take() { + Some(issue) => Err(issue.source), + None => Ok(self.legacy), + } + } +} +#[derive(Debug)] +pub(crate) struct ManagedPublication { + pub generation_id: String, + pub janitor: ManagedJanitorReport, +} +#[derive(Debug)] +pub(crate) struct ManagedReconciliation { + pub recovery: ManagedRecoveryReport, + pub janitor: ManagedJanitorReport, +} +#[derive(Debug, Default)] +pub(crate) struct ManagedRecoveryReport { + pub publishing_recovered: usize, + pub skipped_locked: usize, + pub observation_complete: bool, +} +impl ManagedReconciliation { + fn new(recovery: ManagedRecoveryReport, mut janitor: ManagedJanitorReport) -> Self { + janitor.legacy.run_recovery.publishing_recovered = recovery.publishing_recovered; + janitor.legacy.run_recovery.skipped_locked = janitor + .legacy + .run_recovery + .skipped_locked + .max(recovery.skipped_locked); + Self { recovery, janitor } + } +} +#[derive(Debug)] +struct PublicationFailure { + source: NativeError, + may_have_effects: bool, +} +impl PublicationFailure { + fn before(source: NativeError) -> Self { + Self { + source, + may_have_effects: false, + } + } + fn ambiguous(source: NativeError) -> Self { + Self { + source, + may_have_effects: true, + } + } +} +#[derive(Debug)] +enum ReadyProbe { + Absent, + Valid, + Invalid(NativeError), +} +#[derive(Debug)] +enum RunResolution { + Committed { previous: Option }, + Obsolete { published: GenerationPaths }, +} + #[derive(Debug)] pub(crate) struct ManagedReadSnapshot { pub generation_id: String, @@ -149,9 +297,17 @@ pub(crate) struct ManagedWriteSession { pub base_generation: Option, ready: bool, finished: bool, + #[cfg(test)] + publication_report: Option, } impl ManagedWriteSession { + pub(crate) fn writer_lease(&self) -> Result<&WriterLease, NativeError> { + self.writer_lease + .as_ref() + .ok_or_else(|| NativeError::InvalidInput("write session is finished".into())) + } + pub(crate) fn phase_workspace_root(&self) -> PathBuf { self.store.layout.phase_workspace_root() } @@ -204,10 +360,31 @@ impl ManagedWriteSession { result } + #[cfg(test)] pub(crate) fn publish_with_stats( &mut self, graph_summary: &S, ) -> Result { + let mut publication = self.publish_with_stats_typed(graph_summary)?; + // Explicit historical adapter only: production publication waits for + // native verification before genuine generation disposition is sealed. + if let Err(error) = self.store.finalize_previous_retirement( + self.base_generation + .as_ref() + .map(|active| active.generation_id.as_str()), + ) { + publication + .janitor + .issue(ManagedJanitorStage::PreviousRetirementMarker, error, true); + } + self.publication_report = Some(publication.janitor); + Ok(publication.generation_id) + } + + pub(crate) fn publish_with_stats_typed( + &mut self, + graph_summary: &S, + ) -> Result { if !self.ready { if let Err(error) = self.mark_ready_with_stats(graph_summary) { return Err(self.abort_after_error(error)); @@ -220,42 +397,88 @@ impl ManagedWriteSession { drop(self.writer_lease.take()); return Err(error); } - match self + let mut janitor = match self .store - .publish_candidate_inner(&workspace, &self.candidate) + .publish_candidate_inner_typed(&workspace, &self.candidate) { - Ok(generation_id) => { - finish_committed_workspace(workspace, &generation_id); - Ok(generation_id) - } - Err(error) => { - let generation_id = self.candidate.generation_id.clone(); - if self - .store - .publication_is_committed(&generation_id) - .unwrap_or(false) - { - finish_committed_workspace(workspace, &generation_id); - return Ok(generation_id); - } + Ok(report) => report, + Err(failure) => { self.finished = true; - let error = abort_workspace_after_error(workspace, error); + let error = if failure.may_have_effects { + workspace.retain_for_recovery(); + failure.source + } else { + abort_workspace_after_error(workspace, failure.source) + }; drop(self.writer_lease.take()); - Err(error) + return Err(error); } + }; + let generation_id = self.candidate.generation_id.clone(); + if self.base_generation.is_some() { + janitor.pending = true; + janitor.legacy.retired_pending = 1; + janitor.legacy.retired_generations_pending = 1; + janitor.deferred_reason = Some("managed_generation_collection_deferred"); + } + #[cfg(test)] + MANAGED_POST_PUBLICATION.with(|hook| { + if let Some(hook) = hook.take() { + hook(workspace.root()); + } + }); + // These are the same resolved publication's independent optional + // finalization steps. A failed journal update does not invent an extant + // run if actual disposal subsequently succeeds. + if let Err(error) = + workspace.mark_phase(RunPhase::Published, Some(generation_id.clone()), None) + { + janitor.issue(ManagedJanitorStage::PublishedRunJournal, error, false); } + if let Err(error) = workspace.finish() { + janitor.issue(ManagedJanitorStage::TerminalRunRemoval, error, true); + janitor.legacy.run_recovery.skipped_locked = 1; + } + Ok(ManagedPublication { + generation_id, + janitor, + }) } + pub(crate) fn phase_owner_scope(&self) -> Option { + self.workspace.as_ref()?.phase_owner_scope() + } + + pub(crate) fn reconcile_typed(&self) -> Result { + let writer = self.writer_lease()?; + self.store.recover_required_with_owned_writer(writer) + } + #[cfg(test)] pub(crate) fn recover_and_gc(&self) -> Result { - if self.writer_lease.is_none() { - return Err(NativeError::InvalidInput( - "managed cleanup requires this session's writer lease".into(), - )); + self.store + .cleanup_with_owned_writer_typed(self.writer_lease()?)? + .janitor + .into_legacy() + } + pub(crate) fn check_active_generation(&self, expected: &str) -> Result<(), NativeError> { + let active = self.store.read_active_with_shared_lock()?; + if active.as_ref().map(|a| a.generation_id.as_str()) != Some(expected) { + return Err(NativeError::InvalidInput(format!( + "managed publish activated {}, but reopened generation was {:?}", + expected, + active.map(|a| a.generation_id) + ))); } - self.store.cleanup_with_owned_writer() + validate_ready_generation(&self.store.layout.generation(expected)?) + } + pub(crate) fn finish_without_cleanup(mut self) { + self.finished = true; + drop(self.writer_lease.take()); } + #[cfg(test)] pub(crate) fn finish(mut self) { + let _legacy_report = self.publication_report.take(); self.finished = true; drop(self.writer_lease.take()); // A final old-generation reader may have released its lease while this @@ -293,6 +516,74 @@ impl Drop for ManagedWriteSession { } } +#[cfg(test)] +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) enum ManagedFault { + None, + PointerAfterRename, + MandatorySync, + GenerationScan, +} +#[cfg(test)] +thread_local! { static MANAGED_FAULT: std::cell::Cell = const { std::cell::Cell::new(ManagedFault::None) }; } +#[cfg(test)] +thread_local! { + static MANAGED_POST_PUBLICATION: std::cell::Cell> = const { std::cell::Cell::new(None) }; +} +#[cfg(test)] +pub(crate) fn set_managed_cleanup_hook(hook: fn(&Path)) { + MANAGED_POST_PUBLICATION.with(|value| value.set(Some(hook))); +} +#[cfg(test)] +static SCOPED_MANAGED_SYNC_FAULT: std::sync::Mutex> = + std::sync::Mutex::new(None); +#[cfg(test)] +pub(crate) struct ScopedManagedSyncFault(Option); +#[cfg(test)] +impl Drop for ScopedManagedSyncFault { + fn drop(&mut self) { + *SCOPED_MANAGED_SYNC_FAULT + .lock() + .unwrap_or_else(|error| error.into_inner()) = self.0; + } +} +#[cfg(test)] +pub(crate) fn inject_scoped_managed_sync_fault( + root: &Path, +) -> Result { + let identity = super::locks::path_identity(root)?; + let mut current = SCOPED_MANAGED_SYNC_FAULT + .lock() + .unwrap_or_else(|error| error.into_inner()); + let previous = current.replace(identity); + Ok(ScopedManagedSyncFault(previous)) +} + +#[cfg(test)] +pub(crate) struct ManagedFaultReset(ManagedFault); +#[cfg(test)] +impl Drop for ManagedFaultReset { + fn drop(&mut self) { + MANAGED_FAULT.with(|f| f.set(self.0)); + } +} +#[cfg(test)] +pub(crate) fn inject_managed_fault(fault: ManagedFault) -> ManagedFaultReset { + ManagedFaultReset(MANAGED_FAULT.with(|f| f.replace(fault))) +} +#[cfg(test)] +fn managed_fault(fault: ManagedFault) -> Result<(), NativeError> { + if MANAGED_FAULT.with(|f| f.get()) == fault { + Err(std::io::Error::other(match fault { + ManagedFault::MandatorySync => "injected mandatory managed sync failure", + _ => "injected generation-only scan failure", + }) + .into()) + } else { + Ok(()) + } +} + #[derive(Debug, Clone)] pub(crate) struct ManagedStore { layout: ManagedLayout, @@ -355,14 +646,35 @@ impl ManagedStore { } pub(crate) fn begin_write(&self) -> Result { + let fresh = super::layout::create_fresh_reference_root(self.layout.storage_root())?; self.ensure_layout()?; - let writer_lease = self.open_writer_lock()?; + let mut writer_lease = self.open_writer_lock()?; + let reference_domain = + super::maintenance_ownership::ReferenceDomain::managed(&self.layout)?; + if let Some(fresh) = &fresh { + super::maintenance_ownership::initialize_fresh_reference_domain( + &reference_domain, + fresh, + &writer_lease, + )?; + } + // Legacy roots may acquire their first ordinary use lock here. Observe + // the real scope again after that producer-only coordination step, so + // upgraded run associations never retain a stale missing-lock stamp. + let mut artifact_use = super::maintenance_ownership::ArtifactUseLease::acquire_shared( + &self.layout.artifacts_root(), + )?; + let reference_domain = + super::maintenance_ownership::ReferenceDomain::managed(&self.layout)?; + artifact_use.bind_reference_domain(&reference_domain)?; + writer_lease.retain(artifact_use.into_lock()); ensure_registry_empty(&self.layout.phase_workspace_root()) .map_err(NativeError::InvalidInput)?; collect_registry(&self.layout.phase_workspace_root()).map_err(NativeError::InvalidInput)?; let base_generation = self.read_active_with_shared_lock()?; - let mut workspace = RunWorkspace::create( + let mut workspace = RunWorkspace::create_scoped( self.layout.runs_root(), + &reference_domain, base_generation .as_ref() .map(|value| value.generation_id.clone()), @@ -387,6 +699,8 @@ impl ManagedStore { base_generation, ready: false, finished: false, + #[cfg(test)] + publication_report: None, }) } @@ -399,19 +713,55 @@ impl ManagedStore { } pub(crate) fn resolve_active_read(&self) -> Result, NativeError> { + // A read probe before first publication must not create the storage + // root/state lock: doing so would erase the later genuine producer's + // exclusive root-creation event and leave its outputs untracked. + match fs::symlink_metadata(self.layout.active_pointer_path()) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(error.into()), + Ok(_) => {} + } let _state_lease = self.open_state_lock(LockMode::Shared)?; let Some(active) = self.read_active_generation()? else { return Ok(None); }; let generation = self.layout.generation(&active.generation_id)?; + let root = fs::symlink_metadata(generation.root())?; + if !root.is_dir() || root.file_type().is_symlink() { + return Err(NativeError::InvalidInput( + "managed generation root is not an owned directory".into(), + )); + } + let lock = super::locks::open_shared_coordination(&generation.lease_path()).ok(); + // Missing/unwritable optional generation coordination must not deny an + // otherwise committed read. Actual physical DB/manifest family guards + // protect this snapshot even in that legacy/unknown metadata case. + let database_use = [ + super::maintenance_ownership::DatabaseUseLease::acquire_shared(&generation.db_path())?, + super::maintenance_ownership::DatabaseUseLease::acquire_shared( + &generation.manifest_path(), + )?, + ]; + // Fixed, noncreating optional metadata observation while the actual + // generation lease is held. Only a verified FINAL blocks late reads; + // untracked/unknown optional records cannot deny a committed read. + if let Ok(memory) = super::maintenance_budget::CleanupMemory::new(64 * 1024) { + if matches!( + super::maintenance_generation_gate::observe_generation_disposition( + &self.layout, + &generation, + &memory + ), + Ok(super::maintenance_generation_gate::GenerationDisposition::Final) + ) { + return Err(NativeError::InvalidInput( + "managed graph generation was retired; rebuild required".into(), + )); + } + } validate_ready_generation(&generation)?; let metadata = read_generation_metadata(&generation)?; - let lock = open_locked(generation.lease_path(), LockMode::Shared)?; - let lease = Arc::new(GenerationLease::new( - lock, - self.clone(), - active.generation_id.clone(), - )); + let lease = Arc::new(GenerationLease::new(lock, database_use)); Ok(Some(ManagedReadSnapshot { generation_id: active.generation_id, db_path: generation.db_path(), @@ -423,73 +773,312 @@ impl ManagedStore { })) } - pub(crate) fn cleanup(&self) -> Result { - self.ensure_layout()?; - // Cleanup is opportunistic for readers. Never replay or collect while - // a writer or an unproven native phase still owns candidate files. - let Some(_writer_lease) = - try_open_locked(self.layout.writer_lock_path(), LockMode::Exclusive)? + /// Committed readers resolve only required transactions. Optional generation + /// metadata and janitor traversal never gate opening an existing graph. + fn has_existing_run_work(&self) -> Result { + match fs::symlink_metadata(self.layout.runs_root()) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(error.into()), + Ok(_) => self.has_reference_run_work(), + } + } + fn deferred_required_report( + reason: &'static str, + pending: bool, + recovery_complete: bool, + ) -> ManagedReconciliation { + ManagedReconciliation::new( + ManagedRecoveryReport { + observation_complete: recovery_complete, + ..ManagedRecoveryReport::default() + }, + ManagedJanitorReport { + deferred_reason: Some(reason), + pending, + ..ManagedJanitorReport::default() + }, + ) + } + pub(crate) fn recover_for_read(&self) -> Result { + if !self.has_existing_run_work()? { + return Ok(Self::deferred_required_report( + "managed_janitor_deferred", + false, + true, + )); + } + let Some(writer) = try_open_locked(self.layout.writer_lock_path(), LockMode::Exclusive)? else { - return self.pending_cleanup_report(); + return Ok(Self::deferred_required_report( + "managed_writer_busy", + true, + false, + )); }; - self.cleanup_with_owned_writer() + self.recover_required_with_owned_writer(&writer) } - - fn cleanup_with_owned_writer(&self) -> Result { + fn recover_required_with_owned_writer( + &self, + _writer: &WriterLease, + ) -> Result { + let mut result = Self::deferred_required_report("managed_janitor_deferred", false, true); + if !self.has_existing_run_work()? { + return Ok(result); + } + result.janitor.pending = true; if !matches!( probe_registry(&self.layout.phase_workspace_root()), Ok(true) ) { - return self.pending_cleanup_report(); + return Ok(Self::deferred_required_report( + "managed_containment_unresolved", + true, + false, + )); } - let _state_lease = self.open_state_lock(LockMode::Exclusive)?; - let mut report = ManagedCleanupReport::default(); + let domain = super::maintenance_ownership::ReferenceDomain::managed(&self.layout)?; + let _mutation = + match super::maintenance_ownership::ReferenceMutation::try_begin_in_domain(&domain) { + Ok(Some(mutation)) => mutation, + Ok(None) | Err(_) => { + return Ok(Self::deferred_required_report( + "managed_reference_control_unavailable", + true, + false, + )) + } + }; + let _state = self.open_state_lock(LockMode::Exclusive)?; let mut active = self.read_active_generation()?; - report.run_recovery = self.recover_runs_locked(&mut active)?; - self.collect_retired_generations_locked( - active.as_ref().map(|value| value.generation_id.as_str()), - &mut report, + self.recover_runs_locked_typed( + &mut active, + &mut result.janitor, + &mut result.recovery, + false, + &_mutation, )?; - report.retired_generations_deleted = report.retired_deleted; - report.retired_generations_pending = report.retired_pending; - Ok(report) + Ok(ManagedReconciliation::new(result.recovery, result.janitor)) } - fn pending_cleanup_report(&self) -> Result { - // This report is observational: never acquire another writer's locks, - // replay journals, or remove retained work merely to report pending GC. - let mut report = ManagedCleanupReport::default(); - report.run_recovery.skipped_locked = - existing_directories_with_prefix(&self.layout.runs_root(), "run-")?.len(); - let active = match self.read_active_generation() { - Ok(active) => active, - Err(e) if is_not_found_error(&e) => None, - Err(e) => return Err(e), + #[cfg(test)] + pub(crate) fn cleanup(&self) -> Result { + self.cleanup_typed()?.janitor.into_legacy() + } + #[cfg(test)] + pub(crate) fn cleanup_typed(&self) -> Result { + self.ensure_layout()?; + let Some(writer) = try_open_locked(self.layout.writer_lock_path(), LockMode::Exclusive)? + else { + return self.pending_reconciliation("managed_writer_busy"); }; - for generation in existing_directories_with_prefix(&self.layout.generations_root(), "gen-")? + self.cleanup_with_owned_writer_typed(&writer) + } + #[cfg(test)] + fn cleanup_with_owned_writer_typed( + &self, + _writer: &WriterLease, + ) -> Result { + let run_work = self.has_reference_run_work()?; + let active_before = self.read_active_with_shared_lock()?; + let mut report = ManagedJanitorReport::complete(); + let generation_work = match self + .has_generation_cleanup_work(active_before.as_ref().map(|a| a.generation_id.as_str())) { - if active.as_ref().is_some_and(|a| { - generation - .file_name() - .is_some_and(|n| n == format!("gen-{}", a.generation_id).as_str()) - }) { + Ok(work) => work, + Err(error) => { + report.issue(ManagedJanitorStage::GenerationEnumeration, error, true); + report.observation_complete = false; + false + } + }; + if !run_work && !generation_work { + let observed = self.pending_reconciliation("managed_cleanup_observation")?; + report.merge_latest(observed.janitor); + return Ok(ManagedReconciliation::new(observed.recovery, report)); + } + if !matches!( + probe_registry(&self.layout.phase_workspace_root()), + Ok(true) + ) { + let mut deferred = self.pending_reconciliation("managed_containment_unresolved")?; + report.merge_latest(deferred.janitor); + report.observation_complete = false; + deferred = ManagedReconciliation::new(deferred.recovery, report); + return Ok(deferred); + } + let domain = match super::maintenance_ownership::ReferenceDomain::managed(&self.layout) { + Ok(domain) => domain, + Err(_) => { + return self.pending_reconciliation_with_issue( + "managed_reference_control_unavailable", + report, + ) + } + }; + let _mutation = + match super::maintenance_ownership::ReferenceMutation::try_begin_in_domain(&domain) { + Ok(Some(mutation)) => mutation, + Ok(None) | Err(_) => { + return self.pending_reconciliation_with_issue( + "managed_reference_control_unavailable", + report, + ) + } + }; + let _state = self.open_state_lock(LockMode::Exclusive)?; + let mut active = self.read_active_generation()?; + let mut recovery = ManagedRecoveryReport { + observation_complete: true, + ..ManagedRecoveryReport::default() + }; + self.recover_runs_locked_typed(&mut active, &mut report, &mut recovery, true, &_mutation) + .map_err(|primary| append_janitor_secondary(primary, &mut report))?; + if report.first_issue.is_none() { + if let Err(error) = self.collect_retired_generations_locked( + active.as_ref().map(|a| a.generation_id.as_str()), + &mut report.legacy, + ) { + report.issue(ManagedJanitorStage::RetiredGenerationRemoval, error, true); + report.observation_complete = false; + } + } else { + report.pending = true; + report.observation_complete = false; + } + report.legacy.retired_generations_deleted = report.legacy.retired_deleted; + report.legacy.retired_generations_pending = report.legacy.retired_pending; + report.pending |= + report.legacy.retired_pending > 0 || report.legacy.run_recovery.skipped_locked > 0; + Ok(ManagedReconciliation::new(recovery, report)) + } + #[cfg(test)] + fn pending_reconciliation_with_issue( + &self, + reason: &'static str, + mut issue: ManagedJanitorReport, + ) -> Result { + let observed = self.pending_reconciliation(reason)?; + issue.merge_latest(observed.janitor); + issue.observation_complete = false; + Ok(ManagedReconciliation::new(observed.recovery, issue)) + } + fn has_reference_run_work(&self) -> Result { + for entry in fs::read_dir(self.layout.runs_root())? { + let entry = entry?; + if entry.file_type()?.is_dir() + && entry.file_name().to_string_lossy().starts_with("run-") + && RunWorkspace::reference_domain(&entry.path())?.is_some() + { + return Ok(true); + } + } + Ok(false) + } + #[cfg(test)] + fn has_generation_cleanup_work(&self, active: Option<&str>) -> Result { + #[cfg(test)] + managed_fault(ManagedFault::GenerationScan)?; + for entry in fs::read_dir(self.layout.generations_root())? { + let entry = entry?; + if !entry.file_type()?.is_dir() + || active.is_some_and(|id| entry.file_name() == format!("gen-{id}").as_str()) + { continue; } - match fs::symlink_metadata(generation.join("retired.json")) { - Ok(metadata) if metadata.is_file() => report.retired_pending += 1, + let retired = match fs::symlink_metadata(entry.path().join("retired.json")) { + Ok(_) => true, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => false, + Err(e) => return Err(e.into()), + }; + let ready = match fs::symlink_metadata(entry.path().join("READY")) { + Ok(_) => true, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => false, + Err(e) => return Err(e.into()), + }; + if retired || !ready { + return Ok(true); + } + } + Ok(false) + } + #[cfg(test)] + fn pending_reconciliation( + &self, + reason: &'static str, + ) -> Result { + let mut report = ManagedJanitorReport::complete(); + report.deferred_reason = Some(reason); + for entry in fs::read_dir(self.layout.runs_root())? { + let entry = match entry { + Ok(e) => e, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue, + Err(e) => return Err(e.into()), + }; + match entry.file_type() { + Ok(kind) + if kind.is_dir() && entry.file_name().to_string_lossy().starts_with("run-") => + { + report.legacy.run_recovery.skipped_locked += 1 + } Ok(_) => {} Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} Err(e) => return Err(e.into()), } } - report.retired_generations_pending = report.retired_pending; - Ok(report) + let active = self.read_active_generation()?; + let generation_observation = (|| -> Result<(), NativeError> { + #[cfg(test)] + managed_fault(ManagedFault::GenerationScan)?; + for entry in fs::read_dir(self.layout.generations_root())? { + let entry = entry?; + if !entry.file_type()?.is_dir() { + continue; + } + if active.as_ref().is_some_and(|a| { + entry.file_name() == format!("gen-{}", a.generation_id).as_str() + }) { + continue; + } + match fs::symlink_metadata(entry.path().join("retired.json")) { + Ok(meta) if meta.is_file() => report.legacy.retired_pending += 1, + Ok(_) => {} + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(e.into()), + } + } + Ok(()) + })(); + if let Err(error) = generation_observation { + report.issue(ManagedJanitorStage::GenerationEnumeration, error, true); + report.observation_complete = false; + } + report.legacy.retired_generations_pending = report.legacy.retired_pending; + report.pending |= + report.legacy.run_recovery.skipped_locked > 0 || report.legacy.retired_pending > 0; + let recovery = ManagedRecoveryReport { + publishing_recovered: 0, + skipped_locked: report.legacy.run_recovery.skipped_locked, + observation_complete: reason == "managed_cleanup_observation" + && report.legacy.run_recovery.skipped_locked == 0, + }; + if reason != "managed_cleanup_observation" { + report.observation_complete = false; + } + Ok(ManagedReconciliation::new(recovery, report)) } pub(crate) fn read_active_generation(&self) -> Result, NativeError> { let path = self.layout.active_pointer_path(); - if !path.exists() { - return Ok(None); + match fs::symlink_metadata(&path) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(error.into()), + Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_file() => { + return Err(NativeError::InvalidInput(format!( + "active generation pointer is not a regular file: {}", + path.display() + ))) + } + Ok(_) => {} } let text = fs::read_to_string(path)?; let active: ActiveGeneration = serde_json::from_str(&text)?; @@ -525,28 +1114,41 @@ impl ManagedStore { workspace.mark_candidate_ready() } - fn publish_candidate_inner( + fn publish_candidate_inner_typed( &self, workspace: &RunWorkspace, candidate: &ManagedCandidate, - ) -> Result { + ) -> Result { ensure_registry_empty(&self.layout.phase_workspace_root()) - .map_err(NativeError::InvalidInput)?; - let _state_lease = self.open_state_lock(LockMode::Exclusive)?; - let mut active = self.read_active_generation()?; - self.finish_run_locked(workspace.root(), &workspace.journal()?, &mut active, false)?; - Ok(candidate.generation_id.clone()) + .map_err(|e| PublicationFailure::before(NativeError::InvalidInput(e)))?; + let _state = self + .open_state_lock(LockMode::Exclusive) + .map_err(PublicationFailure::before)?; + let mut active = self + .read_active_generation() + .map_err(PublicationFailure::before)?; + let journal = workspace.journal().map_err(PublicationFailure::before)?; + self.finish_run_transaction_locked(&journal, workspace.root(), &mut active, false)?; + // No optional predecessor marker/removal occurs before the API proves + // native coherence. Its NativeVerifiedGeneration capability is the + // sole production entry to new Origin/FINAL disposition. + let report = ManagedJanitorReport::complete(); + debug_assert_eq!( + active.as_ref().map(|a| a.generation_id.as_str()), + Some(candidate.generation_id.as_str()) + ); + Ok(report) } - fn recover_runs_locked( + fn recover_runs_locked_typed( &self, active: &mut Option, - ) -> Result { + report: &mut ManagedJanitorReport, + recovery: &mut ManagedRecoveryReport, + perform_janitor: bool, + mutation: &super::maintenance_ownership::ReferenceMutation<'_>, + ) -> Result<(), NativeError> { let runs_root = self.layout.runs_root(); - if !runs_root.exists() { - return Ok(RunWorkspaceRecovery::default()); - } - let mut report = RunWorkspaceRecovery::default(); for entry in fs::read_dir(&runs_root)? { // A worker can finish and remove its run workspace after the // directory iterator yields an entry. Windows reports that @@ -572,19 +1174,63 @@ impl ManagedStore { continue; } - // A cleanup reader must not recreate a workspace that disappeared - // after read_dir observed it. - let lease = match try_open_locked_in_existing_parent( - path.join("lease.lock"), - LockMode::Exclusive, - ) { - Ok(Some(lease)) => lease, - Ok(None) => { - report.skipped_locked += 1; + // Neither a vanished parent nor a lease removed by cooperative + // retirement may be recreated by an ordinary recovery reader. + let lease = + match try_open_existing_locked(&path.join("lease.lock"), LockMode::Exclusive) { + Ok(Some(lease)) => lease, + Ok(None) => { + report.legacy.run_recovery.skipped_locked += 1; + recovery.skipped_locked += 1; + recovery.observation_complete = false; + continue; + } + Err(error) if is_not_found_error(&error) => continue, + Err(error) => return Err(error), + }; + let expected_domain = + super::maintenance_ownership::ReferenceDomain::managed(&self.layout)?; + match RunWorkspace::reference_domain(&path)? { + Some(domain) if domain.same_scope(&expected_domain) => {} + _ => { + report.legacy.run_recovery.skipped_locked += 1; + report.observation_complete = false; + recovery.skipped_locked += 1; + recovery.observation_complete = false; continue; } - Err(error) if is_not_found_error(&error) => continue, - Err(error) => return Err(error), + } + // Optional registration damage must not preempt required journal + // replay. A validated owner-terminal control, however, is the + // actual resolution receipt and prevents replay on later reads. + let registration = if !perform_janitor { + match super::maintenance_controls::WorkspaceRegistration::recover_run( + &path, + &expected_domain, + &lease, + mutation, + ) + .and_then(|registration| { + let terminal = registration + .as_ref() + .map(|registration| registration.terminal_complete()) + .transpose()? + .unwrap_or(false); + Ok((registration, terminal)) + }) { + Ok((_, true)) => { + report.pending = true; + report.observation_complete = false; + continue; + } + Ok((registration, false)) => registration, + Err(error) => { + report.issue(ManagedJanitorStage::TerminalRunRemoval, error, true); + None + } + } + } else { + None }; let journal = match read_run_journal_or_default(&path, name) { Ok(journal) => journal, @@ -594,121 +1240,265 @@ impl ManagedStore { } Err(error) => return Err(error), }; - let recovered = match journal.phase { - RunPhase::Publishing | RunPhase::Published => { - self.finish_run_locked(&path, &journal, active, true)?; - report.publishing_recovered += 1; - true + let resolution = if matches!(journal.phase, RunPhase::Publishing | RunPhase::Published) + { + match self.finish_run_transaction_locked(&journal, &path, active, true) { + Ok(resolution) => { + report.legacy.run_recovery.publishing_recovered += 1; + recovery.publishing_recovered += 1; + Some(resolution) + } + Err(failure) => return Err(failure.source), } - _ => false, + } else { + None }; + if !perform_janitor { + if let (Some(registration), Some(resolution)) = (®istration, &resolution) { + let disposition = match resolution { + RunResolution::Committed { .. } => { + super::maintenance_controls::WorkspaceDisposition::PublishedConsumed + } + RunResolution::Obsolete { .. } => { + super::maintenance_controls::WorkspaceDisposition::AbortedNotPublished + } + }; + let closure = (|| -> Result<(), NativeError> { + registration.source_change(&lease, mutation, false, true)?; + // Publishing closes ordinary metadata startup before its + // old journal write. Recovery must produce its own resolved + // journal and creator token; observing that old inode is + // never metadata ownership or a terminal receipt. + let resolved = RunJournal { + run_id: journal.run_id.clone(), + phase: match resolution { + RunResolution::Committed { .. } => RunPhase::Published, + RunResolution::Obsolete { .. } => RunPhase::Failed, + }, + base_generation_id: journal.base_generation_id.clone(), + candidate_generation_id: journal.candidate_generation_id.clone(), + active_generation_id: active.as_ref().map(|a| a.generation_id.clone()), + last_error: None, + }; + let publication = crate::process_lifecycle::write_private_json_recorded( + &path.join("journal.json"), + &resolved, + ) + .map_err(NativeError::InvalidInput)? + .ok_or_else(|| { + NativeError::InvalidInput( + "recovered journal creation is unverified".into(), + ) + })?; + registration.record_metadata(&lease, mutation, &publication)?; + sync_dir(&path)?; + registration.terminal(&lease, mutation, disposition) + })(); + if let Err(error) = closure { + report.issue(ManagedJanitorStage::TerminalRunRemoval, error, true); + } + } + // This run was actually locked and its required transaction + // was resolved. Retaining it for bounded optional collection + // does not invent a skipped/live recovery lock. + report.pending = true; + report.observation_complete = false; + continue; + } drop(lease); - if !recovered { - remove_run_root_confined(&path, &runs_root)?; - report.deleted += 1; + if let Some(resolution) = resolution { + self.finish_run_janitor_locked( + &path, + resolution, + active.as_ref().map(|a| a.generation_id.as_str()), + true, + report, + ); + } else if report.first_issue.is_none() { + match super::run_workspace::ensure_persisted_cleanup_allowed(&path) + .and_then(|()| remove_run_root_confined(&path, &runs_root)) + { + Ok(()) => report.legacy.run_recovery.deleted += 1, + Err(error) => { + report.issue(ManagedJanitorStage::TerminalRunRemoval, error, true); + report.legacy.run_recovery.skipped_locked += 1; + } + } + } else { + report.legacy.run_recovery.skipped_locked += 1; + report.pending = true; } } - Ok(report) + Ok(()) } - fn finish_run_locked( + fn probe_ready(paths: &GenerationPaths) -> ReadyProbe { + match fs::symlink_metadata(paths.root()) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => ReadyProbe::Absent, + Err(error) => ReadyProbe::Invalid(error.into()), + Ok(metadata) if !metadata.is_dir() || metadata.file_type().is_symlink() => { + ReadyProbe::Invalid(NativeError::InvalidInput(format!( + "invalid generation root {}", + paths.root().display() + ))) + } + Ok(_) => match validate_ready_generation(paths) { + Ok(()) => ReadyProbe::Valid, + Err(error) => ReadyProbe::Invalid(error), + }, + } + } + fn require_ready(paths: &GenerationPaths) -> Result<(), NativeError> { + match Self::probe_ready(paths) { + ReadyProbe::Valid => Ok(()), + ReadyProbe::Invalid(error) => Err(error), + ReadyProbe::Absent => Err(NativeError::InvalidInput(format!( + "required generation is absent: {}", + paths.root().display() + ))), + } + } + fn finish_run_transaction_locked( &self, - run_root: &Path, journal: &RunJournal, + run_root: &Path, active: &mut Option, - cleanup_run_root: bool, - ) -> Result<(), NativeError> { + recovery: bool, + ) -> Result { + let before = PublicationFailure::before; ensure_registry_empty(&self.layout.phase_workspace_root()) - .map_err(NativeError::InvalidInput)?; - let Some(candidate_id) = journal.candidate_generation_id.as_ref() else { - if cleanup_run_root { - remove_run_root_confined(run_root, &self.layout.runs_root())?; + .map_err(|e| before(NativeError::InvalidInput(e)))?; + let candidate_id = journal.candidate_generation_id.as_ref().ok_or_else(|| { + before(NativeError::InvalidInput( + "publishing run is missing its candidate generation".into(), + )) + })?; + validate_generation_id(candidate_id).map_err(before)?; + if let Some(base) = journal.base_generation_id.as_deref() { + validate_generation_id(base).map_err(before)?; + } + let published = self.layout.generation(candidate_id).map_err(before)?; + let active_id = active.as_ref().map(|a| a.generation_id.clone()); + if active_id.as_deref() == Some(candidate_id.as_str()) { + Self::require_ready(&published).map_err(PublicationFailure::ambiguous)?; + // Complete interrupted mandatory barriers without rewriting identity, + // timestamps, metadata or READY. Readability is not a commit ack. + sync_parent_dir(published.root()).map_err(PublicationFailure::ambiguous)?; + self.complete_root_sync() + .map_err(PublicationFailure::ambiguous)?; + return Ok(RunResolution::Committed { + previous: journal.base_generation_id.clone(), + }); + } + if active_id != journal.base_generation_id { + if !recovery { + return Err(before(NativeError::InvalidInput( + ManagedValidationError::StaleBaseGeneration { + expected: journal.base_generation_id.clone(), + found: active_id, + } + .to_string(), + ))); } - return Ok(()); - }; - validate_generation_id(candidate_id)?; - if let Some(base_generation_id) = journal.base_generation_id.as_deref() { - validate_generation_id(base_generation_id)?; + if let Some(current) = active.as_ref() { + Self::require_ready( + &self + .layout + .generation(¤t.generation_id) + .map_err(before)?, + ) + .map_err(before)?; + } else { + return Err(before(NativeError::InvalidInput( + "cannot resolve stale publishing evidence without a valid active generation" + .into(), + ))); + } + return Ok(RunResolution::Obsolete { published }); } - let staged = - CandidateGenerationLayout::new(run_root.join("candidate"), candidate_id.clone()) + match Self::probe_ready(&published) { + ReadyProbe::Invalid(error) => return Err(before(error)), + ReadyProbe::Valid => {} + ReadyProbe::Absent => { + let staged = CandidateGenerationLayout::new( + run_root.join("candidate"), + candidate_id.clone(), + ) .generation_paths(); - let published = self.layout.generation(candidate_id)?; - let active_id = active.as_ref().map(|value| value.generation_id.clone()); - let base_matches = active_id == journal.base_generation_id; - let published_valid = is_ready_generation_valid(&published); - let staged_valid = is_ready_generation_valid(&staged); - - if active_id.as_deref() == Some(candidate_id.as_str()) && published_valid { - self.finalize_previous_retirement(journal.base_generation_id.as_deref())?; - if cleanup_run_root { - remove_run_root_confined(run_root, &self.layout.runs_root())?; + Self::require_ready(&staged).map_err(before)?; + self.promote_candidate_generation_locked(&staged, &published) + .map_err(PublicationFailure::ambiguous)?; } - return Ok(()); } + // Recovered published-only state may still need promotion's directory + // barriers; complete them before making its pointer authoritative. + sync_parent_dir(published.root()).map_err(PublicationFailure::ambiguous)?; + let activated = self + .activate_published_generation_locked(&published) + .map_err(PublicationFailure::ambiguous)?; + *active = Some(activated); + Ok(RunResolution::Committed { + previous: journal.base_generation_id.clone(), + }) + } - if !base_matches { - if published.root().exists() { - write_json_atomically( - &published.retired_path(), - &GenerationRetirement { - schema_version: MANAGED_SCHEMA_VERSION, - retired_at_ms: unix_time_ms(), - }, - )?; - self.retire_or_delete_generation_locked( - &published, - active.as_ref().map(|value| value.generation_id.as_str()), - )?; - } - if cleanup_run_root { - remove_run_root_confined(run_root, &self.layout.runs_root())?; - return Ok(()); + fn finish_run_janitor_locked( + &self, + run_root: &Path, + resolution: RunResolution, + active_id: Option<&str>, + cleanup_root: bool, + report: &mut ManagedJanitorReport, + ) { + if report.first_issue.is_some() { + if cleanup_root { + report.legacy.run_recovery.skipped_locked += 1; } - return Err(NativeError::InvalidInput( - ManagedValidationError::StaleBaseGeneration { - expected: journal.base_generation_id.clone(), - found: active_id, - } - .to_string(), - )); + report.pending = true; + return; } - - if published_valid && base_matches { - let activated = self.activate_published_generation_locked( - &published, - journal.base_generation_id.as_deref(), - )?; - *active = Some(activated); - if cleanup_run_root { - remove_run_root_confined(run_root, &self.layout.runs_root())?; + if cleanup_root { + if let Err(error) = super::run_workspace::ensure_persisted_cleanup_allowed(run_root) { + report.issue(ManagedJanitorStage::TerminalRunRemoval, error, true); + report.legacy.run_recovery.skipped_locked += 1; + return; } - return Ok(()); } - - if staged_valid && base_matches { - self.promote_candidate_generation_locked(&staged, &published)?; - let activated = self.activate_published_generation_locked( - &published, - journal.base_generation_id.as_deref(), - )?; - *active = Some(activated); - if cleanup_run_root { - remove_run_root_confined(run_root, &self.layout.runs_root())?; + let action = match resolution { + RunResolution::Committed { previous } => self + .finalize_previous_retirement(previous.as_deref()) + .map_err(|e| (ManagedJanitorStage::PreviousRetirementMarker, e)), + RunResolution::Obsolete { published } => (|| -> Result<(), NativeError> { + if published.root().exists() { + write_json_atomically( + &published.retired_path(), + &GenerationRetirement { + schema_version: MANAGED_SCHEMA_VERSION, + retired_at_ms: unix_time_ms(), + }, + )?; + self.retire_or_delete_generation_locked(&published, active_id)?; + } + Ok(()) + })() + .map_err(|e| (ManagedJanitorStage::RetiredGenerationRemoval, e)), + }; + if let Err((stage, error)) = action { + report.issue(stage, error, true); + if cleanup_root { + report.legacy.run_recovery.skipped_locked += 1; } - return Ok(()); - } - - if published.root().exists() { - self.retire_or_delete_generation_locked( - &published, - active.as_ref().map(|value| value.generation_id.as_str()), - )?; + return; } - if cleanup_run_root { - remove_run_root_confined(run_root, &self.layout.runs_root())?; + if cleanup_root { + match remove_run_root_confined(run_root, &self.layout.runs_root()) { + Ok(()) => report.legacy.run_recovery.deleted += 1, + Err(error) => { + report.issue(ManagedJanitorStage::TerminalRunRemoval, error, true); + report.legacy.run_recovery.skipped_locked += 1; + } + } } - Ok(()) } fn promote_candidate_generation_locked( @@ -734,7 +1524,6 @@ impl ManagedStore { fn activate_published_generation_locked( &self, published: &GenerationPaths, - previous_active_id: Option<&str>, ) -> Result { validate_ready_generation(published)?; let mut metadata = read_generation_metadata(published)?; @@ -750,21 +1539,37 @@ impl ManagedStore { published_at: format!("unix:{now_ms}"), activated_at_ms: now_ms, }; - if let Err(error) = write_json_atomically(&self.layout.active_pointer_path(), &active) { - let _ = write_json_atomically( - &published.retired_path(), - &GenerationRetirement { - schema_version: MANAGED_SCHEMA_VERSION, - retired_at_ms: unix_time_ms(), - }, - ); - return Err(error); - } - sync_dir(self.layout.storage_root())?; - self.finalize_previous_retirement(previous_active_id)?; + self.write_active_pointer(&active)?; + self.complete_root_sync()?; Ok(active) } + fn write_active_pointer(&self, active: &ActiveGeneration) -> Result<(), NativeError> { + #[cfg(test)] + if MANAGED_FAULT.with(|f| f.get()) == ManagedFault::PointerAfterRename { + return crate::storage::atomic::write_json_atomically_with_fault( + &self.layout.active_pointer_path(), + active, + crate::storage::atomic::AtomicWriteFailure::AfterRename, + ); + } + write_json_atomically(&self.layout.active_pointer_path(), active) + } + fn complete_root_sync(&self) -> Result<(), NativeError> { + #[cfg(test)] + if SCOPED_MANAGED_SYNC_FAULT + .lock() + .unwrap_or_else(|error| error.into_inner()) + .is_some_and(|identity| { + super::locks::path_identity(self.layout.storage_root()).ok() == Some(identity) + }) + { + return Err(std::io::Error::other("injected mandatory managed sync failure").into()); + } + #[cfg(test)] + managed_fault(ManagedFault::MandatorySync)?; + sync_dir(self.layout.storage_root()) + } fn finalize_previous_retirement( &self, previous_active_id: Option<&str>, @@ -785,11 +1590,14 @@ impl ManagedStore { Ok(()) } + #[cfg(test)] fn collect_retired_generations_locked( &self, active_id: Option<&str>, report: &mut ManagedCleanupReport, ) -> Result<(), NativeError> { + #[cfg(test)] + managed_fault(ManagedFault::GenerationScan)?; let generations_root = self.layout.generations_root(); if !generations_root.exists() { return Ok(()); @@ -861,38 +1669,6 @@ impl ManagedStore { Ok(()) } - fn best_effort_cleanup_generation(&self, generation_id: &str) { - let Ok(Some(_writer_lease)) = - try_open_locked(self.layout.writer_lock_path(), LockMode::Exclusive) - else { - return; - }; - if !matches!( - probe_registry(&self.layout.phase_workspace_root()), - Ok(true) - ) { - return; - } - let Ok(Some(_state_lock)) = - try_open_locked(self.layout.state_lock_path(), LockMode::Exclusive) - else { - return; - }; - let Ok(active) = self.read_active_generation() else { - return; - }; - if active.as_ref().map(|value| value.generation_id.as_str()) == Some(generation_id) { - return; - } - let Ok(generation) = self.layout.generation(generation_id) else { - return; - }; - let _ = self.retire_or_delete_generation_locked( - &generation, - active.as_ref().map(|value| value.generation_id.as_str()), - ); - } - fn open_state_lock(&self, mode: LockMode) -> Result { open_locked(self.layout.state_lock_path(), mode) } @@ -905,18 +1681,6 @@ impl ManagedStore { let _lease = self.open_state_lock(LockMode::Shared)?; self.read_active_generation() } - - fn publication_is_committed(&self, generation_id: &str) -> Result { - let _lease = self.open_state_lock(LockMode::Shared)?; - let Some(active) = self.read_active_generation()? else { - return Ok(false); - }; - if active.generation_id != generation_id { - return Ok(false); - } - validate_ready_generation(&self.layout.generation(generation_id)?)?; - Ok(true) - } } pub(crate) struct GraphStorage; @@ -1034,10 +1798,6 @@ fn validate_ready_generation(paths: &GenerationPaths) -> Result<(), NativeError> Ok(()) } -fn is_ready_generation_valid(paths: &GenerationPaths) -> bool { - validate_ready_generation(paths).is_ok() -} - fn generation_sizes(paths: &GenerationPaths) -> Result { let mut logical = 0_u64; let mut physical = 0_u64; @@ -1124,39 +1884,6 @@ fn remove_path_without_symlinks(path: &Path) -> Result<(), NativeError> { Ok(()) } -fn existing_directories_with_prefix( - root: &Path, - prefix: &str, -) -> Result, NativeError> { - let entries = match fs::read_dir(root) { - Ok(entries) => entries, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()), - Err(e) => return Err(e.into()), - }; - let mut paths = Vec::new(); - for entry in entries { - let entry = match entry { - Ok(entry) => entry, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue, - Err(e) => return Err(e.into()), - }; - if !entry - .file_name() - .to_str() - .is_some_and(|name| name.starts_with(prefix)) - { - continue; - } - match entry.file_type() { - Ok(kind) if kind.is_dir() => paths.push(entry.path()), - Ok(_) => {} - Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} - Err(e) => return Err(e.into()), - } - } - Ok(paths) -} - fn read_run_journal_or_default(path: &Path, name: &str) -> Result { let journal_path = path.join("journal.json"); let text = match fs::read_to_string(&journal_path) { @@ -1224,9 +1951,526 @@ fn abort_workspace_after_error(workspace: RunWorkspace, primary: NativeError) -> } } -fn finish_committed_workspace(workspace: RunWorkspace, generation_id: &str) { - // The active pointer is the transaction commit. Journal finalization and - // workspace removal remain recoverable cleanup after that boundary. - let _ = workspace.mark_phase(RunPhase::Published, Some(generation_id.to_string()), None); - let _ = workspace.finish(); +#[cfg(test)] +fn append_janitor_secondary( + primary: NativeError, + report: &mut ManagedJanitorReport, +) -> NativeError { + match report.first_issue.take() { + None => primary, + Some(issue) => { + NativeError::InvalidInput(format!("{primary}; cleanup failed: {}", issue.source)) + } + } +} + +#[cfg(test)] +mod transaction_janitor_tests { + use super::*; + use crate::protocol::GraphSummary; + + fn store(label: &str) -> (PathBuf, ManagedStore) { + let root = std::env::temp_dir().canonicalize().unwrap().join(format!( + "managed-role-{label}-{}-{}", + std::process::id(), + unix_time_ms() + )); + fs::create_dir(&root).unwrap(); + ( + root.clone(), + ManagedStore::new(ManagedLayout::new(root.join("storage"))), + ) + } + #[test] + fn managed_absent_read_probe_preserves_genuine_first_producer_root_creation() { + let (root, store) = store("noncreating-absent-read"); + assert!(!store.layout.storage_root().exists()); + assert!(store.resolve_active_read().unwrap().is_none()); + assert!( + !store.layout.storage_root().exists(), + "read probe created producer namespace" + ); + let session = store.begin_write().unwrap(); + session.abort(None).unwrap(); + assert!( + matches!( + super::super::maintenance_ownership::observe_reference_epoch( + &super::super::maintenance_ownership::ReferenceDomain::managed(store.layout()) + .unwrap() + ) + .unwrap(), + super::super::maintenance_ownership::EpochObservation::Verified(_) + ), + "actual first producer did not initialize its fresh reference domain" + ); + fs::remove_dir_all(root).unwrap(); + } + + fn prepared(store: &ManagedStore) -> ManagedWriteSession { + let mut session = store.begin_write().unwrap(); + fs::write(session.candidate_db_path(), b"storage ABI fixture").unwrap(); + fs::write(session.candidate_manifest_path(), b"{}").unwrap(); + session + .mark_ready_with_stats(&GraphSummary::default()) + .unwrap(); + session + } + fn leave_publishing(mut session: ManagedWriteSession, promoted: bool) -> PathBuf { + let workspace = session.workspace.take().unwrap(); + workspace + .mark_phase(RunPhase::Publishing, None, None) + .unwrap(); + if promoted { + let _state = session.store.open_state_lock(LockMode::Exclusive).unwrap(); + let published = session + .store + .layout + .generation(&session.candidate.generation_id) + .unwrap(); + session + .store + .promote_candidate_generation_locked(&session.candidate.paths, &published) + .unwrap(); + } + let root = workspace.root().to_path_buf(); + workspace.retain_for_recovery(); + session.finish_without_cleanup(); + root + } + + #[test] + fn managed_read_recovery_does_not_recreate_a_partially_retired_run_lease() { + use crate::storage::maintenance_budget::{CleanupMemory, MutationGrant}; + use crate::storage::{ + maintenance_controls as controls, maintenance_workspace_gate as gate, + }; + use std::time::Duration; + let (root, store) = store("noncreating-partial-retirement"); + // A genuine still-Publishing sibling forces required read recovery; + // it must remain recoverable while another closed run is being removed. + let sibling = leave_publishing(prepared(&store), false); + let domain = + super::super::maintenance_ownership::ReferenceDomain::managed(store.layout()).unwrap(); + let mut run = RunWorkspace::create_scoped(store.layout.runs_root(), &domain, None).unwrap(); + run.set_cleanup_barrier(store.layout.phase_workspace_root()) + .unwrap(); + let retired = run.retire_for_maintenance().unwrap(); + // Isolate fixed root transport after receipt collection. Every receipt + // here was created by this fixture, as in the root-transport unit tests. + for entry in fs::read_dir(retired.join(".workspace-origins")).unwrap() { + fs::remove_file(entry.unwrap().path()).unwrap(); + } + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + let mut terminal = + controls::prepare_terminal_workspace_boxed(&retired, &domain, &memory).unwrap(); + let mut initial = + gate::prepare_workspace_root_removal(&retired, &domain, Some(&terminal), &memory) + .unwrap(); + let mut grant = MutationGrant::for_test(100, Duration::from_millis(100)); + gate::begin_workspace_root_removal(&mut terminal, &mut initial, &mut grant).unwrap(); + drop(initial); + drop(terminal); + let mut observed_gap = false; + for _ in 0..16 { + let mut next = gate::prepare_pending_workspace_root_removal(&domain, &memory) + .unwrap() + .unwrap(); + let mut grant = MutationGrant::for_test(100, Duration::from_millis(100)); + let progress = + gate::advance_workspace_root_removal(&mut next, &mut grant, 0, &mut |_, _| { + panic!("root should not complete before the lease/rmdir grant seam") + }) + .unwrap(); + if retired.exists() && !retired.join("lease.lock").exists() { + assert_eq!(progress, gate::WorkspaceRemovalProgress::Pending); + observed_gap = true; + break; + } + } + assert!( + observed_gap, + "did not reach actual cooperative lease-removal seam" + ); + let recovered = store.recover_for_read().unwrap(); + assert_eq!(recovered.recovery.publishing_recovered, 1); + assert!(recovered.janitor.first_issue.is_none(), "{recovered:?}"); + assert!(retired.exists()); + assert!( + !retired.join("lease.lock").exists(), + "recovery recreated a removed lease inode" + ); + assert!( + sibling.exists(), + "required read recovery removed retained sibling payload" + ); + let mut complete = false; + for _ in 0..8 { + let mut next = gate::prepare_pending_workspace_root_removal(&domain, &memory) + .unwrap() + .unwrap(); + let existed = retired.exists(); + let mut grant = MutationGrant::for_test(100, Duration::from_millis(100)); + let progress = gate::advance_workspace_root_removal( + &mut next, + &mut grant, + 0, + &mut |outcome, _| { + match outcome { + gate::WorkspaceRootOutcome::Removed(_) => assert!(existed), + gate::WorkspaceRootOutcome::RecoveredMissing(_) => assert!(!existed), + } + assert!(!retired.exists()); + Ok(()) + }, + ) + .unwrap(); + if progress == gate::WorkspaceRemovalProgress::Complete { + complete = true; + break; + } + } + assert!(complete); + assert!(!retired.exists()); + assert!( + gate::prepare_pending_workspace_root_removal(&domain, &memory) + .unwrap() + .is_none() + ); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn managed_read_recovery_closes_registered_run_without_replaying() { + use crate::storage::maintenance_budget::CleanupMemory; + for promoted in [false, true] { + let (root, store) = store(if promoted { + "read-published" + } else { + "read-staged" + }); + let session = prepared(&store); + let id = session.candidate.generation_id.clone(); + let run = leave_publishing(session, promoted); + assert!( + run.join(".workspace.binding").is_file(), + "fixture must have genuine producer registration" + ); + let domain = + super::super::maintenance_ownership::ReferenceDomain::managed(store.layout()) + .unwrap(); + let memory = CleanupMemory::new(8 * 1024 * 1024).unwrap(); + assert!( + super::super::maintenance_controls::prepare_terminal_workspace_boxed( + &run, &domain, &memory + ) + .is_err() + ); + let recovered = store.recover_for_read().unwrap(); + assert_eq!(recovered.recovery.publishing_recovered, 1); + assert!(recovered.janitor.first_issue.is_none(), "{recovered:?}"); + assert!( + run.exists(), + "read recovery traversed/removed retained payload" + ); + assert_eq!( + store + .read_active_generation() + .unwrap() + .unwrap() + .generation_id, + id + ); + let terminal = super::super::maintenance_controls::prepare_terminal_workspace_boxed( + &run, &domain, &memory, + ) + .unwrap(); + drop(terminal); + let pointer = fs::read(store.layout.active_pointer_path()).unwrap(); + let repeated = store.recover_for_read().unwrap(); + assert_eq!( + repeated.recovery.publishing_recovered, 0, + "owner-terminal recovered publication replayed on next read" + ); + assert_eq!( + fs::read(store.layout.active_pointer_path()).unwrap(), + pointer + ); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn managed_typed_recovery_accepts_staged_only_and_published_only_seams() { + for promoted in [false, true] { + let (root, store) = store(if promoted { "published" } else { "staged" }); + let session = prepared(&store); + let id = session.candidate.generation_id.clone(); + let run = leave_publishing(session, promoted); + let result = store.cleanup_typed().unwrap(); + assert_eq!(result.recovery.publishing_recovered, 1); + assert!(result.recovery.observation_complete); + assert!(result.janitor.first_issue.is_none()); + assert_eq!( + store + .read_active_generation() + .unwrap() + .unwrap() + .generation_id, + id + ); + assert!(!run.exists()); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn managed_typed_ambiguous_pointer_error_retains_bytes_and_requires_durability_recovery() { + let (root, store) = store("ambiguous"); + let mut session = prepared(&store); + let id = session.candidate.generation_id.clone(); + let run = session.workspace.as_ref().unwrap().root().to_path_buf(); + let fault = inject_managed_fault(ManagedFault::PointerAfterRename); + let error = session + .publish_with_stats_typed(&GraphSummary::default()) + .unwrap_err(); + assert!(error.to_string().contains("after rename")); + drop(fault); + drop(session); + let generation = store.layout.generation(&id).unwrap(); + assert!( + !generation.retired_path().exists(), + "uncertain pointer failure marked candidate retired" + ); + let evidence = || { + [ + fs::read(run.join("journal.json")).unwrap(), + fs::read(store.layout.active_pointer_path()).unwrap(), + fs::read(generation.metadata_path()).unwrap(), + fs::read(generation.ready_path()).unwrap(), + ] + }; + let before = evidence(); + assert!( + try_open_locked(store.layout.writer_lock_path(), LockMode::Exclusive) + .unwrap() + .is_some() + ); + let fault = inject_managed_fault(ManagedFault::MandatorySync); + assert!(store + .cleanup_typed() + .unwrap_err() + .to_string() + .contains("mandatory managed sync")); + assert_eq!(evidence(), before); + drop(fault); + let result = store.cleanup_typed().unwrap(); + assert_eq!(result.recovery.publishing_recovered, 1); + assert_eq!( + fs::read(store.layout.active_pointer_path()).unwrap(), + before[1] + ); + assert_eq!(fs::read(generation.metadata_path()).unwrap(), before[2]); + assert_eq!(fs::read(generation.ready_path()).unwrap(), before[3]); + assert!(!run.exists()); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn managed_typed_required_corrupt_or_missing_evidence_is_not_an_absent_alternative() { + for corrupt_present in [false, true] { + let (root, store) = store(if corrupt_present { + "corrupt" + } else { + "missing" + }); + let session = prepared(&store); + let staged = session.candidate.paths.root().to_path_buf(); + let published = store + .layout + .generation(&session.candidate.generation_id) + .unwrap(); + let run = leave_publishing(session, false); + if corrupt_present { + fs::create_dir_all(published.root()).unwrap(); + fs::write(published.db_path(), b"invalid present evidence").unwrap(); + } else { + fs::remove_dir_all(&staged).unwrap(); + } + let journal = fs::read(run.join("journal.json")).unwrap(); + assert!(store.cleanup_typed().is_err()); + assert_eq!(fs::read(run.join("journal.json")).unwrap(), journal); + if corrupt_present { + assert!( + staged.exists(), + "silently preferred/replaced staged alternative" + ); + } + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn managed_typed_generation_issue_never_preempts_required_replay_or_its_error() { + for corrupt_run in [false, true] { + let (root, store) = store(if corrupt_run { + "later-error" + } else { + "later-success" + }); + let session = prepared(&store); + let id = session.candidate.generation_id.clone(); + let run = leave_publishing(session, false); + if corrupt_run { + let mut journal: RunJournal = + serde_json::from_slice(&fs::read(run.join("journal.json")).unwrap()).unwrap(); + journal.candidate_generation_id = Some("../invalid".into()); + write_json_atomically(&run.join("journal.json"), &journal).unwrap(); + } + let fault = inject_managed_fault(ManagedFault::GenerationScan); + if corrupt_run { + let error = store.cleanup_typed().unwrap_err().to_string(); + assert!( + error.starts_with("invalid managed generation id"), + "{error}" + ); + assert!( + error.contains("cleanup failed: injected generation-only"), + "{error}" + ); + } else { + let result = store.cleanup_typed().unwrap(); + assert_eq!(result.recovery.publishing_recovered, 1); + assert!(result.recovery.observation_complete); + assert!(result.janitor.first_issue.is_some()); + assert_eq!( + store + .read_active_generation() + .unwrap() + .unwrap() + .generation_id, + id + ); + assert!(run.exists(), "first optional issue should defer disposal"); + assert_eq!(result.janitor.legacy.run_recovery.skipped_locked, 1); + } + drop(fault); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn managed_typed_recovered_publication_retains_run_until_persisted_barrier_resolves() { + let (root, store) = store("persisted-barrier"); + let session = prepared(&store); + let id = session.candidate.generation_id.clone(); + let run = leave_publishing(session, false); + fs::write(run.join("cleanup-barrier.json"), b"invalid cleanup barrier").unwrap(); + let journal = fs::read(run.join("journal.json")).unwrap(); + let report = store.cleanup_typed().unwrap(); + assert_eq!(report.recovery.publishing_recovered, 1); + assert!(report.recovery.observation_complete); + assert!(report.janitor.pending); + assert_eq!(report.janitor.legacy.run_recovery.skipped_locked, 1); + assert!(report + .janitor + .first_issue + .as_ref() + .unwrap() + .source + .to_string() + .contains("invalid run cleanup barrier")); + assert_eq!( + store + .read_active_generation() + .unwrap() + .unwrap() + .generation_id, + id + ); + assert_eq!(fs::read(run.join("journal.json")).unwrap(), journal); + // Strict compatibility still returns the original optional source and + // never bypasses the persisted containment gate on its next attempt. + assert!(store + .cleanup() + .unwrap_err() + .to_string() + .contains("invalid run cleanup barrier")); + assert!(run.exists()); + fs::remove_file(run.join("cleanup-barrier.json")).unwrap(); + store.cleanup().unwrap(); + assert!(!run.exists()); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn managed_typed_merge_preserves_first_issue_with_a_complete_latest_report() { + // Pure report algebra: this synthetic complete observation makes no + // filesystem-disposal claim. Actual removal is tested by bounded GC. + let mut first = ManagedJanitorReport::complete(); + first.issue( + ManagedJanitorStage::PublishedRunJournal, + NativeError::InvalidInput("first journal diagnostic".into()), + true, + ); + first.legacy.run_recovery.skipped_locked = 1; + let mut latest = ManagedJanitorReport::complete(); + latest.issue( + ManagedJanitorStage::TerminalRunRemoval, + NativeError::InvalidInput("second diagnostic".into()), + false, + ); + first.merge_latest(latest); + assert_eq!(first.issue_count, 2); + assert!(!first.pending); + assert_eq!(first.legacy.run_recovery.skipped_locked, 0); + assert_eq!( + first.first_issue.as_ref().unwrap().source.to_string(), + "first journal diagnostic" + ); + assert_eq!( + first.into_legacy().unwrap_err().to_string(), + "first journal diagnostic" + ); + } + + #[test] + fn managed_registered_owner_finish_retains_terminal_work_for_bounded_collection() { + let (root, store) = store("terminal-retention"); + let mut session = prepared(&store); + // Strict recovery may report the genuinely live owner without deleting + // its workspace or turning that pending state into a primary error. + assert_eq!( + session + .recover_and_gc() + .unwrap() + .run_recovery + .skipped_locked, + 1 + ); + let candidate = session.candidate_db_path(); + let bytes = fs::read(&candidate).unwrap(); + let workspace = session.workspace.take().unwrap(); + let run = workspace.root().to_path_buf(); + workspace.finish().unwrap(); + assert!(run.exists()); + assert_eq!(fs::read(&candidate).unwrap(), bytes); + session.finish_without_cleanup(); + let domain = + super::super::maintenance_ownership::ReferenceDomain::managed(store.layout()).unwrap(); + let memory = super::super::maintenance_budget::CleanupMemory::new(8 * 1024 * 1024).unwrap(); + super::super::maintenance_controls::prepare_terminal_workspace_boxed( + &run, &domain, &memory, + ) + .unwrap(); + let latest = store.recover_for_read().unwrap(); + assert!( + latest.janitor.pending, + "terminal retention was reported disposed" + ); + assert_eq!(latest.janitor.legacy.run_recovery.skipped_locked, 0); + assert!(latest.janitor.first_issue.is_none()); + assert!(run.exists()); + assert_eq!(fs::read(candidate).unwrap(), bytes); + fs::remove_dir_all(root).unwrap(); + } } diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 06738ec8..030a57d2 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1,7 +1,39 @@ pub(crate) mod atomic; pub(crate) mod containment; pub(crate) mod direct; +pub(crate) mod inventory; pub(crate) mod layout; pub(crate) mod locks; +pub(crate) mod maintenance; +pub(crate) mod maintenance_admission; +pub(crate) mod maintenance_backup; +pub(crate) mod maintenance_budget; +pub(crate) mod maintenance_candidate; +pub(crate) mod maintenance_controls; +pub(crate) mod maintenance_generation_engine; +pub(crate) mod maintenance_generation_gate; +pub(crate) mod maintenance_generations; +pub(crate) mod maintenance_journal; +pub(crate) mod maintenance_json; +pub(crate) mod maintenance_legacy_association; +pub(crate) mod maintenance_legacy_bootstrap; +pub(crate) mod maintenance_legacy_engine; +pub(crate) mod maintenance_legacy_generations_engine; +pub(crate) mod maintenance_legacy_guard; +pub(crate) mod maintenance_legacy_staging; +pub(crate) mod maintenance_legacy_stream; +pub(crate) mod maintenance_native_projection; +pub(crate) mod maintenance_origin; +pub(crate) mod maintenance_ownership; +pub(crate) mod maintenance_projection; +pub(crate) mod maintenance_references; +pub(crate) mod maintenance_retirement; +pub(crate) mod maintenance_scheduler; +pub(crate) mod maintenance_validation; +pub(crate) mod maintenance_workspace_gate; pub(crate) mod managed; pub(crate) mod run_workspace; + +#[cfg(all(test, debug_assertions))] +#[path = "../../tests/support/storage_maintenance/collector.rs"] +mod maintenance_collector_tests; diff --git a/src/storage/run_workspace.rs b/src/storage/run_workspace.rs index 34ca4f2e..fe0f8342 100644 --- a/src/storage/run_workspace.rs +++ b/src/storage/run_workspace.rs @@ -1,7 +1,10 @@ use crate::error::NativeError; use crate::storage::atomic::write_json_atomically; use crate::storage::layout::managed_generation_id; -use crate::storage::locks::{open_locked, try_open_locked_in_existing_parent, LockMode, RunLease}; +use crate::storage::locks::RunLease; +#[cfg(test)] +use crate::storage::locks::{try_open_locked_in_existing_parent, LockMode}; +use crate::storage::maintenance_ownership::{ReferenceDomain, ReferenceMutation}; use serde::{Deserialize, Serialize}; use std::fs; use std::path::{Path, PathBuf}; @@ -43,29 +46,114 @@ pub(crate) struct RunWorkspaceRecovery { pub(crate) struct RunWorkspace { root: PathBuf, run_id: String, - lease: Option, + lease: Option>, finished: bool, cleanup_barrier: Option, + _reference_mutation: Option>, + workspace_registration: + Option>, +} + +/// Retains the real parent owner lease through phase creation/execution. This +/// cannot be fabricated from an addressed run directory or serialized facts. +#[derive(Debug)] +pub(crate) struct ParentRunScope { + owner: std::sync::Arc, + registration: std::sync::Arc, +} +impl ParentRunScope { + pub(crate) fn retain(&self) -> Self { + Self { + owner: std::sync::Arc::clone(&self.owner), + registration: std::sync::Arc::clone(&self.registration), + } + } + pub(crate) fn domain(&self) -> &ReferenceDomain { + self.registration.domain() + } + pub(super) fn owner(&self) -> &RunLease { + &self.owner + } + pub(super) fn registration( + &self, + ) -> &crate::storage::maintenance_controls::WorkspaceRegistration { + &self.registration + } } impl RunWorkspace { + // Only legacy/test fixtures may create a workspace without an association; + // production callers must supply their actual reference domain. + #[cfg(test)] pub(crate) fn create( runs_root: impl AsRef, base_generation_id: Option, ) -> Result { - let runs_root = runs_root.as_ref(); + Self::create_inner(runs_root.as_ref(), None, base_generation_id) + } + + pub(crate) fn create_scoped( + runs_root: impl AsRef, + domain: &ReferenceDomain, + base_generation_id: Option, + ) -> Result { + if !domain.allows_runs_root(runs_root.as_ref())? { + return Err(NativeError::InvalidInput( + "reference_run_root_mismatch".into(), + )); + } + Self::create_inner(runs_root.as_ref(), Some(domain), base_generation_id) + } + + fn create_inner( + runs_root: &Path, + domain: Option<&ReferenceDomain>, + base_generation_id: Option, + ) -> Result { + let mutation = domain.map(ReferenceMutation::begin_in_domain).transpose()?; + let startup = domain.and_then(|domain| { + crate::storage::locks::open_shared_coordination(&domain.workspace_startup_path()).ok() + }); fs::create_dir_all(runs_root)?; let run_id = managed_generation_id(); let root = runs_root.join(format!("run-{run_id}")); - fs::create_dir(&root)?; - let lease = open_locked(root.join("lease.lock"), LockMode::Exclusive)?; + let fresh = crate::storage::layout::create_fresh_reference_root(&root)? + .ok_or_else(|| NativeError::InvalidInput("run root already exists".into()))?; + let anchor = crate::storage::locks::RootedDirectory::open(&root, fresh.identity())?; + let lease = crate::storage::locks::create_new_coordination_in( + &anchor, + b"lease.lock", + &root.join("lease.lock"), + )?; + let workspace_registration = match (domain, mutation.as_ref(), startup.as_ref()) { + (Some(domain), Some(mutation), Some(_)) => { + crate::storage::maintenance_controls::WorkspaceRegistration::create( + &fresh, domain, mutation, &lease, 1, + ) + .ok() + .flatten() + } + _ => None, + }; let this = Self { root, run_id, - lease: Some(lease), + lease: Some(std::sync::Arc::new(lease)), finished: false, cleanup_barrier: None, + _reference_mutation: mutation, + workspace_registration: workspace_registration.map(std::sync::Arc::new), }; + if let Some(domain) = domain { + this.write_metadata( + &this.root.join(".reference-scope.json"), + &RunReferenceScope { + version: 1, + run: crate::storage::locks::path_identity(&this.root)?, + domain: domain.clone(), + }, + )?; + } this.write_journal(RunJournal { run_id: this.run_id.clone(), phase: RunPhase::Created, @@ -79,6 +167,13 @@ impl RunWorkspace { Ok(this) } + #[cfg(test)] + pub(crate) fn abandon_for_test(mut self) -> PathBuf { + self.finished = true; + drop(self.lease.take()); + self.root.clone() + } + pub(crate) fn set_cleanup_barrier(&mut self, root: PathBuf) -> Result<(), NativeError> { // Keep the in-memory guard even if persistence fails: Drop must retain // this run while its unknown phase can still own candidate files. @@ -92,7 +187,11 @@ impl RunWorkspace { std::env::current_dir()?.join(root) }; self.cleanup_barrier = Some(root.clone()); - write_json_atomically(&self.root.join("cleanup-barrier.json"), &root) + self.write_metadata(&self.root.join("cleanup-barrier.json"), &root) + } + + pub(crate) fn reference_domain(root: &Path) -> Result, NativeError> { + read_reference_scope(root) } fn ensure_cleanup_allowed(&self) -> Result<(), NativeError> { @@ -104,6 +203,13 @@ impl RunWorkspace { Ok(()) } + pub(crate) fn phase_owner_scope(&self) -> Option { + Some(ParentRunScope { + owner: std::sync::Arc::clone(self.lease.as_ref()?), + registration: std::sync::Arc::clone(self.workspace_registration.as_ref()?), + }) + } + pub(crate) fn root(&self) -> &Path { &self.root } @@ -160,6 +266,14 @@ impl RunWorkspace { self.write_journal(journal) } + /// Retain exact publication evidence after an ambiguous transaction result. + /// This changes only destructor state; ordinary guard release still runs. + pub(crate) fn retain_for_recovery(mut self) { + self.finished = true; + drop(self.lease.take()); + drop(self._reference_mutation.take()); + } + pub(crate) fn finish(mut self) -> Result<(), NativeError> { self.finish_inner(None) } @@ -168,6 +282,20 @@ impl RunWorkspace { self.finish_inner(error) } + /// Publish the actual owner-terminal event and retain its registered root + /// for bounded collection. No path/receipt supplied by a caller is adopted. + #[cfg(test)] + pub(crate) fn retire_for_maintenance(mut self) -> Result { + if self.workspace_registration.is_none() { + return Err(NativeError::InvalidInput( + "workspace ownership is unverified".into(), + )); + } + self.finish_inner(None)?; + Ok(self.root.clone()) + } + + #[cfg(test)] pub(crate) fn cleanup_orphans( runs_root: impl AsRef, ) -> Result { @@ -204,6 +332,20 @@ impl RunWorkspace { report.skipped_locked += 1; continue; } + let domain = match read_reference_scope(&path) { + Ok(Some(domain)) => domain, + _ => { + report.skipped_locked += 1; + continue; + } + }; + let _mutation = match ReferenceMutation::begin_in_domain(&domain) { + Ok(mutation) => mutation, + Err(_) => { + report.skipped_locked += 1; + continue; + } + }; let journal = read_orphan_journal(&path, name)?; if journal.phase == RunPhase::Publishing { report.publishing_recovered += 1; @@ -216,6 +358,17 @@ impl RunWorkspace { } fn finish_inner(&mut self, primary_error: Option) -> Result<(), NativeError> { + if self + .lease + .as_ref() + .is_some_and(|lease| std::sync::Arc::strong_count(lease) != 1) + { + self.finished = true; + drop(self.lease.take()); + return Err(NativeError::InvalidInput( + "workspace still has live phase ownership".into(), + )); + } if let Err(error) = self.ensure_cleanup_allowed() { self.finished = true; let _ = self.mark_cleanup_pending(primary_error, &error); @@ -225,8 +378,38 @@ impl RunWorkspace { if let Some(error) = primary_error.clone() { self.mark_phase(RunPhase::Failed, None, Some(error))?; } + if let (Some(registration), Some(owner), Some(mutation)) = ( + &self.workspace_registration, + self.lease.as_deref(), + self._reference_mutation.as_ref(), + ) { + let journal = self.journal()?; + let disposition = if journal.phase == RunPhase::Published { + crate::storage::maintenance_controls::WorkspaceDisposition::PublishedConsumed + } else { + crate::storage::maintenance_controls::WorkspaceDisposition::AbortedNotPublished + }; + if journal.phase == RunPhase::Publishing { + self.finished = true; + drop(self.lease.take()); + return Err(NativeError::InvalidInput( + "workspace publication requires recovery".into(), + )); + } + if let Err(error) = registration.terminal(owner, mutation, disposition) { + self.finished = true; + let _ = self.mark_cleanup_pending(primary_error, &error); + drop(self.lease.take()); + return Err(error); + } + } self.finished = true; drop(self.lease.take()); + if self.workspace_registration.is_some() { + // Automatic disposal belongs to the bounded terminal-workspace + // lane; producer completion only publishes closure and releases. + return Ok(()); + } if let Err(cleanup_error) = remove_run_tree(&self.root) { self.mark_cleanup_pending(primary_error, &cleanup_error)?; return Err(cleanup_error); @@ -247,7 +430,35 @@ impl RunWorkspace { } fn write_journal(&self, journal: RunJournal) -> Result<(), NativeError> { - write_json_atomically(&self.journal_path(), &journal) + if let (Some(registration), Some(owner), Some(mutation)) = ( + &self.workspace_registration, + self.lease.as_deref(), + self._reference_mutation.as_ref(), + ) { + let _ = registration.source_change( + owner, + mutation, + journal.phase == RunPhase::Publishing, + journal.phase == RunPhase::Published, + ); + } + self.write_metadata(&self.journal_path(), &journal) + } + fn write_metadata(&self, path: &Path, value: &impl Serialize) -> Result<(), NativeError> { + if let (Some(registration), Some(owner), Some(mutation)) = ( + &self.workspace_registration, + self.lease.as_deref(), + self._reference_mutation.as_ref(), + ) { + let publication = crate::process_lifecycle::write_private_json_recorded(path, value) + .map_err(NativeError::InvalidInput)?; + if let Some(publication) = publication { + let _ = registration.record_metadata(owner, mutation, &publication); + } + crate::storage::atomic::sync_dir(&self.root) + } else { + write_json_atomically(path, value) + } } } @@ -256,19 +467,52 @@ impl Drop for RunWorkspace { if self.finished { return; } - let _ = self.mark_phase( - RunPhase::CleanupPending, - None, - Some("workspace dropped without finish or abort".to_string()), - ); - drop(self.lease.take()); - if self.ensure_cleanup_allowed().is_ok() { - let _ = remove_run_tree(&self.root); - } + // Registered roots must pass the same startup/output/containment + // closure as explicit finish. Failed closure leaves the root intact. + let _ = self.finish_inner(Some("workspace dropped without finish or abort".into())); } } -fn ensure_persisted_cleanup_allowed(root: &Path) -> Result<(), NativeError> { +#[derive(Debug, Serialize, Deserialize)] +struct RunReferenceScope { + version: u32, + run: crate::storage::locks::FileIdentity, + domain: ReferenceDomain, +} +fn read_reference_scope(root: &Path) -> Result, NativeError> { + let path = root.join(".reference-scope.json"); + let metadata = match fs::symlink_metadata(&path) { + Ok(metadata) => metadata, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(error.into()), + }; + if !metadata.is_file() || metadata.file_type().is_symlink() || metadata.len() > 16384 { + return Ok(None); + } + let file = fs::File::open(&path)?; + if crate::storage::locks::file_link_count(&file)? != 1 { + return Ok(None); + } + let identity = crate::storage::locks::path_identity(&path)?; + if identity != crate::storage::locks::file_identity(&file)? { + return Ok(None); + } + let scope: RunReferenceScope = serde_json::from_reader(file)?; + if scope.version != 1 + || scope.run != crate::storage::locks::path_identity(root)? + || !scope.domain.revalidate()? + || !scope.domain.allows_runs_root( + root.parent() + .ok_or_else(|| NativeError::InvalidInput("run parent missing".into()))?, + )? + || identity != crate::storage::locks::path_identity(&path)? + { + return Ok(None); + } + Ok(Some(scope.domain)) +} + +pub(super) fn ensure_persisted_cleanup_allowed(root: &Path) -> Result<(), NativeError> { let bytes = match fs::read(root.join("cleanup-barrier.json")) { Ok(bytes) => bytes, Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()), @@ -287,6 +531,7 @@ fn ensure_persisted_cleanup_allowed(root: &Path) -> Result<(), NativeError> { crate::storage::containment::ensure_registry_empty(&barrier).map_err(NativeError::InvalidInput) } +#[cfg(test)] fn read_orphan_journal(path: &Path, name: &str) -> Result { let journal_path = path.join("journal.json"); if !journal_path.exists() { @@ -492,3 +737,42 @@ mod tests { fs::remove_dir_all(root).unwrap(); } } + +#[cfg(test)] +mod retain_recovery_tests { + use super::*; + #[test] + fn retain_for_recovery_preserves_journal_and_payload_but_releases_actual_lease() { + let root = std::env::temp_dir().join(format!( + "run-retain-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + let workspace = RunWorkspace::create(&root, None).unwrap(); + workspace + .mark_phase(RunPhase::Publishing, Some("published-test".into()), None) + .unwrap(); + fs::write( + workspace.staging_root().join("evidence"), + b"owned recovery evidence", + ) + .unwrap(); + let path = workspace.root().to_path_buf(); + let before = fs::read(workspace.journal_path()).unwrap(); + workspace.retain_for_recovery(); + assert_eq!(fs::read(path.join("journal.json")).unwrap(), before); + assert_eq!( + fs::read(path.join("staging/evidence")).unwrap(), + b"owned recovery evidence" + ); + assert!( + try_open_locked_in_existing_parent(path.join("lease.lock"), LockMode::Exclusive) + .unwrap() + .is_some() + ); + fs::remove_dir_all(root).unwrap(); + } +} diff --git a/src/storage/test_harness.rs b/src/storage/test_harness.rs index 68e2344a..ef21ce42 100644 --- a/src/storage/test_harness.rs +++ b/src/storage/test_harness.rs @@ -1,9 +1,59 @@ #[path = "../error.rs"] #[allow(dead_code)] mod error; -#[path = "mod.rs"] +#[path = "../process_lifecycle/mod.rs"] +#[allow(dead_code)] +mod process_lifecycle; +#[path = "../db_writer/rss.rs"] +#[allow(dead_code)] +mod process_rss; +mod db_writer { + pub(crate) use crate::process_rss::sample_process_rss; +} +// This deliberately small protocol-only crate exercises storage foundations. +// Higher-layer maintenance tests run in the full library with ArtifactStore. +#[path = "."] #[allow(dead_code, unused_imports)] -mod storage; +mod storage { + #[path = "atomic.rs"] + pub(crate) mod atomic; + #[path = "containment.rs"] + pub(crate) mod containment; + #[path = "direct.rs"] + pub(crate) mod direct; + #[path = "inventory.rs"] + pub(crate) mod inventory; + #[path = "layout.rs"] + pub(crate) mod layout; + #[path = "locks.rs"] + pub(crate) mod locks; + #[path = "maintenance_admission.rs"] + pub(crate) mod maintenance_admission; + #[path = "maintenance_budget.rs"] + pub(crate) mod maintenance_budget; + #[path = "maintenance_controls.rs"] + pub(crate) mod maintenance_controls; + #[path = "maintenance_generation_gate.rs"] + pub(crate) mod maintenance_generation_gate; + #[path = "maintenance_json.rs"] + pub(crate) mod maintenance_json; + #[path = "maintenance_legacy_guard.rs"] + pub(crate) mod maintenance_legacy_guard; + #[path = "maintenance_origin.rs"] + pub(crate) mod maintenance_origin; + #[path = "maintenance_ownership.rs"] + pub(crate) mod maintenance_ownership; + #[path = "maintenance_projection.rs"] + pub(crate) mod maintenance_projection; + #[path = "maintenance_retirement.rs"] + pub(crate) mod maintenance_retirement; + #[path = "maintenance_workspace_gate.rs"] + pub(crate) mod maintenance_workspace_gate; + #[path = "managed.rs"] + pub(crate) mod managed; + #[path = "run_workspace.rs"] + pub(crate) mod run_workspace; +} mod protocol { #[derive(Debug, Clone, Default)] @@ -31,6 +81,23 @@ use storage::locks::{ use storage::managed::{ActiveGeneration, ManagedStore, ManagedWriteSession}; use storage::run_workspace::{RunJournal, RunPhase, RunWorkspace}; +fn scoped_run_fixture( + runs_root: impl AsRef, + base: Option, +) -> RunWorkspace { + let runs_root = runs_root.as_ref(); + let runs_root = if runs_root.is_absolute() { + runs_root.to_owned() + } else { + std::env::current_dir().unwrap().join(runs_root) + }; + let layout = ManagedLayout::new(runs_root.parent().unwrap()); + layout.ensure_roots().unwrap(); + drop(storage::locks::open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap()); + let domain = storage::maintenance_ownership::ReferenceDomain::managed(&layout).unwrap(); + RunWorkspace::create_scoped(runs_root, &domain, base).unwrap() +} + static TEST_SEQUENCE: AtomicU64 = AtomicU64::new(0); #[test] @@ -407,6 +474,12 @@ fn managed_last_reader_drop_triggers_best_effort_retirement_cleanup() { drop(reader); + // Ordinary reader destruction releases ownership only. The bounded + // controller owns automatic retirement; this historical compatibility + // fixture invokes the explicit strict adapter to verify the old sweep. + assert!(store.layout().generation(&first).unwrap().root().exists()); + let cleanup = store.cleanup().unwrap(); + assert!(cleanup.retired_generations_deleted > 0); assert!(!store.layout().generation(&first).unwrap().root().exists()); } @@ -452,6 +525,177 @@ fn managed_metadata_sizes_include_db_sidecars_and_allocated_bytes() { session.abort(Some("done".to_string())).unwrap(); } +fn committed_direct_read_registration_case(corrupt: bool, read_only_parent: bool) { + use storage::inventory::{default_direct, MaintenanceTarget}; + use storage::locks::{open_locked, path_identity}; + use storage::maintenance_ownership::{ + claim_maintenance, ArtifactUseLease, DatabaseUseLease, MaintenanceAdmission, + }; + let root = fs::canonicalize(temp_dir("direct-read-registration")).unwrap(); + let state = root.join(".codebaseGraph"); + fs::create_dir(&state).unwrap(); + fs::write(state.join("config.json"), br#"{"schema_version":2}"#).unwrap(); + let mut target = MaintenanceTarget { + repository_root: root.clone(), + state_root: state.clone(), + storage_root: None, + direct_pair: None, + config_path: None, + }; + let pair = default_direct(&target); + let layout = DirectLayout::new(&pair.database, &pair.manifest); + target.direct_pair = Some(pair); + fs::write(layout.db_path(), b"committed graph").unwrap(); + fs::write(layout.manifest_path(), b"{}").unwrap(); + drop(open_locked(layout.writer_lock_path(), LockMode::Exclusive).unwrap()); + for resource in [layout.db_path(), layout.manifest_path()] { + drop(DatabaseUseLease::acquire_shared(resource).unwrap()); + } + // A modern artifact root alone still cannot prove destination-wide + // migration. Reading must not create that missing proof or registration. + drop(ArtifactUseLease::acquire_shared(&layout.artifact_root_path()).unwrap()); + let registration = layout.registration_path(); + let expected_registration = if corrupt { + fs::write(®istration, b"interrupted registration").unwrap(); + Some(( + path_identity(®istration).unwrap(), + fs::read(®istration).unwrap(), + )) + } else { + None + }; + let database_identity = path_identity(layout.db_path()).unwrap(); + let manifest_identity = path_identity(layout.manifest_path()).unwrap(); + let permissions = fs::metadata(&state).unwrap().permissions(); + #[cfg(unix)] + if read_only_parent { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&state, fs::Permissions::from_mode(0o555)).unwrap(); + } + #[cfg(not(unix))] + assert!(!read_only_parent, "Unix directory permission fixture only"); + let store = DirectStore::new(layout.clone()).unwrap(); + let reader = store.begin_read(); + // Restore fixture permissions even if admission unexpectedly fails. + fs::set_permissions(&state, permissions).unwrap(); + let reader = reader.expect("committed read must not produce/decode registration"); + assert!( + try_open_locked(layout.writer_lock_path(), LockMode::Exclusive) + .unwrap() + .is_none() + ); + for resource in [layout.db_path(), layout.manifest_path()] { + assert!(DatabaseUseLease::try_exclusive(resource).unwrap().is_none()); + } + drop(reader); + assert_eq!(path_identity(layout.db_path()).unwrap(), database_identity); + assert_eq!( + path_identity(layout.manifest_path()).unwrap(), + manifest_identity + ); + assert_eq!(fs::read(layout.db_path()).unwrap(), b"committed graph"); + assert_eq!(fs::read(layout.manifest_path()).unwrap(), b"{}"); + if let Some((identity, bytes)) = &expected_registration { + assert_eq!(path_identity(®istration).unwrap(), *identity); + assert_eq!(fs::read(®istration).unwrap(), *bytes); + assert!( + store.begin_write().is_err(), + "producer registration remains strict" + ); + fs::write(layout.journal_path(), b"required replay remains strict").unwrap(); + let journal_identity = path_identity(&layout.journal_path()).unwrap(); + assert!(store.begin_read().is_err()); + assert_eq!( + path_identity(&layout.journal_path()).unwrap(), + journal_identity + ); + assert_eq!(fs::read(®istration).unwrap(), *bytes); + fs::remove_file(layout.journal_path()).unwrap(); + } else { + assert!( + !registration.exists(), + "read must leave missing evidence missing" + ); + } + match claim_maintenance(&target) { + Ok(MaintenanceAdmission::Acquired(lease)) => { + assert!( + !lease.cohort_verified().unwrap(), + "read cannot certify legacy quiescence" + ); + } + Ok(MaintenanceAdmission::Unknown(_)) | Ok(MaintenanceAdmission::Deferred(_)) => (), + Err(error) => panic!("unexpected maintenance observation error: {error}"), + } + assert_eq!(path_identity(layout.db_path()).unwrap(), database_identity); + assert_eq!( + path_identity(layout.manifest_path()).unwrap(), + manifest_identity + ); + if let Some((identity, bytes)) = expected_registration { + assert_eq!(path_identity(®istration).unwrap(), identity); + assert_eq!(fs::read(®istration).unwrap(), bytes); + } else { + assert!(!registration.exists()); + } + fs::remove_dir_all(root).unwrap(); +} + +#[test] +fn direct_committed_read_preserves_missing_registration() { + committed_direct_read_registration_case(false, false); +} + +#[test] +fn direct_committed_read_preserves_corrupt_registration() { + committed_direct_read_registration_case(true, false); +} + +#[test] +#[cfg(unix)] +fn direct_committed_read_accepts_readonly_parent_with_existing_guards() { + committed_direct_read_registration_case(false, true); +} + +#[test] +fn direct_committed_registered_pair_reads_after_publication() { + use storage::locks::path_identity; + let root = temp_dir("direct-registered-read"); + let layout = DirectLayout::new(root.join("graph.ldb"), root.join("manifest.json")); + let store = DirectStore::new(layout.clone()).unwrap(); + let mut session = store.begin_write().unwrap(); + fs::write(session.db_candidate_path(), b"published storage bundle").unwrap(); + fs::write(session.manifest_candidate_path(), b"{}").unwrap(); + session.publish().unwrap(); + session.finish(); + assert!(!layout.journal_path().exists()); + let registration = layout.registration_path(); + let registration_identity = path_identity(®istration).unwrap(); + let registration_bytes = fs::read(®istration).unwrap(); + let database_identity = path_identity(layout.db_path()).unwrap(); + let manifest_identity = path_identity(layout.manifest_path()).unwrap(); + let reader = store.begin_read().unwrap(); + assert_eq!( + fs::read(layout.db_path()).unwrap(), + b"published storage bundle" + ); + assert_eq!(fs::read(layout.manifest_path()).unwrap(), b"{}"); + assert_eq!(path_identity(layout.db_path()).unwrap(), database_identity); + assert_eq!( + path_identity(layout.manifest_path()).unwrap(), + manifest_identity + ); + assert_eq!(path_identity(®istration).unwrap(), registration_identity); + assert_eq!(fs::read(®istration).unwrap(), registration_bytes); + assert!( + try_open_locked(layout.writer_lock_path(), LockMode::Exclusive) + .unwrap() + .is_none() + ); + drop(reader); + fs::remove_dir_all(root).unwrap(); +} + #[test] fn direct_write_session_holds_writer_lock_and_drop_cleans_unpublished_candidates() { let root = @@ -769,8 +1013,11 @@ fn direct_prepared_recovery_preserves_expected_sidecars_on_validation_failure() "checksum mismatch" } }; - // A stray unjournaled candidate must not become part of the committed bundle. - fs::write(direct_sidecar(&layout.db_candidate_path(), "wal"), b"stray").unwrap(); + // A stray unjournaled candidate has no replay/deletion ownership. Keep + // its exact inode/bytes outside the committed bundle across recovery. + let stray = direct_sidecar(&layout.db_candidate_path(), "wal"); + fs::write(&stray, b"stray").unwrap(); + let stray_identity = storage::locks::path_identity(&stray).unwrap(); for _ in 0..3 { let error = store.begin_read().unwrap_err(); assert!( @@ -783,6 +1030,11 @@ fn direct_prepared_recovery_preserves_expected_sidecars_on_validation_failure() b"search.lexicon.bin" ); assert!(!direct_sidecar(layout.db_path(), "wal").exists()); + assert_eq!(fs::read(&stray).unwrap(), b"stray"); + assert_eq!( + storage::locks::path_identity(&stray).unwrap(), + stray_identity + ); assert!(layout.journal_path().exists()); assert!(!layout.manifest_path().exists()); } @@ -794,7 +1046,13 @@ fn direct_prepared_recovery_preserves_expected_sidecars_on_validation_failure() fs::write(damaged, b"search.postings.bin").unwrap(); drop(store.begin_read().unwrap()); assert!(!layout.journal_path().exists()); - assert!(!direct_sidecar(&layout.db_candidate_path(), "wal").exists()); + assert_eq!(fs::read(&stray).unwrap(), b"stray"); + assert_eq!( + storage::locks::path_identity(&stray).unwrap(), + stray_identity + ); + assert!(!direct_sidecar(layout.db_path(), "wal").exists()); + assert!(store.candidate_cleanup_pending()); fs::remove_dir_all(root).unwrap(); } } @@ -928,7 +1186,7 @@ fn managed_publish_reconciles_an_error_after_active_pointer_commit() { fn managed_cleanup_reports_and_later_removes_locked_runs() { let root = temp_dir("managed-cleanup-reports-and-later-removes-locked-runs"); let store = open_managed_store(root.join("storage")); - let workspace = RunWorkspace::create(store.layout().runs_root(), None).unwrap(); + let workspace = scoped_run_fixture(store.layout().runs_root(), None); let cleanup = store.cleanup().unwrap(); assert_eq!(cleanup.run_recovery.skipped_locked, 1); @@ -950,7 +1208,7 @@ fn run_workspace_cleanup_rejects_symlink_content() { use std::os::unix::fs::symlink; let root = temp_dir("run_workspace_cleanup_rejects_symlink_content"); - let workspace = RunWorkspace::create(root.join("runs"), None).unwrap(); + let workspace = scoped_run_fixture(root.join("runs"), None); let outside = root.join("outside.txt"); fs::write(&outside, "outside").unwrap(); symlink(&outside, workspace.root().join("bad-link")).unwrap(); @@ -991,9 +1249,10 @@ fn orphan_publishing_run( manifest: serde_json::Value, stage: OrphanPublishStage, ) -> (PathBuf, String) { - let run_id = managed_generation_id(); + let workspace = scoped_run_fixture(store.layout().runs_root(), base_generation_id.clone()); + let run_root = workspace.root().to_owned(); + let run_id = name_from_run_root(&run_root); let candidate_id = managed_generation_id(); - let run_root = store.layout().runs_root().join(format!("run-{run_id}")); let candidate_root = run_root .join("candidate") .join(format!("gen-{candidate_id}")); @@ -1013,8 +1272,6 @@ fn orphan_publishing_run( }, ) .unwrap(); - fs::write(run_root.join("lease.lock"), b"").unwrap(); - match stage { OrphanPublishStage::BeforeRename => {} OrphanPublishStage::AfterRename @@ -1057,6 +1314,7 @@ fn orphan_publishing_run( } } + assert_eq!(workspace.abandon_for_test(), run_root); (run_root, candidate_id) } @@ -1183,7 +1441,6 @@ fn prove_phase_empty(lifecycle: &std::path::Path) { code: None, signal: None, high_water_bytes: 0, - budget_reason: None, cancelled: true, containment_empty: true, supervision_error: None, @@ -1326,7 +1583,7 @@ fn persisted_run_barrier_survives_drop_and_blocks_generic_orphan_collection() { let root = temp_dir("run_phase_barrier"); let runs = root.join("runs"); let registry = root.join("phases"); - let mut run = RunWorkspace::create(&runs, None).unwrap(); + let mut run = scoped_run_fixture(&runs, None); run.set_cleanup_barrier(registry.clone()).unwrap(); let run_root = run.root().to_path_buf(); fs::write(run.candidate_root().join("owned"), b"candidate").unwrap(); @@ -1542,7 +1799,7 @@ fn relative_barrier_subprocess_fixture() { let runs = PathBuf::from(runs); match std::env::var("CBG_BARRIER_FIXTURE_MODE").unwrap().as_str() { "create" => { - let mut run = RunWorkspace::create(&runs, None).unwrap(); + let mut run = scoped_run_fixture(&runs, None); run.set_cleanup_barrier(PathBuf::from("phases")).unwrap(); let root = run.root().to_path_buf(); fs::write(run.candidate_root().join("owned"), b"candidate").unwrap(); @@ -1593,3 +1850,27 @@ fn private_lifecycle_receipt_accepts_ancestor_alias_but_rejects_foreign_or_symli write_json_atomically(&lifecycle.join("proof.json"), &descriptor).unwrap(); assert!(!probe_lifecycle(&lifecycle).unwrap()); } + +#[test] +fn final_containment_verification_requires_prior_retirement_without_writing() { + use fs2::FileExt; + let root = temp_dir("containment_final_readonly"); + let lifecycle = unknown_phase_registry(&root); + prove_phase_empty(&lifecycle); + let marker = lifecycle.join("retired"); + assert!(!marker.exists()); + assert!(storage::containment::ensure_retired_registry_empty(&root).is_err()); + assert!(!marker.exists(), "final verification created a marker"); + storage::containment::ensure_registry_empty(&root).unwrap(); + let bytes = fs::read(&marker).unwrap(); + storage::containment::ensure_retired_registry_empty(&root).unwrap(); + assert_eq!(fs::read(&marker).unwrap(), bytes); + let lease = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(lifecycle.join("lease")) + .unwrap(); + lease.try_lock_exclusive().unwrap(); + assert!(storage::containment::ensure_retired_registry_empty(&root).is_err()); + assert_eq!(fs::read(&marker).unwrap(), bytes); +} diff --git a/tests/auto_repo_root.rs b/tests/auto_repo_root.rs index 3cbcf024..067cff2f 100644 --- a/tests/auto_repo_root.rs +++ b/tests/auto_repo_root.rs @@ -5,8 +5,10 @@ use std::{ process::{Command, Output, Stdio}, }; -fn bin() -> &'static str { - env!("CARGO_BIN_EXE_codebase-graph") +fn bin() -> PathBuf { + std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(env!("CARGO_BIN_EXE_codebase-graph"))) } fn unique_temp_dir(prefix: &str) -> PathBuf { diff --git a/tests/coordinator_process.rs b/tests/coordinator_process.rs index 5116f87d..7ef73a17 100644 --- a/tests/coordinator_process.rs +++ b/tests/coordinator_process.rs @@ -8,6 +8,12 @@ use std::time::{Duration, Instant}; static TEST_SEQUENCE: AtomicU64 = AtomicU64::new(0); +fn binary() -> PathBuf { + std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(env!("CARGO_BIN_EXE_codebase-graph"))) +} + #[test] fn twenty_mcp_clients_share_one_coordinator_worker_and_take_over() { let repo = temp_repo(); @@ -22,7 +28,7 @@ fn twenty_mcp_clients_share_one_coordinator_worker_and_take_over() { fs::create_dir_all(&state).unwrap(); // Keep the periodic reconciliation fallback inside the generation-change // deadline when auto backend selection falls back to polling. - write_config_with_policy_and_interval(&repo, &storage, "leader", 768, 384, 32, Some(1_000)); + write_config_with_policy_and_interval(&repo, &storage, "leader", 32, Some(1_000)); let mut clients = ClientGroup::default(); for index in 0..20 { @@ -48,6 +54,8 @@ fn twenty_mcp_clients_share_one_coordinator_worker_and_take_over() { let mut owner_min_rss = u64::MAX; let mut owner_max_rss = 0_u64; let mut worker_peak_rss = 0_u64; + // Pin metadata during quiescence checks. + let mut artifact_observation = None; while !active_path.exists() && Instant::now() < deadline { maximum_workers = maximum_workers.max(directory_count(&workers_root)); if let Ok(rss) = sample_process_rss(first_pid) { @@ -59,6 +67,13 @@ fn twenty_mcp_clients_share_one_coordinator_worker_and_take_over() { worker_peak_rss = worker_peak_rss.max(rss); } } + if artifact_observation.is_none() { + if let Ok(file) = fs::File::open(storage.join("artifacts/.use.lock")) { + if fs2::FileExt::try_lock_shared(&file).is_ok() { + artifact_observation = Some(file); + } + } + } std::thread::sleep(Duration::from_millis(25)); } maximum_workers = maximum_workers.max(directory_count(&workers_root)); @@ -68,6 +83,10 @@ fn twenty_mcp_clients_share_one_coordinator_worker_and_take_over() { ); assert_eq!(maximum_workers, 1, "did not observe exactly one worker"); assert!(worker_peak_rss > 0, "did not sample the isolated worker"); + assert!( + artifact_observation.is_some(), + "artifact observation lease was not acquired" + ); assert!( worker_peak_rss <= 768 * 1024 * 1024, "worker RSS exceeded 768 MiB: {worker_peak_rss}" @@ -186,45 +205,89 @@ fn twenty_mcp_clients_share_one_coordinator_worker_and_take_over() { .iter() .any(|client| client.pid() == replacement_pid)); + // Exclude new births before deliberately killing the remaining owners. + // A best-effort snapshot after kill could omit a still-closing Windows + // directory and later misclassify its retained owner-loss evidence as new. + let snapshot_deadline = Instant::now() + Duration::from_secs(30); + let (_snapshot_worker, _snapshot_writer) = loop { + let locks = (|| { + let worker = observation_lock(&storage.join("worker.lock"))?; + let writer = observation_lock(&storage.join("writer.lock"))?; + Ok::<_, String>((worker, writer)) + })(); + match locks { + Ok(locks) => break locks, + Err(reason) => { + assert!( + Instant::now() < snapshot_deadline, + "preexisting-root snapshot stayed busy: {reason}" + ); + std::thread::sleep(Duration::from_millis(25)); + } + } + }; clients.kill_all(); let active_before = active_generation_id(&active_path); - write_config(&repo, &storage, 129, 1, 1); + // Preserve the exact preexisting incarnations from deliberate owner death; + // that process could not publish a terminal event after being killed. + let mut preexisting = std::collections::BTreeMap::new(); + for category in ["runs", "workers"] { + for entry in fs::read_dir(storage.join(category)).unwrap() { + let path = entry.unwrap().path(); + let identity = physical_directory_identity(&path).unwrap_or_else(|error| { + panic!("cannot capture preexisting {}: {error}", path.display()) + }); + preexisting.insert(path, identity); + } + } + drop(_snapshot_writer); + drop(_snapshot_worker); + write_config(&repo, &storage, 1); fs::write( repo.join("src/lib.rs"), - format!( - "/*{}*/\npub fn coordinated_graph() -> bool {{ false }}\n", - "x".repeat(2 * 1024 * 1024) - ), + "pub fn coordinated_graph_without_resource_ceiling() -> bool { false }\n", ) .unwrap(); clients.push(McpClient::start(&repo, 20)); - let deadline = Instant::now() + Duration::from_secs(30); - let mut last_error = String::new(); - while Instant::now() < deadline { + let updated = + wait_for_active_generation_change(&active_path, &active_before, Duration::from_secs(90)); + assert_ne!(updated, active_before); + let active_before = updated; + // Observe a real quiescent owner after the replacement build, + // then verify retained registered roots are terminal rather than live; + // their directory count is no longer synchronous disposal evidence. + let deadline = Instant::now() + Duration::from_secs(10); + loop { let health = clients.clients[20].call( - 20_000, + 20_001, "tools/call", json!({ - "name": "graph_health", - "arguments": {"include_structured_content": true} + "name": "graph_health", "arguments": {"include_structured_content": true} }), ); - last_error = health["result"]["structuredContent"]["refresh"]["last_error"] - .as_str() - .unwrap_or("") - .to_string(); - if last_error.contains("memory_budget_exceeded") { - break; + match registered_work_is_terminal_at_quiescence(&storage, &preexisting) { + Ok(retained_owner_loss) => { + if !retained_owner_loss.is_empty() { + assert_eq!( + health["result"]["structuredContent"]["maintenance"]["pending"], true, + "retained owner-loss evidence was reported idle" + ); + for path in retained_owner_loss { + assert_eq!( + physical_directory_identity(&path).unwrap(), + preexisting[&path] + ); + } + } + break; + } + Err(reason) => assert!( + Instant::now() < deadline, + "failed refresh did not reach closed owner state: {reason}; health={health}" + ), } - std::thread::sleep(Duration::from_millis(50)); } - assert!( - last_error.contains("memory_budget_exceeded"), - "expected a structured memory failure, got {last_error:?}" - ); assert_eq!(active_generation_id(&active_path), active_before); - assert_eq!(directory_count(&storage.join("runs")), 0); - assert_eq!(directory_count(&workers_root), 0); } #[test] @@ -233,8 +296,8 @@ fn http_server_plus_coordinator_stays_below_idle_rss_gate() { let state = repo.join(".codebaseGraph"); let storage = state.join("storage"); fs::create_dir_all(&state).unwrap(); - write_config_with_policy(&repo, &storage, "off", 768, 384, 32); - let mut child = Command::new(env!("CARGO_BIN_EXE_codebase-graph")) + write_config_with_policy(&repo, &storage, "off", 32); + let mut child = Command::new(binary()) .arg("mcp") .arg("http") .arg("--repo-root") @@ -306,7 +369,7 @@ struct McpClient { impl McpClient { fn start(repo: &Path, index: usize) -> Self { - let mut child = Command::new(env!("CARGO_BIN_EXE_codebase-graph")) + let mut child = Command::new(binary()) .arg("mcp") .arg("start") .arg("--repo-root") @@ -392,6 +455,181 @@ fn wait_for_coordinator( } } +fn physical_directory_identity(path: &Path) -> Result<(u64, u64), String> { + let metadata = fs::symlink_metadata(path).map_err(|error| error.to_string())?; + if !metadata.is_dir() || metadata.file_type().is_symlink() { + return Err("workspace is not a real directory".into()); + } + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt; + Ok((metadata.dev(), metadata.ino())) + } + #[cfg(windows)] + { + use std::os::windows::{fs::OpenOptionsExt, io::AsRawHandle}; + use windows_sys::Win32::Storage::FileSystem::{ + GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION, FILE_ATTRIBUTE_DIRECTORY, + FILE_ATTRIBUTE_REPARSE_POINT, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, + FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, + }; + let file = fs::OpenOptions::new() + .access_mode(FILE_READ_ATTRIBUTES) + .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) + .custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT) + .open(path) + .map_err(|error| error.to_string())?; + let mut information: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() }; + if unsafe { GetFileInformationByHandle(file.as_raw_handle() as _, &mut information) } == 0 { + return Err(std::io::Error::last_os_error().to_string()); + } + if information.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == 0 + || information.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 + { + return Err("workspace is not a real directory".into()); + } + Ok(( + u64::from(information.dwVolumeSerialNumber), + (u64::from(information.nFileIndexHigh) << 32) | u64::from(information.nFileIndexLow), + )) + } + #[cfg(not(any(unix, windows)))] + { + Err("physical workspace identity unavailable".into()) + } +} + +/// Observation only: real worker/writer/owner leases exclude concurrent births +/// and mutations while test assertions inspect committed terminal control facts. +/// These bytes are never used to authorize cleanup or adopt unknown payloads. +fn observation_lock(path: &Path) -> Result { + let file = fs::OpenOptions::new() + .read(true) + .write(true) + .open(path) + .map_err(|error| format!("{}: {error}", path.display()))?; + fs2::FileExt::try_lock_exclusive(&file) + .map_err(|error| format!("busy {}: {error}", path.display()))?; + Ok(file) +} + +fn registered_work_is_terminal_at_quiescence( + storage: &Path, + preexisting: &std::collections::BTreeMap, +) -> Result, String> { + use sha2::{Digest, Sha256}; + let lock = observation_lock; + let _worker = lock(&storage.join("worker.lock"))?; + let _writer = lock(&storage.join("writer.lock"))?; + if active_worker_pid(&storage.join("worker.json")).is_some() { + return Err("actual worker execution state remains live".into()); + } + let number = |bytes: &[u8]| u64::from_le_bytes(bytes.try_into().unwrap()); + let mut retained_owner_loss = Vec::new(); + for category in ["runs", "workers"] { + let entries = fs::read_dir(storage.join(category)).map_err(|error| error.to_string())?; + for entry in entries { + let root = entry.map_err(|error| error.to_string())?.path(); + let _owner = lock(&root.join("lease.lock"))?; + let read = |name: &str| fs::read(root.join(name)).map_err(|error| error.to_string()); + let scope = read(".workspace.binding")?; + if scope.len() != 512 + || &scope[..8] != b"CGWSCP01" + || Sha256::digest(&scope[..480]).as_slice() != &scope[480..] + { + return Err(format!("unverified registration {}", root.display())); + } + let selector = read(".publication.selector")?; + if selector.len() != 2392 + || &selector[..8] != b"CGCOMM01" + || selector[8..40] != scope[128..160] + || Sha256::digest(&selector[..96]).as_slice() != &selector[96..128] + { + return Err(format!("uncommitted control {}", root.display())); + } + let intent = &selector[128..]; + if &intent[..8] == b"CGINTN01" + && intent[8..40] == scope[128..160] + && Sha256::digest(&intent[..2232]).as_slice() == &intent[2232..] + && number(&intent[48..56]) > number(&selector[40..48]) + { + return Err(format!( + "control recovery remains pending {}", + root.display() + )); + } + let active = number(&selector[48..56]); + if active > 1 { + return Err("invalid active control slot".into()); + } + let slot = read(&format!(".publication.data{active}"))?; + if slot.len() != 2136 + || &slot[..8] != b"CGSLOT01" + || slot[8..40] != scope[128..160] + || slot[40..48] != selector[40..48] + || number(&slot[48..56]) != 456 + || Sha256::digest(&slot[..2104]).as_slice() != &slot[2104..] + || slot[2104..] != selector[56..88] + { + return Err(format!("unverified terminal payload {}", root.display())); + } + let control = &slot[56..512]; + if &control[..8] == b"CGWCTL01" + && control[8] == 0 + && control[9] == 0 + && category == "workers" + && preexisting.get(&root) == Some(&physical_directory_identity(&root)?) + { + // An earlier deliberately killed owner cannot produce terminal + // authority. Its actual unlocked lease + empty-tree + // receipt prove inactivity; preserve its Building evidence. + let mut leases = Vec::new(); + for entry in fs::read_dir(&root).map_err(|error| error.to_string())? { + let path = entry.map_err(|error| error.to_string())?.path(); + if !path + .file_name() + .unwrap() + .to_string_lossy() + .starts_with(".lifecycle-") + { + continue; + } + let lease = lock(&path.join("lease"))?; + let receipt: serde_json::Value = serde_json::from_slice( + &fs::read(path.join("receipt.json")).map_err(|error| error.to_string())?, + ) + .map_err(|error| error.to_string())?; + if receipt["containment_empty"] != true { + return Err(format!( + "preexisting owner-loss containment unproven {}", + root.display() + )); + } + leases.push(lease); + } + if leases.is_empty() { + return Err("owner-loss root lacks actual empty-tree proof".into()); + } + retained_owner_loss.push(root); + continue; + } + if &control[..8] != b"CGWCTL01" + || !matches!(control[8], 4 | 5) + || !matches!(control[9], 1 | 2) + || control[40..48] != control[48..56] + || control[16..24] != control[56..64] + { + return Err(format!( + "nonterminal registered {} phase={}", + root.display(), + control[8] + )); + } + } + } + Ok(retained_owner_loss) +} + fn directory_count(path: &Path) -> usize { fs::read_dir(path) .map(|entries| { @@ -507,27 +745,18 @@ fn sample_process_rss(_pid: u32) -> std::io::Result { )) } -fn write_config(repo: &Path, storage: &Path, worker: u64, rust: u64, spill: u64) { - write_config_with_policy(repo, storage, "leader", worker, rust, spill); +fn write_config(repo: &Path, storage: &Path, spill: u64) { + write_config_with_policy(repo, storage, "leader", spill); } -fn write_config_with_policy( - repo: &Path, - storage: &Path, - policy: &str, - worker: u64, - rust: u64, - spill: u64, -) { - write_config_with_policy_and_interval(repo, storage, policy, worker, rust, spill, None); +fn write_config_with_policy(repo: &Path, storage: &Path, policy: &str, spill: u64) { + write_config_with_policy_and_interval(repo, storage, policy, spill, None); } fn write_config_with_policy_and_interval( repo: &Path, storage: &Path, policy: &str, - worker: u64, - rust: u64, spill: u64, reconcile_interval_ms: Option, ) { @@ -544,8 +773,6 @@ fn write_config_with_policy_and_interval( "refresh": refresh, "materialization": { "include_fts": true, - "worker_memory_mib": worker, - "rust_memory_mib": rust, "spill_chunk_mib": spill, "max_parallelism": 2 } @@ -563,5 +790,5 @@ fn temp_repo() -> PathBuf { sequence )); fs::create_dir_all(&path).unwrap(); - path + fs::canonicalize(path).unwrap() } diff --git a/tests/copy_phase_process.rs b/tests/copy_phase_process.rs new file mode 100644 index 00000000..f4744944 --- /dev/null +++ b/tests/copy_phase_process.rs @@ -0,0 +1,552 @@ +#![cfg(debug_assertions)] + +use codebase_graph::api::{ + CodebaseGraphApi, OperationRequest, OutputFormat, QueryRequest, RepoSelector, SearchRequest, +}; +use serde_json::{json, Value}; +use sha2::{Digest, Sha256}; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::{Command, Output}; +use std::sync::atomic::{AtomicU64, Ordering}; + +static SEQUENCE: AtomicU64 = AtomicU64::new(0); + +fn binary() -> PathBuf { + std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(env!("CARGO_BIN_EXE_codebase-graph"))) +} + +struct Fixture { + root: PathBuf, + direct: bool, +} + +impl Fixture { + fn new(direct: bool) -> Self { + let root = std::env::temp_dir().join(format!( + "copy-phase-{}-{}", + std::process::id(), + SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir(&root).unwrap(); + let root = fs::canonicalize(root).unwrap(); + fs::create_dir(root.join("src")).unwrap(); + if !direct { + fs::create_dir(root.join(".codebaseGraph")).unwrap(); + fs::write( + root.join(".codebaseGraph/config.json"), + serde_json::to_vec(&json!({"schema_version":2,"repo_root":root})).unwrap(), + ) + .unwrap(); + } else { + fs::create_dir(root.join("output")).unwrap(); + } + Self { root, direct } + } + + fn selector(&self) -> RepoSelector { + RepoSelector { + repo_root: Some(self.root.clone()), + db_path: self.direct.then(|| self.root.join("output/graph.ldb")), + manifest_path: self.direct.then(|| self.root.join("output/manifest.json")), + ..RepoSelector::default() + } + } + + fn source(&self, label: &str) { + fs::write( + self.root.join("src/lib.rs"), + format!("pub fn {label}() {{}}\n"), + ) + .unwrap(); + } + + fn build(&self, trace: &Path, spill_mib: u64) -> Output { + let mut command = Command::new(binary()); + command + .current_dir(&self.root) + .args(["build", "--repo-root"]) + .arg(&self.root) + .args([ + "--mode", + "full", + "--no-git", + "--single-thread", + "--spill-chunk-mib", + ]) + .arg(spill_mib.to_string()) + .arg("--json") + .env("CODEBASE_GRAPH_TEST_COPY_RESULT_DIR", trace); + if self.direct { + command + .arg("--db") + .arg(self.root.join("output/graph.ldb")) + .arg("--manifest") + .arg(self.root.join("output/manifest.json")); + } + command.output().unwrap() + } + + fn paths(&self) -> (PathBuf, PathBuf) { + if self.direct { + return ( + self.root.join("output/graph.ldb"), + self.root.join("output/manifest.json"), + ); + } + let pointer: Value = serde_json::from_slice( + &fs::read(self.root.join(".codebaseGraph/storage/active.json")).unwrap(), + ) + .unwrap(); + let generation = self + .root + .join(".codebaseGraph/storage/generations") + .join(format!( + "gen-{}", + pointer["generation_id"].as_str().unwrap() + )); + ( + generation.join("graph.ldb"), + generation.join("manifest.json"), + ) + } + + fn query(&self, statement: &str) -> Vec> { + let result = CodebaseGraphApi::new() + .execute_operation(&OperationRequest::Query(QueryRequest { + repo: self.selector(), + statement: statement.into(), + parameters: json!({}), + limit: 1000, + output_format: OutputFormat::Typed, + })) + .unwrap(); + serde_json::from_value(result.payload["rows"].clone()).unwrap() + } + + fn search(&self, label: &str) -> Vec { + let result = CodebaseGraphApi::new() + .execute_operation(&OperationRequest::Search(SearchRequest { + repo: self.selector(), + query: label.into(), + layer: "semantic".into(), + profile: "brief".into(), + limit: 10, + budget: 0, + context_limit: 0, + max_depth: None, + detail: "slim".into(), + output_format: OutputFormat::Typed, + })) + .unwrap(); + result.payload["results"] + .as_array() + .unwrap() + .iter() + .filter_map(|row| row["label"].as_str().map(str::to_owned)) + .collect() + } +} + +fn assert_success(output: &Output) { + assert!( + output.status.success(), + "CLI failed: {}\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); +} + +fn traces(directory: &Path) -> Vec { + // The writer waits for each child; successful phase records append in execution order. + fs::read_to_string(directory.join("phases.jsonl")) + .unwrap() + .lines() + .map(|line| serde_json::from_str(line).unwrap()) + .collect() +} + +fn bundle_hashes(db: &Path, manifest: &Path) -> BTreeMap { + let mut paths = vec![db.to_path_buf(), manifest.to_path_buf()]; + let value: Value = serde_json::from_slice(&fs::read(manifest).unwrap()).unwrap(); + if let Some(files) = value["search_backend"]["files"].as_object() { + paths.extend( + files + .keys() + .map(|suffix| PathBuf::from(format!("{}.{}", db.display(), suffix))), + ); + } + paths + .into_iter() + .map(|path| { + let digest = format!("{:x}", Sha256::digest(fs::read(&path).unwrap())); + (path, digest) + }) + .collect() +} + +#[test] +fn native_copy_acknowledgement_failure_preserves_managed_and_direct_bundles() { + for direct in [false, true] { + let fixture = Fixture::new(direct); + fixture.source("stable_marker"); + assert_success(&fixture.build(&fixture.root.join("initial-trace"), 32)); + let (db, manifest) = fixture.paths(); + let before = bundle_hashes(&db, &manifest); + assert!(fixture + .search("stable_marker") + .contains(&"stable_marker".to_owned())); + + fixture.source("failed_marker"); + let failed_trace = fixture.root.join("failed-trace"); + fs::create_dir(&failed_trace).unwrap(); + fs::write( + failed_trace.join("FAIL_AFTER_COPY"), + b"fault only after real COPY and checkpoint", + ) + .unwrap(); + let failed = fixture.build(&failed_trace, 32); + assert!(!failed.status.success()); + let diagnostic = format!( + "{}{}", + String::from_utf8_lossy(&failed.stdout), + String::from_utf8_lossy(&failed.stderr) + ); + assert!( + diagnostic.contains("COPY result acknowledgement failed"), + "{diagnostic}" + ); + assert!( + !diagnostic.contains("non-empty primary-key node table"), + "first error was replaced: {diagnostic}" + ); + let events = traces(&failed_trace); + assert_eq!( + events + .iter() + .map(|event| event["phase_kind"].as_str().unwrap()) + .collect::>(), + ["schema", "copy"] + ); + assert!(events.iter().all(|event| event["success"] == true)); + assert_eq!(events[1]["copy_index"], 0); + assert!(events[1].get("buffer_pool_bytes").is_none()); + assert!(events[1].get("worker_memory_bytes").is_none()); + assert!(events[1]["staged_file_bytes"].as_u64().unwrap() > 0); + assert_ne!( + events[0]["pid"], events[1]["pid"], + "phases must actually be isolated" + ); + assert_eq!(fixture.paths(), (db.clone(), manifest.clone())); + assert_eq!(bundle_hashes(&db, &manifest), before); + assert!(fixture + .search("stable_marker") + .contains(&"stable_marker".to_owned())); + assert!(!fixture + .search("failed_marker") + .contains(&"failed_marker".to_owned())); + + fixture.source("recovered_marker"); + let recovery_trace = fixture.root.join("recovery-trace"); + assert_success(&fixture.build(&recovery_trace, 32)); + assert!(fixture + .search("recovered_marker") + .contains(&"recovered_marker".to_owned())); + assert!(!fixture + .search("failed_marker") + .contains(&"failed_marker".to_owned())); + assert_ne!(traces(&recovery_trace)[0]["pid"], events[0]["pid"]); + fs::remove_dir_all(&fixture.root).unwrap(); + } +} + +fn string_rows(connection: &lbug::Connection<'_>, statement: &str) -> Vec> { + connection + .query(statement) + .unwrap() + .map(|row| { + row.into_iter() + .map(|value| match value { + lbug::Value::String(value) => value, + other => panic!("expected string result, got {other:?}"), + }) + .collect() + }) + .collect() +} + +#[test] +fn jumbo_staged_row_survives_real_copy_and_fresh_process_read() { + let fixture = Fixture::new(true); + let padding = "x".repeat(2 * 1024 * 1024); + fs::write( + fixture.root.join("src/lib.rs"), + format!("/*{padding}*/\npub fn jumbo_record_marker() {{}}\n"), + ) + .unwrap(); + let trace = fixture.root.join("jumbo-trace"); + assert_success(&fixture.build(&trace, 1)); + assert!(traces(&trace) + .iter() + .any(|event| event["phase_kind"] == "copy" + && event["staged_file_bytes"] + .as_u64() + .is_some_and(|bytes| bytes > 1024 * 1024))); + let (db_path, _) = fixture.paths(); + let database = lbug::Database::new( + &db_path, + lbug::SystemConfig::default() + .read_only(true) + .max_num_threads(1), + ) + .unwrap(); + let connection = lbug::Connection::new(&database).unwrap(); + let rows = string_rows(&connection, "MATCH (n:SyntaxCapture) WHERE n.tree_sitter_node_type='block_comment' RETURN CAST(n.metadata AS STRING)"); + let metadata: Value = serde_json::from_str(&rows[0][0]).unwrap(); + assert!(metadata["canonical_key"] + .as_str() + .unwrap() + .contains(&padding)); + drop(connection); + drop(database); + assert!(fixture + .search("jumbo_record_marker") + .contains(&"jumbo_record_marker".to_owned())); + fs::remove_dir_all(fixture.root).unwrap(); +} + +fn logical_snapshot(connection: &lbug::Connection<'_>) -> BTreeMap>> { + [ + ( + "node rows and JSON", + "MATCH (n) RETURN label(n), n.id, CAST(n AS STRING) ORDER BY label(n), n.id", + ), + ( + "connectors", + "MATCH (a)-[r]->(b) RETURN label(r), a.id, b.id ORDER BY label(r), a.id, b.id", + ), + ] + .into_iter() + .map(|(name, statement)| (name.to_owned(), string_rows(connection, statement))) + .collect() +} + +#[test] +fn bounded_production_chunks_reopen_with_primary_keys_connectors_and_search() { + let fixture = Fixture::new(true); + let mut body = String::new(); + for index in 0..1400 { + body.push_str(&format!( + "pub fn chunk_symbol_{index:04}() -> usize {{ {index} }}\n" + )); + } + fs::write(fixture.root.join("src/lib.rs"), body).unwrap(); + let trace = fixture.root.join("chunk-trace"); + let built = fixture.build(&trace, 1); + assert_success(&built); + let response: Value = serde_json::from_slice(&built.stdout).unwrap(); + eprintln!( + "chunked native build resources: {}", + response["phase_high_water_marks"] + ); + let events = traces(&trace); + let copies = events + .iter() + .filter(|event| event["phase_kind"] == "copy") + .collect::>(); + let mut repeated = BTreeMap::>::new(); + for (position, event) in events.iter().enumerate() { + assert_eq!(event["max_num_threads"], 1); + if event["phase_kind"] != "copy" { + continue; + } + let target = event["target"] + .as_str() + .unwrap() + .trim_matches('`') + .to_owned(); + let bytes = event["staged_file_bytes"].as_u64().unwrap(); + assert!(bytes > 0 && bytes <= 1024 * 1024, "{event}"); + assert!( + event["staged_file_path"] + .as_str() + .unwrap() + .ends_with(".json") + || event["staged_file_path"] + .as_str() + .unwrap() + .ends_with(".csv") + ); + if !target.starts_with("FROM_") && !target.starts_with("TO_") { + repeated.entry(target).or_default().push(position); + } + } + assert!( + repeated + .values() + .filter(|positions| positions.len() >= 2) + .count() + >= 2, + "production must rotate multiple node/edge-node tables" + ); + for (table, positions) in &repeated { + if positions.len() < 2 { + continue; + } + assert!( + events[positions[0] + 1..positions[1]] + .iter() + .any(|event| event["phase_kind"] == "index" && event["target"] == *table), + "no early PK index for {table}" + ); + } + let first_connector = events + .iter() + .position(|event| { + event["target"] + .as_str() + .is_some_and(|table| table.starts_with("`FROM_") || table.starts_with("`TO_")) + }) + .unwrap(); + assert!(!events[first_connector..] + .iter() + .any(|event| event["phase_kind"] == "index")); + assert_eq!( + copies + .iter() + .map(|event| event["pid"].as_u64().unwrap()) + .collect::>() + .len(), + copies.len() + ); + + let (db_path, _) = fixture.paths(); + let database = lbug::Database::new( + &db_path, + lbug::SystemConfig::default() + .read_only(true) + .max_num_threads(1), + ) + .unwrap(); + let connection = lbug::Connection::new(&database).unwrap(); + let functions = string_rows( + &connection, + "MATCH (n:Function) RETURN n.id, n.label ORDER BY n.label", + ); + assert_eq!(functions.len(), 1400); + assert_eq!( + functions + .iter() + .map(|row| row[1].clone()) + .collect::>(), + (0..1400) + .map(|index| format!("chunk_symbol_{index:04}")) + .collect() + ); + let mut lookup = connection + .prepare("MATCH (n:Function {id:$id}) RETURN n.id, n.label") + .unwrap(); + for expected in &functions { + let mut rows = connection + .execute( + &mut lookup, + vec![("id", lbug::Value::String(expected[0].clone()))], + ) + .unwrap(); + assert_eq!( + rows.next().unwrap(), + vec![ + lbug::Value::String(expected[0].clone()), + lbug::Value::String(expected[1].clone()) + ] + ); + assert!(rows.next().is_none()); + } + for (table, positions) in &repeated { + if positions.len() < 2 { + continue; + } + let ids = string_rows( + &connection, + &format!("MATCH (n:`{table}`) RETURN n.id ORDER BY n.id"), + ); + assert!(!ids.is_empty()); + assert_eq!(ids.iter().collect::>().len(), ids.len()); + let mut keyed = connection + .prepare(&format!("MATCH (n:`{table}` {{id:$id}}) RETURN n.id")) + .unwrap(); + for expected in ids { + let mut result = connection + .execute( + &mut keyed, + vec![("id", lbug::Value::String(expected[0].clone()))], + ) + .unwrap(); + assert_eq!( + result.next().unwrap(), + vec![lbug::Value::String(expected[0].clone())] + ); + assert!(result.next().is_none()); + } + } + let plan = format!( + "{:?}", + connection + .query("EXPLAIN MATCH (n:Function {id:'missing'}) RETURN n.id") + .unwrap() + ); + eprintln!("PK lookup plan: {plan}"); + assert!( + plan.contains("PRIMARY_KEY_SCAN_NODE_TABLE"), + "lookup did not use the primary-key operator: {plan}" + ); + let endpoints = string_rows(&connection, "MATCH (m:Module)-[:FROM_Defines]->(e:Defines)-[:TO_Defines]->(f:Function) RETURN m.label, f.id, f.label ORDER BY f.label"); + assert_eq!(endpoints.len(), 1400); + assert!(endpoints.iter().all(|row| row[0] == "src.lib")); + assert_eq!( + endpoints + .iter() + .map(|row| row[1..].to_vec()) + .collect::>(), + functions + ); + let chunked_snapshot = logical_snapshot(&connection); + drop(lookup); + drop(connection); + drop(database); + let labels = ["chunk_symbol_0000", "chunk_symbol_1399"]; + let chunked_search = labels + .iter() + .map(|label| fixture.search(label)) + .collect::>(); + for (label, found) in labels.iter().zip(&chunked_search) { + assert!(found.contains(&(*label).to_owned())); + } + assert_eq!( + fixture.query("MATCH (n:Function) RETURN count(n)")[0][0], + 1400 + ); + + // Same source paths and content, fresh candidate, only the existing spill setting differs. + let control_trace = fixture.root.join("control-trace"); + assert_success(&fixture.build(&control_trace, 32)); + let (control_db_path, _) = fixture.paths(); + let control_database = lbug::Database::new( + &control_db_path, + lbug::SystemConfig::default() + .read_only(true) + .max_num_threads(1), + ) + .unwrap(); + let control_connection = lbug::Connection::new(&control_database).unwrap(); + assert_eq!(logical_snapshot(&control_connection), chunked_snapshot); + drop(control_connection); + drop(control_database); + for (label, expected) in labels.iter().zip(chunked_search) { + assert_eq!(fixture.search(label), expected); + } + fs::remove_dir_all(fixture.root).unwrap(); +} diff --git a/tests/generation_storage.rs b/tests/generation_storage.rs index 9f1706f0..edd1f033 100644 --- a/tests/generation_storage.rs +++ b/tests/generation_storage.rs @@ -43,7 +43,8 @@ fn managed_materialization_activates_generations_atomically() { write_source(&repo, "pub fn version() -> &'static str { \"v3\" }\n"); let third = materialize_ok(&repo, None, None); assert_eq!(third["storage_format"], "managed_v2"); - assert_eq!(third["cleanup_pending"], json!(false)); + assert!(third["cleanup_pending"].is_boolean()); + collect_managed_until_owned_idle(&repo); assert!(!first_generation_root.exists()); let active_manifest = read_manifest( @@ -107,7 +108,8 @@ fn managed_reads_survive_deferred_retired_generation_deletion() { drop(held_file); write_source(&repo, "pub fn final_symbol() {}\n"); let published = materialize_ok(&repo, None, None); - assert_eq!(published["cleanup_pending"], false); + assert!(published["cleanup_pending"].is_boolean()); + collect_managed_until_owned_idle(&repo); assert!(!old_root.exists(), "retired generation should be collected"); } @@ -242,6 +244,7 @@ fn managed_materialization_survives_ten_updates_without_generation_leaks() { let storage_root = repo.join(".codebaseGraph").join("storage"); for index in 0..10 { + eprintln!("generation churn publishing revision {} of 10", index + 1); write_source( &repo, &format!("pub fn churned() -> usize {{ {} }}\n", index + 1), @@ -250,6 +253,8 @@ fn managed_materialization_survives_ten_updates_without_generation_leaks() { assert_eq!(payload["storage_format"], "managed_v2"); } + eprintln!("generation churn collecting after ten updates"); + collect_managed_until_owned_idle(&repo); let generations_root = storage_root.join("generations"); let generation_count = fs::read_dir(&generations_root) .expect("generation root should exist") @@ -310,6 +315,100 @@ fn managed_materialization_survives_ten_updates_without_generation_leaks() { ); } +/// Drive the shipping default-false health entry until the fixture's proven +/// owned work is physically idle. Advisory partial/unknown measurement remains +/// truthful; this never rewrites its completeness or grants deletion authority. +fn collect_managed_until_owned_idle(repo: &Path) { + use std::collections::BTreeSet; + use std::process::Command; + let storage = repo.join(".codebaseGraph/storage"); + let active = active_generation_id(&storage); + let manifest = read_manifest(&generation_root(&storage, &active).join("manifest.json")); + let expected = manifest + .files + .values() + .filter_map(|entry| entry.artifact_key.clone()) + .collect::>(); + let receipts = ["runs", "workers", "phases"] + .into_iter() + .filter_map(|name| fs::read_dir(storage.join(name)).ok()) + .flatten() + .map(|entry| { + fs::read_dir(entry.unwrap().path().join(".workspace-origins")) + .map(|entries| entries.count()) + .unwrap_or(0) + }) + .sum::(); + let attempts = 128 + + 16 * (receipts + + expected.len() + + fs::read_dir(storage.join("generations")).unwrap().count()); + let mut last = serde_json::Value::Null; + let binary = std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .unwrap_or_else(|| env!("CARGO_BIN_EXE_codebase-graph").into()); + for attempt in 0..attempts { + let output = Command::new(&binary) + .args(["check-health", "--repo-root"]) + .arg(repo) + .arg("--json") + .output() + .expect("shipping health should execute"); + assert!( + output.status.success(), + "health failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + last = serde_json::from_slice(&output.stdout).unwrap(); + assert_eq!(last["graph_readable"], true); + assert_eq!(active_generation_id(&storage), active); + let maintenance = &last["maintenance"]; + if attempt % 64 == 0 { + eprintln!( + "generation cleanup attempt {attempt}/{attempts}: {}", + maintenance["last_attempt"] + ); + } + assert!( + maintenance["errors"].as_array().unwrap().is_empty(), + "{last}" + ); + for field in ["attempted_entries", "deleted_entries"] { + if let Some(entries) = maintenance["last_attempt"][field].as_u64() { + assert!(entries <= 100, "shipping mutation grant exceeded: {last}"); + } + } + let generations = fs::read_dir(storage.join("generations")) + .unwrap() + .map(|entry| entry.unwrap().file_name().to_string_lossy().into_owned()) + .collect::>(); + let mut keys = BTreeSet::new(); + for shard in fs::read_dir(storage.join("artifacts")).unwrap() { + let shard = shard.unwrap(); + if shard.file_type().unwrap().is_dir() { + for entry in fs::read_dir(shard.path()).unwrap() { + keys.insert(entry.unwrap().file_name().to_string_lossy().into_owned()); + } + } + } + let empty = |name: &str| { + fs::read_dir(storage.join(name)) + .map(|mut entries| entries.next().is_none()) + .unwrap_or(true) + }; + if generations == BTreeSet::from([format!("gen-{active}")]) + && keys == expected + && empty("runs") + && empty("workers") + && fs::metadata(storage.join("artifacts/.publication.stream")) + .is_ok_and(|metadata| metadata.len() == 0) + { + assert_eq!(last["pending_runs"], 0); + return; + } + } + panic!("shipping bounded collection did not reach owned idle: {last}"); +} + fn materialize_ok( repo_root: &Path, db_path: Option, @@ -362,8 +461,6 @@ fn materialize_request( exclude_patterns: Vec::new(), candidate_paths: Vec::new(), parallel: false, - worker_memory_mib: None, - rust_memory_mib: None, spill_chunk_mib: None, max_parallelism: None, progress: false, @@ -480,5 +577,6 @@ fn temp_repo(name: &str) -> PathBuf { } fs::create_dir_all(&root) .unwrap_or_else(|error| panic!("failed to create temp dir {}: {error}", root.display())); - root + fs::canonicalize(&root) + .unwrap_or_else(|error| panic!("failed to resolve temp dir {}: {error}", root.display())) } diff --git a/tests/http_daemon_process.rs b/tests/http_daemon_process.rs index cf01b1b8..80024ca0 100644 --- a/tests/http_daemon_process.rs +++ b/tests/http_daemon_process.rs @@ -938,35 +938,49 @@ fn one_http_daemon_serves_multiple_sessions_and_rejects_duplicate_owner() { .any(|tool| tool["name"] == "graph_health")); } - let context = request( - port, - "POST", - "/mcp", - &[ - ( - "mcp-session-id", - first.headers.get("mcp-session-id").unwrap(), - ), - ("mcp-protocol-version", MCP_PROTOCOL_VERSION), - ], - Some(&json!({ - "jsonrpc": "2.0", - "id": 5, - "method": "tools/call", - "params": { - "name": "graph_context", - "arguments": { - "query": "helper", - "layer": "semantic", - "profile": "definitions", - "detail": "slim", - "context_limit": 1 + // Startup reconciliation/cleanup may briefly own the real execution slot. + // Retry only that documented transient response; preserve every other MCP + // error as a failure with its full diagnostics. + let context_deadline = Instant::now() + Duration::from_secs(120); + let context = loop { + let response = request( + port, + "POST", + "/mcp", + &[ + ( + "mcp-session-id", + first.headers.get("mcp-session-id").unwrap(), + ), + ("mcp-protocol-version", MCP_PROTOCOL_VERSION), + ], + Some(&json!({ + "jsonrpc": "2.0", + "id": 5, + "method": "tools/call", + "params": { + "name": "graph_context", + "arguments": { + "query": "helper", + "layer": "semantic", + "profile": "definitions", + "detail": "slim", + "context_limit": 1 + } } - } - })), - ); + })), + ); + if response.body["result"]["structuredContent"]["error"]["code"] != "graph_busy" { + break response; + } + assert!( + Instant::now() < context_deadline, + "graph_context stayed busy: {response:?}" + ); + thread::sleep(Duration::from_millis(25)); + }; assert_eq!(context.status, 200); - assert_eq!(context.body["result"]["isError"], false); + assert_eq!(context.body["result"]["isError"], false, "{context:?}"); assert!(context.body["result"]["content"][0]["text"] .as_str() .unwrap() diff --git a/tests/native_request_identity.rs b/tests/native_request_identity.rs index 2b0c008c..7b08169f 100644 --- a/tests/native_request_identity.rs +++ b/tests/native_request_identity.rs @@ -5,8 +5,10 @@ use std::{ process::{Command, Output}, }; -fn bin() -> &'static str { - env!("CARGO_BIN_EXE_codebase-graph") +fn bin() -> PathBuf { + std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(env!("CARGO_BIN_EXE_codebase-graph"))) } fn unique_temp_dir(prefix: &str) -> PathBuf { diff --git a/tests/refresh_recovery_process.rs b/tests/refresh_recovery_process.rs index 10be32de..243b6906 100644 --- a/tests/refresh_recovery_process.rs +++ b/tests/refresh_recovery_process.rs @@ -49,6 +49,10 @@ struct ManualBuild { stderr: PathBuf, } +fn publication_wait_timeout() -> Duration { + Duration::from_secs(if cfg!(windows) { 240 } else { 90 }) +} + impl ManualBuild { fn start(root: &Path) -> Self { let stdout = root.join("manual-build.stdout"); @@ -105,7 +109,9 @@ impl ManualBuild { } fn wait_for_success(&mut self) { - let deadline = Instant::now() + Duration::from_secs(90); + // This verifies successful publication, not a production latency bound. + let timeout = publication_wait_timeout(); + let deadline = Instant::now() + timeout; while Instant::now() < deadline { if let Some(status) = self.poll() { self.assert_success(status); @@ -114,7 +120,8 @@ impl ManualBuild { thread::sleep(Duration::from_millis(25)); } panic!( - "manual build did not finish within 90 seconds: {}", + "manual build did not finish within {} seconds: {}", + timeout.as_secs(), self.diagnostics() ); } @@ -336,32 +343,67 @@ fn install(root: &Path) -> PathBuf { "install failed: {}", String::from_utf8_lossy(&output.stderr) ); - let build = Command::new(binary()) - .current_dir(root) - .args([ - "build", - "--repo-root", - root.to_str().unwrap(), - "--mode", - "full", - "--json", - ]) - .output() - .unwrap(); - assert!( - build.status.success(), - "initial build failed: {}", - String::from_utf8_lossy(&build.stderr) - ); + for attempt in 0..3 { + let build = Command::new(binary()) + .current_dir(root) + .args([ + "build", + "--repo-root", + root.to_str().unwrap(), + "--mode", + "full", + "--json", + ]) + .output() + .unwrap(); + if build.status.success() { + break; + } + let error = String::from_utf8_lossy(&build.stderr); + if attempt < 2 && retryable_initial_build_failure(&error) { + eprintln!( + "retrying pre-injection fixture build after authenticated empty cleanup: {error}" + ); + thread::sleep(Duration::from_millis(250)); + continue; + } + panic!("initial build failed: {error}"); + } root.join(".codebaseGraph/config.json") } +// Setup only: never turn an injected fault or unproven containment into a retry. +fn retryable_initial_build_failure(error: &str) -> bool { + error.contains("failed to start guarded Ladybug phase") + && error.contains("Resource temporarily unavailable (os error 35)") + && error.contains("cleanup: Ok(ExitOutcome") + && error.contains("containment_empty: true") + && error.contains("supervision_error: None") + && !error.contains("containment_unknown") +} + +#[test] +fn initial_build_retry_requires_exact_transient_and_empty_cleanup() { + let error="failed to start guarded Ladybug phase: Resource temporarily unavailable (os error 35); cleanup: Ok(ExitOutcome { containment_empty: true, supervision_error: None })"; + assert!(retryable_initial_build_failure(error)); + for invalid in [ + error.replace("containment_empty: true", "containment_empty: false"), + error.replace( + "supervision_error: None", + "supervision_error: Some(unknown)", + ), + error.replace("os error 35", "os error 5"), + error.replace("cleanup: Ok", "cleanup: Err"), + format!("{error}; containment_unknown"), + ] { + assert!(!retryable_initial_build_failure(&invalid), "{invalid}"); + } +} + struct ConfigSettings<'a> { policy: &'a str, backend: &'a str, interval_ms: u64, - worker_memory_mib: u64, - rust_memory_mib: u64, spill_chunk_mib: u64, max_parallelism: usize, exclude: &'a [&'a str], @@ -374,8 +416,6 @@ fn set_config(path: &Path, settings: ConfigSettings<'_>) { "backend": settings.backend, "reconcile_interval_ms": settings.interval_ms }); - config["materialization"]["worker_memory_mib"] = json!(settings.worker_memory_mib); - config["materialization"]["rust_memory_mib"] = json!(settings.rust_memory_mib); config["materialization"]["spill_chunk_mib"] = json!(settings.spill_chunk_mib); config["materialization"]["max_parallelism"] = json!(settings.max_parallelism); config["materialization"]["exclude"] = json!(settings.exclude); @@ -485,15 +525,8 @@ fn start_http_server_with_env( panic!("timed out waiting for HTTP daemon on port {port}"); } -fn now_unix_ms() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as u64 -} - -fn memory_limits(health: &Value) -> &Value { - &health["refresh"]["memory_limits"] +fn execution_settings(health: &Value) -> &Value { + &health["refresh"]["execution_settings"] } fn reconciliation_is_ready(health: &Value) -> bool { @@ -600,8 +633,6 @@ fn legacy_worker_pid_recovery(manual_publication: bool) { policy: "leader", backend: "poll", interval_ms: 100, - worker_memory_mib: 768, - rust_memory_mib: 384, spill_chunk_mib: 32, max_parallelism: 1, exclude: &[], @@ -784,7 +815,7 @@ fn retired_internal_roles_require_restart_before_reading_requests() { } #[test] -fn blocked_refresh_recovers_after_memory_budget_restore_without_source_edit() { +fn legacy_memory_fields_do_not_block_refresh_of_larger_source() { let root = temp_repo(); let unrelated = temp_repo(); fs::write( @@ -793,10 +824,6 @@ fn blocked_refresh_recovers_after_memory_budget_restore_without_source_edit() { ) .unwrap(); let config = install(&root); - - // The source edit is made before the daemon starts. The initial graph is - // therefore the only valid generation available while the tiny budget is - // rejecting the pending startup reconciliation. fs::write( root.join("latest.py"), format!( @@ -810,110 +837,41 @@ fn blocked_refresh_recovers_after_memory_budget_restore_without_source_edit() { ConfigSettings { policy: "leader", backend: "poll", - interval_ms: 100, - worker_memory_mib: 2, - rust_memory_mib: 1, + interval_ms: 5_000, spill_chunk_mib: 1, max_parallelism: 1, exclude: &[], }, ); - + // Ordinary unknown-field deserialization must not resurrect retired workload ceilings. + let mut legacy: Value = serde_json::from_slice(&fs::read(&config).unwrap()).unwrap(); + legacy["materialization"]["worker_memory_mib"] = json!(2); + legacy["materialization"]["rust_memory_mib"] = json!(1); + fs::write(&config, serde_json::to_vec_pretty(&legacy).unwrap()).unwrap(); let (daemon, port, _state) = start_daemon(&root, &config, &unrelated, &[]); let mut session = McpSession::start(port); - let deadline = Instant::now() + Duration::from_secs(40); - let mut blocked_health = None; - while Instant::now() < deadline { + let deadline = Instant::now() + publication_wait_timeout(); + loop { let health = session.health(); - let refresh = &health["refresh"]; - if matches!( - refresh["state"].as_str(), - Some("blocked") | Some("retrying") - ) { - blocked_health = Some(health); + let paths = session.search_paths("latest_symbol"); + if paths.iter().any(|path| path == "latest.py") { + assert_eq!(health["graph_readable"], true); + assert!(execution_settings(&health) + .get("worker_memory_mib") + .is_none()); + assert!(execution_settings(&health).get("rust_memory_mib").is_none()); + assert!(!health["refresh"]["last_error"] + .as_str() + .unwrap_or("") + .contains("memory_budget_exceeded")); break; } - thread::sleep(Duration::from_millis(100)); - } - let blocked = blocked_health.expect("tiny memory budget should block or retry refresh"); - let blocked_refresh = &blocked["refresh"]; - assert_eq!(blocked["ok"], true, "graph health must remain readable"); - assert_eq!(blocked["graph_readable"], true); - assert_eq!(blocked_refresh["task_alive"], true); - assert_eq!(blocked_refresh["pending"], true); - assert!( - blocked_refresh["last_error"] - .as_str() - .is_some_and(|error| error.contains("memory_budget") || error.contains("memory")), - "blocked refresh should expose the memory failure: {blocked}" - ); - if blocked_refresh["state"] == "retrying" { - let next_retry = blocked_refresh["next_retry_unix_ms"] - .as_u64() - .expect("retrying refresh must expose next_retry_unix_ms"); - assert!( - next_retry + 1_000 >= now_unix_ms(), - "next_retry_unix_ms must not be in the past while retrying/blocked: {blocked}" - ); - } else if let Some(next_retry) = blocked_refresh["next_retry_unix_ms"].as_u64() { assert!( - next_retry + 1_000 >= now_unix_ms(), - "blocked next_retry_unix_ms must not be in the past: {blocked}" + Instant::now() < deadline, + "retired memory options blocked refresh: {health}" ); - } - let old_paths = session.search_paths("initial_symbol"); - assert!( - old_paths.iter().any(|path| path == "initial.py"), - "the previous generation must remain queryable during refresh failure: {old_paths:?}" - ); - - set_config( - &config, - ConfigSettings { - policy: "leader", - backend: "poll", - interval_ms: 100, - worker_memory_mib: 768, - rust_memory_mib: 384, - spill_chunk_mib: 32, - max_parallelism: 1, - exclude: &[], - }, - ); - // Native process suites run concurrently in CI. Keep the recovery bound - // independent of the short retry cadence and allow the large source to - // finish loading while other fixtures are building. - let deadline = Instant::now() + Duration::from_secs(90); - let mut recovered = None; - let mut last_health = Value::Null; - while Instant::now() < deadline { - let health = session.health(); - last_health = health.clone(); - let refresh = &health["refresh"]; - let latest_paths = session.search_paths("latest_symbol"); - let limits = memory_limits(&health); - if refresh["state"] == "running" - && refresh["task_alive"] == true - && refresh["pending"] == false - && refresh["dirty_epoch"] == refresh["reconciled_epoch"] - && refresh["next_retry_unix_ms"].is_null() - && limits["worker_memory_mib"] == 768 - && limits["rust_memory_mib"] == 384 - && limits["spill_chunk_mib"] == 32 - && limits["max_parallelism"] == 1 - && latest_paths.iter().any(|path| path == "latest.py") - { - recovered = Some((health, latest_paths)); - break; - } thread::sleep(Duration::from_millis(100)); } - let (recovered, latest_paths) = recovered.unwrap_or_else(|| { - panic!("refresh should recover without another source edit: {last_health}") - }); - assert_eq!(recovered["refresh"]["reconcile_interval_ms"], 100); - assert!(latest_paths.iter().any(|path| path == "latest.py")); - drop(session); drop(daemon); let _ = fs::remove_dir_all(root); @@ -941,8 +899,6 @@ fn config_hot_reload_applies_excludes_while_cli_policy_and_backend_overrides_per policy: "off", backend: "native", interval_ms: 500, - worker_memory_mib: 768, - rust_memory_mib: 384, spill_chunk_mib: 32, max_parallelism: 1, exclude: &[], @@ -954,10 +910,6 @@ fn config_hot_reload_applies_excludes_while_cli_policy_and_backend_overrides_per "leader", "--refresh-backend", "poll", - "--worker-memory-mib", - "768", - "--rust-memory-mib", - "384", "--spill-chunk-mib", "32", "--max-parallelism", @@ -982,10 +934,8 @@ fn config_hot_reload_applies_excludes_while_cli_policy_and_backend_overrides_per thread::sleep(Duration::from_millis(100)); } let initial = initial.expect("CLI policy/backend overrides should run the service"); - assert_eq!(memory_limits(&initial)["worker_memory_mib"], 768); - assert_eq!(memory_limits(&initial)["rust_memory_mib"], 384); - assert_eq!(memory_limits(&initial)["spill_chunk_mib"], 32); - assert_eq!(memory_limits(&initial)["max_parallelism"], 1); + assert_eq!(execution_settings(&initial)["spill_chunk_mib"], 32); + assert_eq!(execution_settings(&initial)["max_parallelism"], 1); // The config now requests the opposite policy/backend and excludes an // existing source file. The explicit CLI settings must remain in force, @@ -996,8 +946,6 @@ fn config_hot_reload_applies_excludes_while_cli_policy_and_backend_overrides_per policy: "off", backend: "native", interval_ms: 100, - worker_memory_mib: 2, - rust_memory_mib: 1, spill_chunk_mib: 1, max_parallelism: 1, exclude: &["ignored.py"], @@ -1008,13 +956,11 @@ fn config_hot_reload_applies_excludes_while_cli_policy_and_backend_overrides_per while Instant::now() < deadline { let health = session.health(); let paths = session.search_paths("ignored_symbol"); - let limits = memory_limits(&health); + let limits = execution_settings(&health); if health["refresh"]["state"] == "running" && health["refresh"]["backend"] == "poll" && health["refresh"]["enabled"] == true && health["refresh"]["reconcile_interval_ms"] == 100 - && limits["worker_memory_mib"] == 768 - && limits["rust_memory_mib"] == 384 && limits["spill_chunk_mib"] == 32 && limits["max_parallelism"] == 1 && !paths.iter().any(|path| path == "ignored.py") @@ -1028,8 +974,6 @@ fn config_hot_reload_applies_excludes_while_cli_policy_and_backend_overrides_per assert_eq!(reloaded["refresh"]["backend"], "poll"); assert_eq!(reloaded["refresh"]["reconcile_interval_ms"], 100); assert_eq!(reloaded["refresh"]["enabled"], true); - assert_eq!(memory_limits(&reloaded)["worker_memory_mib"], 768); - assert_eq!(memory_limits(&reloaded)["rust_memory_mib"], 384); drop(session); drop(daemon); diff --git a/tests/storage_maintenance.rs b/tests/storage_maintenance.rs new file mode 100644 index 00000000..cb37c5d2 --- /dev/null +++ b/tests/storage_maintenance.rs @@ -0,0 +1,274 @@ +#[path = "support/storage_maintenance/mod.rs"] +mod fixture; + +use codebase_graph::api::{ + CodebaseGraphApi, HealthRequest, OperationInvocation, OperationRequest, OutputFormat, + RepoSelector, +}; +use serde_json::{json, Value}; + +#[test] +fn nonboolean_quiescence_confirmation_is_rejected_before_repository_effects() { + let root = fixture::FixtureRoot::new("bad-confirmation").unwrap(); + root.write_revision(20).unwrap(); + let before = fixture::observe_small_tree(root.path()).unwrap(); + for invalid in [Value::Null, json!("true"), json!(1), json!({}), json!([])] { + let invocation = OperationInvocation { + repo: RepoSelector { + repo_root: Some(root.path().to_path_buf()), + ..RepoSelector::default() + }, + arguments: json!({ "confirm_legacy_quiescence": invalid }), + output_format: OutputFormat::Typed, + }; + let error = CodebaseGraphApi::new() + .execute_invocation("health", &invocation) + .expect_err("nonboolean confirmation must fail instead of authorizing migration"); + assert_eq!(error.code, "invalid_request"); + assert!(error + .message + .contains("confirm_legacy_quiescence must be a boolean")); + assert_eq!( + fixture::observe_small_tree(root.path()).unwrap(), + before, + "invalid confirmation must be rejected before repository effects" + ); + } +} + +#[test] +fn health_preserves_legacy_fields_and_marks_unobserved_maintenance_unknown() { + let root = fixture::FixtureRoot::new("health-contract").unwrap(); + root.write_revision(20).unwrap(); + let response = CodebaseGraphApi::new() + .execute_operation(&OperationRequest::Health(HealthRequest { + repo: RepoSelector { + repo_root: Some(root.path().to_path_buf()), + ..RepoSelector::default() + }, + confirm_legacy_quiescence: false, + refresh_status: None, + output_format: OutputFormat::Typed, + })) + .expect("health reports a missing graph through its existing payload"); + let payload = response.payload; + assert_eq!(payload["database_exists"], false); + assert_eq!(payload["graph_readable"], false); + assert!(payload["cleanup_pending"].is_boolean()); + let summary = &payload["maintenance"]; + assert!( + summary.is_object(), + "additive maintenance summary is required" + ); + assert!(summary["requested"].is_boolean()); + assert!(summary["running"].is_boolean()); + assert!(summary["pending"].is_boolean()); + assert!(summary["reasons"].is_array()); + assert!(summary["errors"].is_array()); + + // A later real collector may supply an observation. The unobserved branch + // must never turn unavailable measurements into fabricated zero bytes. + if summary["observed_at_unix_ms"].is_null() { + assert_eq!(summary["measurement_complete"], false); + assert_eq!(summary["observation_complete"], false); + assert!(summary["observation_age_ms"].is_null()); + assert!(summary["inventory_status"].is_null()); + for disposition in [ + "total", + "retained", + "potential_reclamation", + "deferred", + "unknown", + ] { + assert_unknown_bucket(&summary["bytes"][disposition]); + } + for category in [ + "active_graph", + "active_cache", + "leased_generation", + "live_run", + "recovery_evidence", + "artifact_temporary", + "legacy_staging", + "retired_direct", + "coordination", + "unknown", + ] { + assert_unknown_bucket(&summary["bytes"]["by_category"][category]); + } + } +} + +fn assert_unknown_bucket(bucket: &Value) { + let object = bucket.as_object().expect("each fixed byte bucket exists"); + assert!(object.contains_key("logical_bytes")); + assert!(object.contains_key("allocated_bytes")); + assert!(bucket["logical_bytes"].is_null()); + assert!(bucket["allocated_bytes"].is_null()); +} + +#[test] +fn repeated_health_entries_preserve_source_and_foreign_sentinels() { + let root = fixture::FixtureRoot::new("health-confinement").unwrap(); + root.write_revision(20).unwrap(); + // These independent sentinels are outside registered program-owned state. + // Exact observations include bytes, modes and modification timestamps. + let protected = ["src", "target", "knowledge", "caller-staging", "foreign"]; + for directory in &protected[1..] { + let path = root.path().join(directory); + std::fs::create_dir(&path).unwrap(); + std::fs::write( + path.join("sentinel.bin"), + b"must survive ordinary runtime entries\0\xff", + ) + .unwrap(); + } + #[cfg(unix)] + let external = fixture::FixtureRoot::new("health-external-sentinel").unwrap(); + #[cfg(unix)] + let external_before = { + std::fs::write( + external.path().join("sentinel.bin"), + b"external bytes must survive", + ) + .unwrap(); + std::os::unix::fs::symlink(external.path(), root.path().join("foreign/external-link")) + .unwrap(); + fixture::observe_small_tree(external.path()).unwrap() + }; + let before: Vec<_> = protected + .iter() + .map(|directory| fixture::observe_small_tree(&root.path().join(directory)).unwrap()) + .collect(); + for _ in 0..3 { + let response = CodebaseGraphApi::new() + .execute_operation(&OperationRequest::Health(HealthRequest { + repo: RepoSelector { + repo_root: Some(root.path().to_path_buf()), + ..RepoSelector::default() + }, + confirm_legacy_quiescence: false, + refresh_status: None, + output_format: OutputFormat::Typed, + })) + .expect("ordinary health entry reports missing graph without touching foreign state"); + assert_eq!(response.payload["database_exists"], false); + assert_eq!(response.payload["graph_readable"], false); + #[cfg(unix)] + assert_eq!( + fixture::observe_small_tree(external.path()).unwrap(), + external_before, + "external symlink target changed across an unchanged runtime entry" + ); + for (directory, observation) in protected.iter().zip(&before) { + assert_eq!( + &fixture::observe_small_tree(&root.path().join(directory)).unwrap(), + observation, + "protected tree {directory} changed across an unchanged runtime entry" + ); + } + } +} + +/// Public producer/query/reuse coverage only. Disk convergence and injected +/// failure/retirement boundaries require the real collector hooks separately. +#[test] +fn twenty_public_builds_match_clean_control_and_preserve_unchanged_artifact() { + use fixture::workload::{self, BehaviorSnapshot, StableArtifact, WorkloadPair, UPDATES}; + + let pair = WorkloadPair::new().unwrap(); + workload::install(&pair.churn).expect("public setup must publish the initial fixture graph"); + let stable = StableArtifact::observe(&pair.churn).unwrap(); + for revision in 1..=UPDATES { + pair.churn.write_revision(revision).unwrap(); + let response = workload::materialize(&pair.churn, "full") + .unwrap_or_else(|error| panic!("revision {revision} materialization failed: {error}")); + stable.assert_reused(&StableArtifact::observe(&pair.churn).unwrap(), &response); + let unchanged = workload::unchanged_refresh(&pair.churn).unwrap_or_else(|error| { + panic!("revision {revision} unchanged refresh failed: {error}") + }); + println!("revision {revision}: published with stable artifact reuse; {unchanged}"); + + assert_eq!(stable, StableArtifact::observe(&pair.churn).unwrap()); + } + pair.assert_equal_final_sources(); + workload::install(&pair.control) + .expect("public setup must publish the independent final-state control"); + let churn = BehaviorSnapshot::observe(&pair.churn, UPDATES).unwrap(); + let control = BehaviorSnapshot::observe(&pair.control, UPDATES).unwrap(); + assert_eq!( + churn, control, + "churn changed reachable graph/search/context behavior" + ); +} + +#[cfg(debug_assertions)] +#[test] +fn native_phase_owner_death_preserves_prior_graph_and_retries_without_source_edit() { + use fixture::{ + process::FaultFixture, + workload::{self, BehaviorSnapshot, StableArtifact}, + }; + use std::time::Duration; + + let mut fixture = FaultFixture::new().unwrap(); + workload::install(fixture.root()).unwrap(); + let before = BehaviorSnapshot::observe(fixture.root(), 0).unwrap(); + let stable = StableArtifact::observe(fixture.root()).unwrap(); + fixture.root().write_revision(1).unwrap(); + let mut build = fixture.start_paused_build().unwrap(); + let phase = build + .wait_for_native_phase(Duration::from_secs(20)) + .unwrap(); + phase.assert_candidate_present(); + assert_eq!( + before, + BehaviorSnapshot::observe(fixture.root(), 0).unwrap() + ); + fixture.assert_external_unchanged(); + assert!(!build.interrupt_owner().unwrap().success()); + drop(build); + fixture + .wait_for_recovery(&phase, Duration::from_secs(30)) + .unwrap(); + assert_eq!( + before, + BehaviorSnapshot::observe(fixture.root(), 0).unwrap() + ); + fixture.assert_external_unchanged(); + // No write_revision call follows the interruption: the pending source is + // retried exactly once after real lifecycle leases/proofs establish empty + // containment. Retained request bytes remain unchanged until this writer; + // finish_recovery still requires their actual disposal after publication. + let response = workload::materialize(fixture.root(), "full").unwrap(); + stable.assert_reused(&StableArtifact::observe(fixture.root()).unwrap(), &response); + BehaviorSnapshot::observe(fixture.root(), 1).unwrap(); + fixture.finish_recovery(&phase, &response).unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +fn isolated_worker_first_creation_and_followup_keep_same_covered_publication_birth() { + use fixture::{ + process::{CoveredEpochObservation, FaultFixture}, + workload, + }; + use std::time::Duration; + let mut fixture = FaultFixture::new().unwrap(); + fixture.reset_closed_managed_storage().unwrap(); + let mut mcp = fixture.start_mcp().unwrap(); + let first = mcp.wait_for_revision(0, Duration::from_secs(90)).unwrap(); + let first_coverage = CoveredEpochObservation::observe(fixture.root(), &first).unwrap(); + fixture.root().write_revision(1).unwrap(); + let followup = mcp.wait_for_revision(1, Duration::from_secs(90)).unwrap(); + let followup_coverage = CoveredEpochObservation::observe(fixture.root(), &followup).unwrap(); + followup_coverage.assert_same_birth_and_advanced_epoch(&first_coverage); + let reaped = mcp.close().unwrap(); + fixture.root().write_revision(2).unwrap(); + let later = workload::materialize(fixture.root(), "full").unwrap(); + let health = fixture.public_health().unwrap(); + let later_coverage = CoveredEpochObservation::observe(fixture.root(), &health).unwrap(); + later_coverage.assert_same_birth_and_advanced_epoch(&followup_coverage); + workload::BehaviorSnapshot::observe(fixture.root(), 2).unwrap(); + fixture.finish_worker_fixture(reaped, &later).unwrap(); +} diff --git a/tests/support/lifecycle_faults.rs b/tests/support/lifecycle_faults.rs index 84b84364..55be97d3 100644 --- a/tests/support/lifecycle_faults.rs +++ b/tests/support/lifecycle_faults.rs @@ -268,7 +268,21 @@ fn hold_finished_leases(root: &Path, leases: &mut Vec) -> std::io::Res .is_some_and(|receipt| receipt["containment_empty"] == true); let retired = fs::read(path.join("retired")).is_ok_and(|marker| marker == b"retired-v2\n"); - if !empty_receipt && !retired { + // The owner can die after lease creation but before publishing any + // launch/descriptor. Exclusive ownership plus an exact lease-only + // namespace proves that pre-execution seam empty. Unknown files, + // launch evidence and live leases remain protected. + let lease_only = fs::read_dir(&path)?.try_fold(true, |only, entry| { + let entry = entry?; + let metadata = fs::symlink_metadata(entry.path())?; + Ok::<_, std::io::Error>( + only && entry.file_name() == "lease" + && metadata.is_file() + && !metadata.file_type().is_symlink() + && metadata.len() == 0, + ) + })?; + if !empty_receipt && !retired && !lease_only { return Ok(false); } leases.push(lease); @@ -327,6 +341,43 @@ fn fixture_cleanup_accepts_retired_startup_without_terminal_receipt() { remove_quiescent_repo(&root); } +#[test] +fn fixture_cleanup_accepts_only_unlocked_empty_preexecution_lease() { + use fs2::FileExt; + let root = temp_repo(); + let lifecycle = root.join(".lifecycle-empty-startup"); + fs::create_dir(&lifecycle).unwrap(); + fs::write(lifecycle.join("lease"), b"").unwrap(); + let live = fs::OpenOptions::new() + .read(true) + .write(true) + .open(lifecycle.join("lease")) + .unwrap(); + live.lock_exclusive().unwrap(); + assert!(!hold_finished_leases(&root, &mut Vec::new()).unwrap()); + drop(live); + let foreign = lifecycle.join("foreign"); + fs::write(&foreign, b"must retain unknown startup evidence").unwrap(); + assert!(!hold_finished_leases(&root, &mut Vec::new()).unwrap()); + assert_eq!( + fs::read(&foreign).unwrap(), + b"must retain unknown startup evidence" + ); + fs::remove_file(foreign).unwrap(); // This fixture created the negative sentinel. + let mut leases = Vec::new(); + assert!(hold_finished_leases(&root, &mut leases).unwrap()); + assert_eq!(leases.len(), 1); + assert!( + fs::File::open(lifecycle.join("lease")) + .unwrap() + .try_lock_exclusive() + .is_err(), + "positive proof must retain its actual lease until fixture deletion" + ); + drop(leases); + remove_quiescent_repo(&root); +} + #[test] fn fixture_cleanup_keeps_unproven_or_locked_containment() { use fs2::FileExt; @@ -334,6 +385,7 @@ fn fixture_cleanup_keeps_unproven_or_locked_containment() { let lifecycle = root.join(".lifecycle-unproven"); fs::create_dir(&lifecycle).unwrap(); fs::write(lifecycle.join("lease"), b"").unwrap(); + fs::write(lifecycle.join("launch.json"), b"unverified launch evidence").unwrap(); assert!(!hold_finished_leases(&root, &mut Vec::new()).unwrap()); fs::write(lifecycle.join("retired"), b"invalid").unwrap(); assert!(!hold_finished_leases(&root, &mut Vec::new()).unwrap()); @@ -365,9 +417,8 @@ fn configured_repo() -> (PathBuf, PathBuf, Value) { ConfigSettings { policy: "leader", backend: "poll", - interval_ms: 100, - worker_memory_mib: 512, - rust_memory_mib: 384, + // Leave an observable quiescent window after completed recovery. + interval_ms: 5_000, spill_chunk_mib: 32, max_parallelism: 1, exclude: &[], @@ -416,7 +467,7 @@ fn verified_abandoned_guardian_is_terminated_before_automatic_refresh() { serde_json::to_vec(&json!({"descriptor":descriptor,"spec":{ "program":binary(),"args":vec![std::ffi::OsString::from("__codebase_graph_internal"),std::ffi::OsString::from("not-started")], "workspace":workspace,"stderr_path":workspace.join("stderr.log"),"stdout":"Null", - "memory_limit_bytes":null,"parent_rss_pid":null + "parent_rss_pid":null }})) .unwrap(), ) @@ -694,8 +745,6 @@ impl Fixture { policy: "leader", backend: "poll", interval_ms: 100, - worker_memory_mib: 512, - rust_memory_mib: 384, spill_chunk_mib: 32, max_parallelism: 1, exclude: &[], @@ -857,7 +906,7 @@ fn manual_publication_waits_for_failed_phase_tree_to_exit() { let mut manual = ManualBuild::start(&fixture.root); terminate(worker); - let deadline = Instant::now() + Duration::from_secs(90); + let deadline = Instant::now() + publication_wait_timeout(); let mut released = false; let mut published = false; let mut manual_finished = false; @@ -922,38 +971,27 @@ fn authenticated_cancellation_cleans_loader_and_retries_pending_edit() { } #[test] -fn real_loader_memory_failure_cleans_containment_and_retries_pending_edit() { +fn loader_allocation_is_observed_without_resource_budget_cancellation() { let mut fixture = Fixture::new(false); - // The native loader owns the allocation. Its ordinary RSS sampler must - // detect this while the loader remains inside its ready barrier. - fs::write( - fixture - .barrier - .join(format!("ALLOCATE_BYTES-{}", fixture.phase_pid)), - (512_u64 * 1024 * 1024).to_string(), - ) - .unwrap(); - let mut last_health = Value::Null; + let allocation = fixture + .barrier + .join(format!("ALLOCATE_BYTES-{}", fixture.phase_pid)); + fs::write(&allocation, (8_u64 * 1024 * 1024).to_string()).unwrap(); + let ready = fixture + .barrier + .join(format!("phase-{}.memory.json", fixture.phase_pid)); wait_until( - "native loader allocation did not report a memory budget failure", - || { - last_health = fixture.session.health(); - last_health["refresh"]["pending"] == true - && last_health["refresh"]["last_error"] - .as_str() - .is_some_and(|error| error.contains("memory_budget_exceeded")) - }, + "native loader did not acknowledge its fixture-owned allocation", + || ready.is_file(), ); - wait_until("memory failure left the loader alive", || { - !alive(fixture.phase_pid) - }); + assert!(alive(fixture.phase_pid)); fixture.unchanged(); - fs::remove_file( - fixture - .barrier - .join(format!("ALLOCATE_BYTES-{}", fixture.phase_pid)), - ) - .unwrap(); + let health = fixture.session.health(); + assert!(!health["refresh"]["last_error"] + .as_str() + .unwrap_or("") + .contains("memory_budget_exceeded")); + fs::remove_file(allocation).unwrap(); fixture.verify_recovery(); } diff --git a/tests/support/storage_maintenance/collector.rs b/tests/support/storage_maintenance/collector.rs new file mode 100644 index 00000000..b326a710 --- /dev/null +++ b/tests/support/storage_maintenance/collector.rs @@ -0,0 +1,3235 @@ +//! DC-S5 real collector qualification. No synthetic ownership or retirement receipts. +//! This module is compiled inside the product library to use its actual admission. +use crate::api::context::RepositoryIdentity; +use crate::api::{ + CodebaseGraphApi, ContextRequest, HealthRequest, MaterializationRequest, McpTransport, + OperationRequest, OutputFormat, QueryRequest, RepoSelector, RepositoryLifecycleRequest, + SearchRequest, +}; +use crate::coordinator::ExecutionDispatcher; +use crate::protocol::{MaintenanceSummary, NativeManifest, NativeSyntaxMaterializationResponse}; +use crate::storage::layout::ManagedLayout; +use crate::storage::maintenance::{start_controller, MAINTENANCE_MEMORY_BYTES}; +use crate::storage::maintenance_budget::CleanupMemory; +use crate::storage::managed::ManagedStore; +use serde_json::{json, Value}; +use sha2::{Digest, Sha256}; +use std::{ + collections::BTreeSet, + fs, + io::Read, + path::{Path, PathBuf}, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, + time::SystemTime, +}; + +static SEQUENCE: AtomicU64 = AtomicU64::new(0); +const STAGING_FIXTURE_FUNCTIONS: usize = 4; +struct FixtureRoot { + path: PathBuf, +} +impl FixtureRoot { + fn new() -> Self { + for _ in 0..100 { + let sequence = SEQUENCE.fetch_add(1, Ordering::Relaxed); + let parent = std::env::temp_dir().join(format!( + "cgraph-s5-collector-{}-{sequence:016x}", + std::process::id() + )); + match fs::create_dir(&parent) { + Ok(()) => { + let path = parent.join("repository"); + fs::create_dir(&path).unwrap(); + let path = fs::canonicalize(path).unwrap(); + return Self { path }; + } + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue, + Err(error) => panic!("allocate fixture: {error}"), + } + } + panic!("exclusive fixture allocation exhausted") + } + fn path(&self) -> &Path { + &self.path + } + fn write_revision(&self, revision: usize) { + fs::create_dir_all(self.path.join("src")).unwrap(); + if !self.path.join("src/stable.rs").exists() { + fs::write( + self.path.join("src/stable.rs"), + "fn stable_helper() -> usize { 7 }\n", + ) + .unwrap(); + } + fs::write(self.path.join("src/lib.rs"), format!("mod stable;\npub use stable::stable_helper;\nfn revision_{revision}() -> usize {{ stable_helper() + {revision} }}\n")).unwrap(); + } +} +impl Drop for FixtureRoot { + fn drop(&mut self) { + if std::thread::panicking() { + eprintln!( + "retained failed storage qualification fixture: {}", + self.path.display() + ); + return; + } + let _ = fs::remove_dir_all(self.path.parent().unwrap()); + } +} + +// These read oracles retain the same ranked fields and complete context as the +// external workload fixture. Opaque node IDs alone are omitted between roots. +fn selector(root: &FixtureRoot) -> RepoSelector { + RepoSelector { + repo_root: Some(root.path().to_path_buf()), + ..RepoSelector::default() + } +} + +fn execute(request: OperationRequest) -> Result { + CodebaseGraphApi::new() + .execute_operation(&request) + .map(|response| response.payload) + .map_err(|error| format!("{}: {}", error.code, error.message)) +} + +/// Install through the public producer; do not synthesize a managed manifest or +/// ownership record. MCP/client and agent-hook installation are explicitly off. +fn install(root: &FixtureRoot) -> Result { + execute(OperationRequest::Setup(RepositoryLifecycleRequest { + repo: selector(root), + action: "setup".into(), + output_format: OutputFormat::Typed, + dry_run: false, + mcp_client: Some("none".into()), + agent_hooks: "none".into(), + mcp_config_path: None, + instructions_target: Some("skip".into()), + skip_mcp_config: true, + mcp_transport: McpTransport::Stdio, + mcp_daemon_port: None, + mode: "full".into(), + include_fts: true, + })) +} + +fn materialize( + root: &FixtureRoot, + mode: &str, +) -> Result { + materialize_selected(selector(root), mode) +} + +fn materialize_selected( + repo: RepoSelector, + mode: &str, +) -> Result { + let payload = execute(OperationRequest::Materialize(MaterializationRequest { + repo, + native_request_path: None, + source_root: None, + mode: mode.into(), + include_fts: true, + use_git: false, + git_diff: false, + git_base: None, + include_patterns: Vec::new(), + exclude_patterns: Vec::new(), + candidate_paths: Vec::new(), + parallel: false, + spill_chunk_mib: None, + max_parallelism: Some(1), + progress: false, + output_format: OutputFormat::Typed, + }))?; + serde_json::from_value(payload).map_err(|error| format!("materialization response: {error}")) +} + +fn health(root: &FixtureRoot) -> Result { + execute(OperationRequest::Health(HealthRequest { + repo: selector(root), + confirm_legacy_quiescence: false, + refresh_status: None, + output_format: OutputFormat::Typed, + })) +} + +#[derive(Debug, PartialEq, Eq)] +struct BehaviorSnapshot { + function_rows: Vec, + stable_search: Vec, + revision_search: Vec, + revision_context: Vec, +} + +impl BehaviorSnapshot { + fn observe(root: &FixtureRoot, revision: usize) -> Result { + let query = execute(OperationRequest::Query(QueryRequest { + repo: selector(root), + statement: "MATCH (n:Function) RETURN n.label, n.path, n.line_start, n.line_end, n.summary ORDER BY n.path, n.line_start, n.label".into(), + parameters: json!({}), limit: 100, output_format: OutputFormat::Typed, + }))?; + if query["truncated"] != false { + return Err("function query is truncated or lacks completeness".into()); + } + let function_rows = query["rows"] + .as_array() + .ok_or("missing function rows")? + .clone(); + let revision_name = format!("revision_{revision}"); + let mut names = Vec::new(); + for row in &function_rows { + let row = row.as_array().ok_or("function row is not an array")?; + if row.len() != 5 { + return Err("function projection columns changed".into()); + } + names.push(row[0].as_str().ok_or("function name is not a string")?); + } + names.sort_unstable(); + let mut expected = vec![revision_name.as_str(), "stable_helper"]; + expected.sort_unstable(); + if names != expected { + return Err(format!("unexpected reachable function names: {names:?}")); + } + let stable = search(root, "stable_helper")?; + let current = search(root, &revision_name)?; + let selected = current + .iter() + .find(|hit| hit["label"] == revision_name && hit["type"] == "Function") + .ok_or("search did not find current revision function")?; + if !stable + .iter() + .any(|hit| hit["label"] == "stable_helper" && hit["type"] == "Function") + { + return Err("search did not find unchanged helper".into()); + } + let context = execute(OperationRequest::Context(ContextRequest { + repo: selector(root), + query: None, + layer: "semantic".into(), + profile: "definitions".into(), + limit: 100, + budget: 10000, + context_limit: 100, + max_depth: Some(1), + detail: "slim".into(), + node_id: Some( + selected["id"] + .as_str() + .ok_or("search result has no node identity")? + .into(), + ), + node_type: Some("Function".into()), + output_format: OutputFormat::Typed, + }))?; + let context = context["context"] + .as_array() + .ok_or("missing explicit-node context")?; + if context.is_empty() { + return Err("definitions context is unexpectedly empty".into()); + } + let mut revision_context = context + .iter() + .map(project_context) + .collect::, _>>()?; + revision_context.sort_by_key(Value::to_string); + Ok(Self { + function_rows, + stable_search: project_search(&stable)?, + revision_search: project_search(¤t)?, + revision_context, + }) + } +} + +fn search(root: &FixtureRoot, query: &str) -> Result, String> { + let result = execute(OperationRequest::Search(SearchRequest { + repo: selector(root), + query: query.into(), + layer: "semantic".into(), + profile: "definitions".into(), + limit: 100, + budget: 10000, + context_limit: 2, + max_depth: Some(1), + detail: "slim".into(), + output_format: OutputFormat::Typed, + }))?; + result["results"] + .as_array() + .cloned() + .ok_or("missing search results".into()) +} + +/// Omit only repository-specific node IDs/evidence chains. Preserve ranked +/// order, scores, paths, source spans and content; context keeps multiplicity. +fn project_search(rows: &[Value]) -> Result, String> { + rows.iter() + .map(|row| { + project( + row, + &["type", "label", "layer", "path", "rank_score"], + &["span", "summary"], + ) + }) + .collect() +} + +fn project_context(row: &Value) -> Result { + project( + row, + &["type", "label", "layer", "path", "direction", "relation"], + &["span", "summary"], + ) +} + +fn project(row: &Value, required: &[&str], optional: &[&str]) -> Result { + let source = row.as_object().ok_or("response row is not an object")?; + let mut projected = serde_json::Map::new(); + for key in required { + let value = source + .get(*key) + .filter(|value| !value.is_null()) + .ok_or_else(|| format!("response row lacks {key}"))?; + if (*key == "rank_score" && !value.is_number()) + || (*key != "rank_score" && !value.is_string()) + { + return Err(format!("response row has invalid {key} type")); + } + projected.insert((*key).into(), value.clone()); + } + for key in optional { + if let Some(value) = source.get(*key) { + if (*key == "summary" && !value.is_string()) || (*key == "span" && !value.is_object()) { + return Err(format!("response row has invalid {key} type")); + } + projected.insert((*key).into(), value.clone()); + } + } + Ok(Value::Object(projected)) +} + +fn collector_attempt(root: &FixtureRoot) -> MaintenanceSummary { + let summary = collector_attempt_observed(root); + assert!( + summary.errors.is_empty(), + "collector reported error: {summary:?}" + ); + summary +} + +fn collector_attempt_observed(root: &FixtureRoot) -> MaintenanceSummary { + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).unwrap(); + let baseline = memory.used(); + let target = RepositoryIdentity::capture(&selector(root)) + .unwrap() + .maintenance_target(&memory) + .unwrap(); + let (mut runtime, client) = start_controller( + target, + memory.clone(), + Arc::new(ExecutionDispatcher::default()), + false, + ) + .unwrap(); + client.wait_for_first_attempt(); + runtime.stop_and_join(); + let projection = client.projection(); + let summary = projection.summary.clone(); + if let Some(attempt) = &summary.last_attempt { + if let Some(entries) = attempt.attempted_entries { + assert!(entries <= 100, "mutation entry grant exceeded: {summary:?}"); + } + if let Some(deleted) = attempt.deleted_entries { + assert!( + deleted <= 100, + "mutation deletion count exceeded grant: {summary:?}" + ); + if deleted > 0 { + assert!( + attempt + .attempted_entries + .is_some_and(|entries| entries >= deleted), + "actual deletion lacks its charged mutation grant: {summary:?}" + ); + } + } + } + drop(projection); + drop(client); + drop(runtime); + assert_eq!( + memory.used(), + baseline, + "joined collector retained owned allocations" + ); + assert!(memory.peak() <= memory.limit()); + summary +} + +fn committed(root: &FixtureRoot) -> (PathBuf, String, NativeManifest) { + let response = health(root).unwrap(); + assert_eq!(response["graph_readable"], true); + // A count includes real closed producer roots retained for bounded GC. + // It alone cannot classify live/recovery authority or verified idleness. + let storage = fs::canonicalize(response["storage_root"].as_str().unwrap()).unwrap(); + assert!(storage.starts_with(fs::canonicalize(root.path()).unwrap())); + let generation = response["active_generation"].as_str().unwrap().to_owned(); + let manifest_path = fs::canonicalize(response["manifest_path"].as_str().unwrap()).unwrap(); + assert!(manifest_path.starts_with(&storage)); + let manifest = serde_json::from_slice(&fs::read(manifest_path).unwrap()).unwrap(); + (storage, generation, manifest) +} + +fn actual_generation_ids(storage: &Path) -> BTreeSet { + fs::read_dir(storage.join("generations")) + .unwrap() + .map(|entry| { + let entry = entry.unwrap(); + assert!( + entry.file_type().unwrap().is_dir(), + "foreign generation entry retained for investigation" + ); + let name = entry.file_name().into_string().unwrap(); + name.strip_prefix("gen-") + .expect("real managed generation directory prefix") + .to_owned() + }) + .collect() +} +fn cache_keys(storage: &Path) -> BTreeSet { + let mut keys = BTreeSet::new(); + for shard in fs::read_dir(storage.join("artifacts")).unwrap() { + let shard = shard.unwrap(); + let name = shard.file_name().into_string().unwrap(); + let kind = shard.file_type().unwrap(); + if [ + ".use.lock", + ".use-cohort.json", + ".reference-epoch", + ".reference-activity.lock", + ".reference-epoch.lock", + ".publication.binding", + ".publication.data0", + ".publication.data1", + ".publication.selector", + ".publication.stream", + ".workspace-removal.data0", + ".workspace-removal.data1", + ".workspace-removal.selector", + ".workspace-removal.binding", + ] + .contains(&name.as_str()) + && kind.is_file() + { + continue; + } + if name.len() != 2 || !name.bytes().all(|byte| byte.is_ascii_hexdigit()) || !kind.is_dir() { + keys.insert(format!("!unclassified-root:{name}")); + continue; + } + for entry in fs::read_dir(shard.path()).unwrap() { + let entry = entry.unwrap(); + let key = entry.file_name().into_string().unwrap(); + let kind = entry.file_type().unwrap(); + if key.len() != 64 + || !key.bytes().all(|byte| byte.is_ascii_hexdigit()) + || !key.starts_with(&name) + || !kind.is_dir() + { + keys.insert(format!("!unclassified-key:{name}/{key}")); + continue; + } + let payload = fs::symlink_metadata(entry.path().join("partition.json")); + if !payload.is_ok_and(|metadata| metadata.file_type().is_file()) { + keys.insert(format!("!incomplete-key:{name}/{key}")); + } + for leaf in fs::read_dir(entry.path()).unwrap() { + let leaf = leaf.unwrap(); + let name = leaf.file_name().into_string().unwrap(); + if !leaf.file_type().unwrap().is_file() + || !["partition.json", ".entry-origin"].contains(&name.as_str()) + { + keys.insert(format!("!unclassified-leaf:{key}/{name}")); + } + } + if !active_origin_matches_payload(&entry.path(), &key) { + keys.insert(format!("!unverified-origin:{key}")); + } + keys.insert(key); + } + } + // Transient or unknown entries remain explicit non-key members, so they + // prevent final convergence while allowing another real bounded attempt. + keys +} + +/// Independent observation of the actual producer's fixed origin journal. This +/// verifies bytes and phase completion only; it never constructs authority. +fn active_origin_matches_payload(directory: &Path, key: &str) -> bool { + let Ok(metadata) = fs::symlink_metadata(directory.join(".entry-origin")) else { + return false; + }; + if !metadata.file_type().is_file() || metadata.len() != 2048 { + return false; + } + let Ok(bytes) = fs::read(directory.join(".entry-origin")) else { + return false; + }; + for (phase, frame) in bytes.chunks_exact(512).enumerate() { + if &frame[..8] != b"CGORIG01" + || frame[24] != phase as u8 + || frame[480..] != Sha256::digest(&frame[..480])[..] + { + return false; + } + } + let last = &bytes[1536..]; + let observed_key = last[64..96] + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::(); + if observed_key != key { + return false; + } + let Ok(metadata) = fs::symlink_metadata(directory.join("partition.json")) else { + return false; + }; + if !metadata.file_type().is_file() + || metadata.len() != u64::from_le_bytes(last[256..264].try_into().unwrap()) + { + return false; + } + observe_file(&directory.join("partition.json")).digest == last[264..296] +} + +fn referenced_keys(manifest: &NativeManifest) -> BTreeSet { + manifest + .files + .values() + .map(|entry| { + entry + .artifact_key + .clone() + .expect("real producer artifact key") + }) + .collect() +} + +#[derive(Debug, PartialEq, Eq)] +struct FileObservation { + digest: [u8; 32], + modified: SystemTime, + readonly: bool, +} +fn observe_file(path: &Path) -> FileObservation { + let metadata = fs::symlink_metadata(path).unwrap(); + assert!(metadata.file_type().is_file()); + let mut file = fs::File::open(path).unwrap(); + let mut buffer = [0u8; 8192]; + let mut digest = Sha256::new(); + loop { + let count = file.read(&mut buffer).unwrap(); + if count == 0 { + break; + } + digest.update(&buffer[..count]); + } + FileObservation { + digest: digest.finalize().into(), + modified: metadata.modified().unwrap(), + readonly: metadata.permissions().readonly(), + } +} + +#[cfg(unix)] +fn observe_link(path: &Path) -> (PathBuf, u64, u64, SystemTime, u32) { + use std::os::unix::fs::MetadataExt; + let metadata = fs::symlink_metadata(path).unwrap(); + assert!(metadata.file_type().is_symlink()); + ( + fs::read_link(path).unwrap(), + metadata.dev(), + metadata.ino(), + metadata.modified().unwrap(), + metadata.mode(), + ) +} + +fn drain(root: &FixtureRoot) { + let (storage, active, manifest) = committed(root); + let expected = referenced_keys(&manifest); + let receipts = ["runs", "phases"] + .into_iter() + .filter_map(|name| fs::read_dir(storage.join(name)).ok()) + .flatten() + .map(|entry| { + fs::read_dir(entry.unwrap().path().join(".workspace-origins")) + .map(|entries| entries.count()) + .unwrap_or(0) + }) + .sum::(); + // Each real receipt advances through fresh cursor, intent, detach and + // removal grants. Size the fixture's bound to its actual work, while each + // individual collector attempt still enforces the production grant limits. + let attempts = + 128 + 16 * (receipts + cache_keys(&storage).len() + actual_generation_ids(&storage).len()); + let mut last = None; + for _ in 0..attempts { + last = Some(collector_attempt(root)); + if actual_generation_ids(&storage) == BTreeSet::from([active.clone()]) + && cache_keys(&storage) == expected + && fs::read_dir(storage.join("runs")).unwrap().next().is_none() + && fs::metadata(storage.join("artifacts/.publication.stream")) + .is_ok_and(|metadata| metadata.len() == 0) + { + return; + } + } + panic!( + "actual collector did not converge in {attempts} attempts without source edit: {last:?}" + ); +} + +#[test] +fn dc_s5_empty_terminal_workspace_removal_fits_actual_grants() { + use crate::storage::maintenance_ownership::ReferenceDomain; + use crate::storage::run_workspace::RunWorkspace; + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, active, _) = committed(&root); + prepare_canonical_fixture_registry(&storage, &active); + let layout = ManagedLayout::new(storage); + let domain = ReferenceDomain::managed(&layout).unwrap(); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let terminal = run.retire_for_maintenance().unwrap(); + let mut last = None; + for _ in 0..64 { + last = Some(collector_attempt(&root)); + if !terminal.exists() { + BehaviorSnapshot::observe(&root, 0).unwrap(); + return; + } + } + panic!("fixed metadata removal cannot fit fresh actual grants: {last:?}"); +} + +#[test] +fn dc_s5_terminal_absent_barrier_rejects_appeared_entries_under_real_dispatcher() { + use crate::storage::maintenance_admission::{prepare_target_binding, PreparedTargetBinding}; + use crate::storage::maintenance_budget::MemoryBox; + use crate::storage::maintenance_controls::{ + prepare_terminal_workspace_boxed, with_terminal_workspace, + }; + use crate::storage::maintenance_ownership::ReferenceDomain; + use crate::storage::maintenance_scheduler::{ + with_claimed_legacy_confirmation, AttemptOutcome, TargetBinding, + }; + use std::cell::RefCell; + use std::sync::atomic::AtomicBool; + + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, _, _) = committed(&root); + let terminal = fs::read_dir(storage.join("runs")) + .unwrap() + .next() + .unwrap() + .unwrap() + .path(); + let barrier: PathBuf = + serde_json::from_slice(&fs::read(terminal.join("cleanup-barrier.json")).unwrap()).unwrap(); + assert_eq!(barrier, storage.join("phases")); + assert!(!barrier.exists()); + let sentinel = root.path().join("barrier-link-target"); + fs::write(&sentinel, b"foreign sentinel").unwrap(); + let sentinel_before = observe_file(&sentinel); + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).unwrap(); + let target = RepositoryIdentity::capture(&selector(&root)) + .unwrap() + .maintenance_target(&memory) + .unwrap() + .target() + .clone(); + for replacement in 0..if cfg!(unix) { 3 } else { 2 } { + let request_target = target.clone(); + let prepare_target = target.clone(); + let prepare_terminal = terminal.clone(); + let domain = ReferenceDomain::managed(&ManagedLayout::new(&storage)).unwrap(); + let barrier = barrier.clone(); + #[cfg(unix)] + let sentinel = sentinel.clone(); + let seen = Arc::new(AtomicBool::new(false)); + let seen_callback = seen.clone(); + let mut binding: Option> = None; + let snapshot = with_claimed_legacy_confirmation( + Some(&request_target), + Arc::new(ExecutionDispatcher::default()), + move |memory, _, _| { + let binding = binding.get_or_insert_with(|| { + MemoryBox::new( + prepare_target_binding(&prepare_target, memory).unwrap(), + memory, + ) + .unwrap() + }); + let prepared = + prepare_terminal_workspace_boxed(&prepare_terminal, &domain, memory).unwrap(); + Ok((TargetBinding(binding.digest()), RefCell::new(prepared))) + }, + move |prepared, _, grant| { + assert!(!seen_callback.swap(true, Ordering::AcqRel)); + grant.take_entry().unwrap(); + match replacement { + 0 => fs::write(&barrier, b"foreign appeared file").unwrap(), + 1 => fs::create_dir(&barrier).unwrap(), + #[cfg(unix)] + 2 => std::os::unix::fs::symlink(&sentinel, &barrier).unwrap(), + _ => unreachable!(), + } + let mut effect_called = false; + let result = + with_terminal_workspace(&mut prepared.borrow_mut(), grant, 0, &mut |_, _| { + effect_called = true; + Ok(()) + }); + assert!( + result.is_err(), + "appeared barrier passed terminal admission" + ); + assert!(!effect_called, "stale absence permitted an effect"); + if replacement == 1 { + fs::remove_dir(&barrier).unwrap(); + } else { + fs::remove_file(&barrier).unwrap(); + } + Ok(AttemptOutcome::Complete) + }, + ); + assert!(seen.load(Ordering::Acquire), "{snapshot:?}"); + assert_eq!( + snapshot.last_attempt.unwrap().outcome, + AttemptOutcome::Complete + ); + } + assert_eq!(observe_file(&sentinel), sentinel_before); +} + +#[test] +fn dc_s5_twenty_updates_collect_without_source_edits_and_match_clean_control() { + let churn = FixtureRoot::new(); + let control = FixtureRoot::new(); + churn.write_revision(0); + install(&churn).unwrap(); + let (storage, _, first) = committed(&churn); + let stable_key = first.files["src/stable.rs"].artifact_key.clone().unwrap(); + let stable_path = storage + .join("artifacts") + .join(&stable_key[..2]) + .join(&stable_key) + .join("partition.json"); + let stable_before = observe_file(&stable_path); + let foreign = storage.join("unknown-user-note"); + fs::write( + &foreign, + b"arbitrary foreign data, never a retirement proof", + ) + .unwrap(); + let foreign_before = observe_file(&foreign); + let external = FixtureRoot::new(); + let external_file = external.path().join("external-sentinel"); + fs::write(&external_file, b"independent external sentinel").unwrap(); + let external_before = observe_file(&external_file); + #[cfg(unix)] + let external_link = { + let link = storage.join("unknown-user-link"); + std::os::unix::fs::symlink(&external_file, &link).unwrap(); + link + }; + #[cfg(unix)] + let external_link_before = observe_link(&external_link); + for revision in 1..=20 { + churn.write_revision(revision); + let result = materialize(&churn, "full").unwrap(); + assert!(result.database_written); + assert!(result.artifacts_reused >= 1); + // Deliberately no subsequent source edit is used to unblock collection. + drain(&churn); + assert_eq!(observe_file(&stable_path), stable_before); + assert_eq!(observe_file(&foreign), foreign_before); + assert_eq!(observe_file(&external_file), external_before); + #[cfg(unix)] + assert_eq!(observe_link(&external_link), external_link_before); + BehaviorSnapshot::observe(&churn, revision).unwrap(); + eprintln!("collector published/drained/reused revision {revision}"); + } + let before = BehaviorSnapshot::observe(&churn, 20).unwrap(); + for _ in 0..20 { + drain(&churn); + } + assert_eq!(BehaviorSnapshot::observe(&churn, 20).unwrap(), before); + control.write_revision(20); + install(&control).unwrap(); + drain(&control); + assert_eq!(BehaviorSnapshot::observe(&control, 20).unwrap(), before); +} + +#[test] +fn dc_s5_real_reader_lease_protects_predecessor_until_release() { + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, old_generation, _) = committed(&root); + let store = ManagedStore::new(ManagedLayout::new(storage.clone())); + let reader = store.open_read().unwrap(); + assert_eq!(reader.generation_id, old_generation); + root.write_revision(1); + materialize(&root, "full").unwrap(); + for _ in 0..4 { + collector_attempt(&root); + } + assert!( + actual_generation_ids(&storage).contains(&old_generation), + "live reader's real predecessor was removed" + ); + assert!(reader.db_path.is_file()); + BehaviorSnapshot::observe(&root, 1).unwrap(); + drop(reader); + drain(&root); + assert!(!actual_generation_ids(&storage).contains(&old_generation)); + BehaviorSnapshot::observe(&root, 1).unwrap(); +} + +#[test] +fn dc_s5_publication_durability_failure_retains_real_evidence_and_recovers_without_edit() { + use crate::storage::managed::inject_scoped_managed_sync_fault; + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, old, _) = committed(&root); + // Isolate the new required-recovery journal from a prior completed run; + // optional collection may legitimately consume that earlier terminal root. + prepare_canonical_fixture_registry(&storage, &old); + let layout = ManagedLayout::new(storage); + let store = ManagedStore::new(layout.clone()); + root.write_revision(1); + let fault = inject_scoped_managed_sync_fault(layout.storage_root()).unwrap(); + let error = materialize(&root, "full") + .expect_err("mandatory publication sync failure must not be acknowledged as success"); + assert!( + error.contains("mandatory managed sync"), + "unexpected primary error: {error}" + ); + // Publication reached pointer replacement before mandatory sync failed. + // Reading a pointer is observation, never acknowledgment of durable success. + let active = store.read_active_generation().unwrap().unwrap(); + assert_ne!(active.generation_id, old); + let generation = layout.generation(&active.generation_id).unwrap(); + assert!(!generation.retired_path().exists()); + let mut journals = Vec::new(); + for entry in fs::read_dir(layout.runs_root()).unwrap() { + let path = entry.unwrap().path().join("journal.json"); + if path.is_file() { + let journal: crate::storage::run_workspace::RunJournal = + serde_json::from_slice(&fs::read(&path).unwrap()).unwrap(); + if journal.phase == crate::storage::run_workspace::RunPhase::Publishing + && journal.candidate_generation_id.as_deref() == Some(active.generation_id.as_str()) + { + journals.push(path); + } + } + } + assert!( + !journals.is_empty(), + "failed publication lost its real recovery journal" + ); + let proof_paths = [ + layout.active_pointer_path(), + generation.metadata_path(), + generation.ready_path(), + ]; + let proof = proof_paths + .iter() + .map(|path| fs::read(path).unwrap()) + .collect::>(); + let journal_proof = journals + .iter() + .map(|path| fs::read(path).unwrap()) + .collect::>(); + for _ in 0..2 { + let error = store.cleanup_typed().unwrap_err(); + assert!(error.to_string().contains("mandatory managed sync")); + assert_eq!( + proof_paths + .iter() + .map(|path| fs::read(path).unwrap()) + .collect::>(), + proof + ); + assert_eq!( + journals + .iter() + .map(|path| fs::read(path).unwrap()) + .collect::>(), + journal_proof + ); + assert!(!generation.retired_path().exists()); + } + let recovering_runs = journals + .iter() + .filter_map(|path| { + let journal: crate::storage::run_workspace::RunJournal = + serde_json::from_slice(&fs::read(path).unwrap()).unwrap(); + (journal.phase == crate::storage::run_workspace::RunPhase::Publishing) + .then(|| path.parent().unwrap().to_path_buf()) + }) + .collect::>(); + assert!(!recovering_runs.is_empty()); + for run in &recovering_runs { + assert!( + run.join(".workspace.binding").is_file(), + "must exercise actual registered producer recovery" + ); + } + drop(fault); + // Exercise the production read path (perform_janitor=false), then the real + // bounded collector. Required recovery publishes closure without traversal. + let recovery = store.recover_for_read().unwrap(); + assert_eq!(recovery.recovery.publishing_recovered, 1); + assert!(recovery.janitor.first_issue.is_none(), "{recovery:?}"); + let domain = crate::storage::maintenance_ownership::ReferenceDomain::managed(&layout).unwrap(); + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).unwrap(); + for run in &recovering_runs { + assert!(run.exists(), "read recovery removed retained payload"); + crate::storage::maintenance_controls::prepare_terminal_workspace_boxed( + run, &domain, &memory, + ) + .unwrap(); + } + assert_eq!( + store + .recover_for_read() + .unwrap() + .recovery + .publishing_recovered, + 0, + "owner-terminal recovery must not replay on subsequent read" + ); + assert_eq!( + store + .read_active_generation() + .unwrap() + .unwrap() + .generation_id, + active.generation_id + ); + assert_eq!( + proof_paths + .iter() + .map(|path| fs::read(path).unwrap()) + .collect::>(), + proof + ); + BehaviorSnapshot::observe(&root, 1).unwrap(); + // The failed producer never reached NativeVerifiedGeneration sealing. + // Required read recovery closes its run, but cannot invent successor Origin + // or predecessor FINAL authority. Drain registered runs without requiring + // unrelated generation/cache retirement that must remain protected here. + let old_paths = layout.generation(&old).unwrap(); + let old_origin = fs::read(old_paths.root().join(".generation-origin")).unwrap(); + assert!(!old_paths.root().join(".generation-final").exists()); + assert!(!generation.root().join(".generation-origin").exists()); + let receipts = fs::read_dir(layout.runs_root()) + .unwrap() + .map(|entry| { + fs::read_dir(entry.unwrap().path().join(".workspace-origins")) + .map(|origins| origins.count()) + .unwrap_or(0) + }) + .sum::(); + let mut summary = collector_attempt(&root); + for _ in 0..128 + 16 * receipts { + if fs::read_dir(layout.runs_root()).unwrap().next().is_none() { + break; + } + summary = collector_attempt(&root); + } + assert!( + summary.pending, + "unsealed generations were reported idle: {summary:?}" + ); + assert!(!summary.observation_complete); + assert!( + summary.reasons.iter().any(|reason| matches!( + reason.as_str(), + "unknown_program_origin" | "legacy_quiescence_required" + )), + "unsealed generations lacked a concrete provenance or quiescence blocker: {summary:?}" + ); + assert_eq!( + fs::read(old_paths.root().join(".generation-origin")).unwrap(), + old_origin + ); + for run in recovering_runs { + assert!( + !run.exists(), + "bounded collector retained resolved registered run" + ); + } + assert!(fs::read_dir(layout.runs_root()).unwrap().next().is_none()); + assert_eq!( + actual_generation_ids(layout.storage_root()), + BTreeSet::from([old.clone(), active.generation_id.clone()]) + ); + assert!( + !old_paths.root().join(".generation-final").exists(), + "read recovery or collector invented native-verified retirement authority" + ); + BehaviorSnapshot::observe(&root, 1).unwrap(); +} + +/// Genuine current-producer leaves for both negative provenance checks and the +/// later terminal-workspace test. This does not establish ownership of `path`. +fn emitted_partition(root: &FixtureRoot) -> crate::partition_builder::GraphPartition { + use crate::partition_builder::GraphPartition; + let (storage, _, manifest) = committed(root); + let key = manifest.files["src/lib.rs"].artifact_key.as_ref().unwrap(); + let envelope: Value = serde_json::from_slice( + &fs::read( + storage + .join("artifacts") + .join(&key[..2]) + .join(key) + .join("partition.json"), + ) + .unwrap(), + ) + .unwrap(); + let partition: GraphPartition = serde_json::from_value(envelope["partition"].clone()).unwrap(); + partition.validate_raw_rows().unwrap(); + partition +} + +fn emit_many_staging_leaves(root: &FixtureRoot, path: &Path) -> usize { + emit_staging_partition(&emitted_partition(root), path) +} + +fn emit_staging_partition( + partition: &crate::partition_builder::GraphPartition, + path: &Path, +) -> usize { + use crate::staging_writer::StagingAccumulator; + let mut accumulator = + StagingAccumulator::with_limits(path.to_str().unwrap(), 256 * 1024, 1024).unwrap(); + accumulator.add_partition(partition); + let staged = accumulator.finish().unwrap(); + assert_eq!(staged.unique_node_count, partition.nodes.len()); + assert_eq!(staged.unique_edge_count, partition.edges.len()); + staged.copy_calls +} + +fn observe_staging_files(root: &Path) -> std::collections::BTreeMap { + fn visit( + root: &Path, + path: &Path, + files: &mut std::collections::BTreeMap, + ) { + for entry in fs::read_dir(path).unwrap() { + let entry = entry.unwrap(); + let kind = entry.file_type().unwrap(); + assert!(!kind.is_symlink()); + if kind.is_dir() { + visit(root, &entry.path(), files); + } else { + assert!(kind.is_file()); + files.insert( + entry.path().strip_prefix(root).unwrap().to_owned(), + observe_file(&entry.path()), + ); + } + } + } + let mut files = std::collections::BTreeMap::new(); + visit(root, root, &mut files); + files +} + +#[test] +fn dc_s5_genuine_multileaf_staging_without_terminal_authority_is_preserved() { + let root = FixtureRoot::new(); + root.write_revision(0); + let mut source = fs::read_to_string(root.path().join("src/lib.rs")).unwrap(); + for number in 0..STAGING_FIXTURE_FUNCTIONS { + source.push_str(&format!( + "pub fn staged_{number}() -> usize {{ {number} }}\n" + )); + } + fs::write(root.path().join("src/lib.rs"), source).unwrap(); + install(&root).unwrap(); + let (storage, _, _) = committed(&root); + let staging = storage + .parent() + .unwrap() + .join("native-staging") + .join("unregistered-current-producer"); + fs::create_dir_all(&staging).unwrap(); + assert!( + emit_many_staging_leaves(&root, &staging) > 100, + "fixture did not exercise a multigrant leafset" + ); + let before = observe_staging_files(&staging); + assert!(before.len() > 100); + // Valid current bytes and recognizable names do not create terminal-run + // authority or assert old-helper quiescence. Each actual attempt must retain + // this root; this is a negative test, not a >100-leaf deletion qualification. + for _ in 0..4 { + collector_attempt(&root); + assert_eq!(observe_staging_files(&staging), before); + } + assert_eq!(health(&root).unwrap()["graph_readable"], true); +} + +#[test] +fn dc_s5_former_default_direct_is_retired_and_explicit_rebuild_creates_fresh_incarnation() { + use crate::storage::layout::{DirectLayout, DIRECT_DB_SIDECAR_SUFFIXES}; + use crate::storage::maintenance_retirement::{observe_direct, DirectDisposition}; + let root = FixtureRoot::new(); + root.write_revision(0); + materialize(&root, "full").unwrap(); + let direct_health = health(&root).unwrap(); + assert!( + direct_health["storage_root"].is_null(), + "fixture did not create the unconfigured default Direct destination" + ); + let database = PathBuf::from(direct_health["database_path"].as_str().unwrap()); + let manifest = PathBuf::from(direct_health["manifest_path"].as_str().unwrap()); + let layout = DirectLayout::new(database.clone(), manifest.clone()); + let lock_identity = crate::storage::locks::path_identity(&layout.writer_lock_path()).unwrap(); + assert!(database.starts_with(root.path().join(".codebaseGraph"))); + assert_eq!(observe_direct(&layout).unwrap(), DirectDisposition::Active); + install(&root).unwrap(); + let (storage, _, _) = committed(&root); + assert_ne!( + PathBuf::from(health(&root).unwrap()["database_path"].as_str().unwrap()), + database + ); + let mut retired = false; + for _ in 0..256 { + collector_attempt(&root); + if observe_direct(&layout).unwrap() == DirectDisposition::Retired { + retired = true; + break; + } + } + assert!( + retired, + "proven former default Direct bundle never retired without source edit" + ); + assert!(!database.exists()); + assert!(!manifest.exists()); + for suffix in DIRECT_DB_SIDECAR_SUFFIXES { + assert!(!PathBuf::from(format!("{}.{suffix}", database.display())).exists()); + } + assert_eq!( + crate::storage::locks::path_identity(&layout.writer_lock_path()).unwrap(), + lock_identity + ); + BehaviorSnapshot::observe(&root, 0).unwrap(); + let direct = RepoSelector { + repo_root: Some(root.path().to_owned()), + db_path: Some(database.clone()), + manifest_path: Some(manifest), + ..RepoSelector::default() + }; + let error = execute(OperationRequest::Query(QueryRequest { + repo: direct.clone(), + statement: "MATCH (n:Function) RETURN count(n)".into(), + parameters: json!({}), + limit: 10, + output_format: OutputFormat::Typed, + })) + .unwrap_err(); + assert!( + error.contains("rebuild required"), + "retired destination did not give explicit remediation: {error}" + ); + materialize_selected(direct, "full").unwrap(); + assert_eq!(observe_direct(&layout).unwrap(), DirectDisposition::Active); + assert!(database.is_file()); + assert!(storage.exists()); + BehaviorSnapshot::observe(&root, 0).unwrap(); +} + +#[test] +fn dc_s5_foreign_leaf_stays_at_its_original_path_when_owned_neighbor_is_unreferenced() { + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, _, manifest) = committed(&root); + let key = manifest.files["src/lib.rs"].artifact_key.as_ref().unwrap(); + let directory = storage.join("artifacts").join(&key[..2]).join(key); + let foreign = directory.join("foreign-user-note"); + fs::write(&foreign, b"not part of the artifact producer leafset").unwrap(); + let foreign_before = observe_file(&foreign); + let foreign_identity = crate::storage::locks::path_identity(&foreign).unwrap(); + let directory_identity = crate::storage::locks::path_identity(&directory).unwrap(); + root.write_revision(1); + materialize(&root, "full").unwrap(); + let (_, _, current) = committed(&root); + assert!(!referenced_keys(¤t).contains(key)); + let mut reported_unknown = false; + for _ in 0..8 { + let summary = collector_attempt(&root); + reported_unknown |= summary.reasons.iter().any(|reason| { + matches!( + reason.as_str(), + "unknown_entries_retained" + | "maintenance_candidate_unknown" + | "unknown_program_origin" + ) + }); + assert_eq!(observe_file(&foreign), foreign_before); + assert_eq!( + crate::storage::locks::path_identity(&foreign).unwrap(), + foreign_identity + ); + assert_eq!( + crate::storage::locks::path_identity(&directory).unwrap(), + directory_identity + ); + } + assert!( + reported_unknown, + "foreign state was not reported unknown/incomplete" + ); + // Exact-file retirement may dispose of the proven owned neighbor. The + // unknown sibling and its public directory must stay at their identities; + // this case cannot qualify a clean cache state. + assert!(cache_keys(&storage).contains(&format!("!unclassified-leaf:{key}/foreign-user-note"))); + BehaviorSnapshot::observe(&root, 1).unwrap(); +} + +#[test] +fn dc_s5_artifact_use_lease_defers_obsolete_key_until_release() { + use crate::storage::maintenance_ownership::{ArtifactUseLease, ReferenceDomain}; + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, _, manifest) = committed(&root); + let key = manifest.files["src/lib.rs"].artifact_key.as_ref().unwrap(); + let directory = storage.join("artifacts").join(&key[..2]).join(key); + let payload = directory.join("partition.json"); + let before = observe_file(&payload); + let domain = ReferenceDomain::managed(&ManagedLayout::new(storage.clone())).unwrap(); + let held = ArtifactUseLease::acquire_in_domain(&domain).unwrap(); + root.write_revision(1); + materialize(&root, "full").unwrap(); + let (_, _, successor) = committed(&root); + assert!(!referenced_keys(&successor).contains(key)); + for _ in 0..4 { + let summary = collector_attempt(&root); + assert!( + summary.pending, + "live artifact use was not reported pending: {summary:?}" + ); + assert!( + summary.reasons.iter().any(|reason| matches!( + reason.as_str(), + "reference_in_use" | "maintenance_admission_busy" + )), + "live artifact use lacks a concrete deferral reason: {summary:?}" + ); + assert_eq!(observe_file(&payload), before); + } + drop(held); + drain(&root); + assert!(!directory.exists()); + BehaviorSnapshot::observe(&root, 1).unwrap(); +} + +#[cfg(unix)] +#[test] +fn dc_s5_permission_failure_is_reported_and_retry_without_edit_reclaims_owned_key() { + use crate::storage::maintenance_ownership::{ArtifactUseLease, ReferenceDomain}; + use std::os::unix::fs::PermissionsExt; + // A process with DAC override cannot exercise this permission boundary. + assert_ne!( + unsafe { libc::geteuid() }, + 0, + "run permission qualification as an ordinary user" + ); + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, initial_active, manifest) = committed(&root); + prepare_canonical_fixture_registry(&storage, &initial_active); + let key = manifest.files["src/lib.rs"].artifact_key.as_ref().unwrap(); + let shard = storage.join("artifacts").join(&key[..2]); + let directory = shard.join(key); + let domain = ReferenceDomain::managed(&ManagedLayout::new(storage.clone())).unwrap(); + let held = ArtifactUseLease::acquire_in_domain(&domain).unwrap(); + root.write_revision(1); + materialize(&root, "full").unwrap(); + let active = ManagedStore::new(ManagedLayout::new(storage.clone())) + .read_active_generation() + .unwrap() + .unwrap() + .generation_id; + prepare_canonical_fixture_registry(&storage, &active); + assert!(directory.exists()); + struct RestorePermissions { + path: PathBuf, + mode: u32, + } + impl Drop for RestorePermissions { + fn drop(&mut self) { + fs::set_permissions(&self.path, fs::Permissions::from_mode(self.mode)) + .expect("restore only fixture-owned injected permissions"); + } + } + let restore = RestorePermissions { + path: directory.clone(), + mode: fs::metadata(&directory).unwrap().permissions().mode(), + }; + // Block the payload's first detach, not only the final empty-directory + // rmdir. A shard-only denial permits legitimate payload reclamation first. + fs::set_permissions(&directory, fs::Permissions::from_mode(0o500)).unwrap(); + let denial = fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(directory.join("permission-probe")); + assert_eq!( + denial.unwrap_err().kind(), + std::io::ErrorKind::PermissionDenied, + "fixture must exercise actual OS denial rather than metadata inference" + ); + drop(held); + let payload_before = observe_file(&directory.join("partition.json")); + let payload_identity = + crate::storage::locks::path_identity(&directory.join("partition.json")).unwrap(); + let mut observed = false; + let mut last = None; + // Canonical work is isolated above; allow its real cooperative validation + // and fair mutation turns, each retaining the production grant limits. + for _ in 0..64 { + let summary = collector_attempt_observed(&root); + if summary + .reasons + .iter() + .any(|reason| reason == "permission_denied") + { + assert!(summary.pending); + observed = true; + break; + } + last = Some(summary); + } + assert!( + observed, + "real denied mutation did not report permission_denied: {last:?}" + ); + assert_eq!( + observe_file(&directory.join("partition.json")), + payload_before + ); + assert_eq!( + crate::storage::locks::path_identity(&directory.join("partition.json")).unwrap(), + payload_identity, + "permission denial replaced the owned payload incarnation" + ); + BehaviorSnapshot::observe(&root, 1).unwrap(); + drop(restore); + drain(&root); + assert!(!directory.exists()); + BehaviorSnapshot::observe(&root, 1).unwrap(); +} + +/// Canonical-only preparation. The public producer has returned successfully +/// and every remaining run must prove that same active candidate, a terminal +/// journal, unlocked ownership and an empty real phase registry. Established +/// compatibility cleanup here is fixture preparation, NOT terminal-run GC proof. +fn prepare_canonical_fixture_registry(storage: &Path, active: &str) { + use crate::storage::run_workspace::{RunJournal, RunPhase, RunWorkspace}; + let layout = ManagedLayout::new(storage.to_owned()); + crate::storage::containment::ensure_registry_empty(&layout.phase_workspace_root()).unwrap(); + let runs = fs::read_dir(layout.runs_root()) + .unwrap() + .map(|entry| entry.unwrap().path()) + .collect::>(); + for run in &runs { + let metadata = fs::symlink_metadata(run).unwrap(); + assert!(metadata.file_type().is_dir()); + let journal: RunJournal = + serde_json::from_slice(&fs::read(run.join("journal.json")).unwrap()).unwrap(); + assert_eq!( + run.file_name().unwrap().to_str().unwrap(), + format!("run-{}", journal.run_id) + ); + assert_eq!(journal.candidate_generation_id.as_deref(), Some(active)); + match journal.phase { + RunPhase::Published => { + assert_eq!(journal.active_generation_id.as_deref(), Some(active)); + assert!(journal.last_error.is_none()); + } + RunPhase::CleanupPending => assert!(journal + .last_error + .as_deref() + .is_some_and(|error| error.starts_with("cleanup pending:"))), + phase => panic!("canonical fixture has unresolved run phase: {phase:?}"), + } + crate::storage::containment::ensure_registry_empty(&run.join("phases")).unwrap(); + } + if !runs.is_empty() { + let compatibility = RunWorkspace::cleanup_orphans(layout.runs_root()).unwrap(); + assert_eq!(compatibility.skipped_locked, 0); + assert_eq!(compatibility.publishing_recovered, 0); + assert_eq!(compatibility.deleted, runs.len()); + eprintln!("canonical-only fixture preparation: {} known completed run(s) removed by established compatibility cleanup; this does not qualify terminal GC", compatibility.deleted); + } + assert_eq!(fs::read_dir(layout.runs_root()).unwrap().count(), 0); +} + +fn capture_receipt_diagnostics(root: &FixtureRoot, artifact_root: &Path, key: &str) -> PathBuf { + use crate::storage::maintenance_ownership::{observe_reference_epoch, ReferenceDomain}; + let domain = ReferenceDomain::managed(&ManagedLayout::new( + artifact_root.parent().unwrap().to_owned(), + )); + let epoch = match &domain { + Ok(domain) => format!("{:?}", observe_reference_epoch(domain)), + Err(error) => error.to_string(), + }; + let names = [ + ".use.lock", + ".use-cohort.json", + ".reference-epoch", + ".reference-activity.lock", + ".reference-epoch.lock", + ".publication.binding", + ".publication.selector", + ".publication.data0", + ".publication.data1", + ".publication.stream", + ]; + let mut metadata = serde_json::Map::new(); + for name in names { + let path = artifact_root.join(name); + metadata.insert(name.into(), match fs::read(&path) { + Ok(bytes) => json!({"bytes":bytes.len(),"sha256":format!("{:x}",Sha256::digest(&bytes)),"prefix_hex":bytes.iter().take(32).map(|byte|format!("{byte:02x}")).collect::()}), + Err(error) => json!({"error":error.to_string()}), + }); + } + let receipt = artifact_root + .join(&key[..2]) + .join(key) + .join(".entry-origin"); + let receipt = match fs::read(receipt) { + Ok(bytes) => { + json!({"bytes":bytes.len(),"frames":bytes.chunks(512).map(|frame|json!({"bytes":frame.len(),"phase":frame.get(24),"checksum_valid":frame.len()==512 && frame[480..]==Sha256::digest(&frame[..480])[..]})).collect::>()}) + } + Err(error) => json!({"error":error.to_string()}), + }; + let path = root + .path() + .parent() + .unwrap() + .join("qualification-receipt-diagnostics.json"); + fs::write(&path, serde_json::to_vec_pretty(&json!({"fixture_root":root.path(),"artifact_root":artifact_root,"key":key,"reference_epoch":epoch,"controls":metadata,"receipt":receipt})).unwrap()).unwrap(); + path +} + +struct CanonicalFixture { + root: Option, + artifact_root: PathBuf, + key: String, + directory: PathBuf, + payload: FileObservation, + origin: Vec, + database: PathBuf, + database_before: FileObservation, + manifest: PathBuf, + manifest_before: Vec, + behavior: BehaviorSnapshot, + completed: bool, +} +impl CanonicalFixture { + fn new() -> Self { + use crate::artifact_store::{ + artifact_key, ArtifactKeyInput, ArtifactStore, ArtifactWriteOutcome, + }; + use crate::partition_builder::GraphPartition; + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, active, manifest_data) = committed(&root); + assert_eq!( + actual_generation_ids(&storage), + BTreeSet::from([active.clone()]) + ); + prepare_canonical_fixture_registry(&storage, &active); + let behavior = BehaviorSnapshot::observe(&root, 0).unwrap(); + let health = health(&root).unwrap(); + let database = fs::canonicalize(health["database_path"].as_str().unwrap()).unwrap(); + let manifest = fs::canonicalize(health["manifest_path"].as_str().unwrap()).unwrap(); + let database_before = observe_file(&database); + let manifest_before = fs::read(&manifest).unwrap(); + let artifact_root = fs::canonicalize(storage.join("artifacts")).unwrap(); + let source_key = manifest_data.files["src/lib.rs"] + .artifact_key + .as_ref() + .unwrap(); + let envelope: Value = serde_json::from_slice( + &fs::read( + artifact_root + .join(&source_key[..2]) + .join(source_key) + .join("partition.json"), + ) + .unwrap(), + ) + .unwrap(); + let mut partition: GraphPartition = + serde_json::from_value(envelope["partition"].clone()).unwrap(); + // The real key builder and real artifact producer are used. A distinct + // parser-version input makes an extra valid cache entry; no graph or + // generation manifest is edited to introduce/remove a reference. + let parser_version = format!( + "{}-canonical-qualification-spare", + manifest_data.parser_version + ); + let ontology_schema = crate::protocol::OntologySchema::default(); + let key = artifact_key(&ArtifactKeyInput { + source_root: root.path().to_str().unwrap(), + repository_label: "repository", + relative_path: &partition.entry.path, + content_hash: &partition.entry.content_hash, + language: &partition.entry.language, + parser_version: &parser_version, + profile: None, + ontology: &manifest_data.ontology, + ontology_schema: &ontology_schema, + }) + .unwrap(); + assert!(!referenced_keys(&manifest_data).contains(&key)); + partition.set_artifact_key(key.clone()); + assert_eq!( + ArtifactStore::new(artifact_root.clone()) + .store_partition(&key, &partition) + .unwrap(), + ArtifactWriteOutcome::Written + ); + let directory = artifact_root.join(&key[..2]).join(&key); + if !active_origin_matches_payload(&directory, &key) { + let diagnostics = capture_receipt_diagnostics(&root, &artifact_root, &key); + std::mem::forget(root); + panic!( + "normal producer did not emit a complete bound receipt; retained diagnostics: {}", + diagnostics.display() + ); + } + let payload = observe_file(&directory.join("partition.json")); + let origin = fs::read(directory.join(".entry-origin")).unwrap(); + assert_eq!(fs::read(&manifest).unwrap(), manifest_before); + Self { + root: Some(root), + artifact_root, + key, + directory, + payload, + origin, + database, + database_before, + manifest, + manifest_before, + behavior, + completed: false, + } + } + fn root(&self) -> &FixtureRoot { + self.root.as_ref().unwrap() + } + fn assert_active_unchanged_without_runtime_entry(&self) { + assert_eq!(observe_file(&self.database), self.database_before); + assert_eq!(fs::read(&self.manifest).unwrap(), self.manifest_before); + } + fn finish(&mut self) { + let current: NativeManifest = + serde_json::from_slice(&fs::read(&self.manifest).unwrap()).unwrap(); + assert!(!referenced_keys(¤t).contains(&self.key)); + assert!( + !self.directory.exists(), + "actual unreferenced key directory survived completed retirement" + ); + assert!(retired_payloads(&self.artifact_root).is_empty()); + self.assert_active_unchanged_without_runtime_entry(); + drain(self.root()); + assert_eq!( + BehaviorSnapshot::observe(self.root(), 0).unwrap(), + self.behavior + ); + self.completed = true; + } +} +impl Drop for CanonicalFixture { + fn drop(&mut self) { + if !self.completed { + if let Some(root) = self.root.take() { + eprintln!( + "retained failed canonical qualification fixture: {}", + root.path().display() + ); + std::mem::forget(root); + } + } + } +} + +fn retired_payloads(root: &Path) -> Vec { + let mut paths = Vec::new(); + for entry in fs::read_dir(root).unwrap() { + let entry = entry.unwrap(); + let name = entry.file_name().into_string().unwrap(); + if let Some(nonce) = name.strip_prefix(".retired-") { + assert_eq!(nonce.len(), 64); + assert!(nonce + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))); + assert!(entry.file_type().unwrap().is_file()); + paths.push(entry.path()); + } + } + paths.sort(); + paths +} + +#[test] +fn dc_s5_canonical_only_real_producer_spare_key_is_collected_without_source_edit() { + let mut fixture = CanonicalFixture::new(); + let mut charged_mutation = false; + for _ in 0..128 { + let summary = collector_attempt(fixture.root()); + charged_mutation |= summary + .last_attempt + .as_ref() + .and_then(|attempt| attempt.attempted_entries) + .is_some_and(|entries| entries > 0); + if !fixture.directory.exists() && retired_payloads(&fixture.artifact_root).is_empty() { + break; + } + } + assert!( + charged_mutation, + "actual canonical reclamation lacks a recorded mutation grant" + ); + fixture.finish(); +} + +fn canonical_fault_resume(point: crate::storage::maintenance_candidate::CandidateFault) { + use crate::storage::maintenance_candidate::{arm_candidate_fault, CandidateFault}; + let mut fixture = CanonicalFixture::new(); + let guard = arm_candidate_fault(&fixture.artifact_root, point).unwrap(); + let mut last = None; + for _ in 0..128 { + last = Some(collector_attempt_observed(fixture.root())); + if guard.reached() { + break; + } + } + assert!( + guard.reached(), + "real transition hook was never reached: {last:?}" + ); + assert!( + last.as_ref() + .unwrap() + .last_attempt + .as_ref() + .and_then(|attempt| attempt.attempted_entries) + .is_some_and(|entries| entries > 0), + "reached mutation fault lacks recorded charged grant: {last:?}" + ); + fixture.assert_active_unchanged_without_runtime_entry(); + assert!(!fixture.directory.join("partition.json").exists()); + assert_eq!( + fs::read(fixture.directory.join(".entry-origin")).unwrap(), + fixture.origin + ); + let private = retired_payloads(&fixture.artifact_root); + match point { + CandidateFault::AfterDetach => { + assert_eq!( + private.len(), + 1, + "durable detach did not retain its actual private payload" + ); + assert_eq!(observe_file(&private[0]).digest, fixture.payload.digest); + } + CandidateFault::AfterPayloadUnlink => assert!( + private.is_empty(), + "actual unlink boundary still has a private payload" + ), + CandidateFault::DuringPreparation => { + unreachable!("preparation uses its dedicated cancellation fixture") + } + } + let stream = fixture.artifact_root.join(".publication.stream"); + assert!( + fs::metadata(&stream).unwrap().len() >= 512, + "interruption lost the real retirement journal" + ); + assert!( + last.as_ref().unwrap().pending, + "interrupted retirement was not reported pending" + ); + drop(guard); + // Each call constructs a fresh actual controller/admission pair; no source + // writes, fake receipts or publication of another generation drive recovery. + for _ in 0..128 { + collector_attempt(fixture.root()); + if !fixture.directory.exists() && retired_payloads(&fixture.artifact_root).is_empty() { + break; + } + } + fixture.finish(); +} + +#[test] +fn dc_s5_canonical_only_durable_detach_interruption_resumes_without_source_edit() { + canonical_fault_resume(crate::storage::maintenance_candidate::CandidateFault::AfterDetach); +} +#[test] +fn dc_s5_canonical_only_payload_unlink_interruption_resumes_without_source_edit() { + canonical_fault_resume( + crate::storage::maintenance_candidate::CandidateFault::AfterPayloadUnlink, + ); +} + +fn all_function_rows(root: &FixtureRoot) -> Vec { + let query = execute(OperationRequest::Query(QueryRequest { + repo: selector(root), statement: "MATCH (n:Function) RETURN n.label, n.path, n.line_start, n.line_end, n.summary ORDER BY n.path, n.line_start, n.label".into(), + parameters: json!({}), limit: 1000, output_format: OutputFormat::Typed, + })).unwrap(); + assert_eq!(query["truncated"], false); + let rows = query["rows"].as_array().unwrap().clone(); + let mut actual = rows + .iter() + .map(|row| row[0].as_str().unwrap().to_owned()) + .collect::>(); + actual.sort(); + let mut expected = (0..STAGING_FIXTURE_FUNCTIONS) + .map(|number| format!("staged_{number}")) + .collect::>(); + expected.extend(["revision_0".into(), "stable_helper".into()]); + expected.sort(); + assert_eq!(actual, expected); + rows +} + +fn staging_output_count(root: &Path) -> usize { + let mut count = 0; + for entry in fs::read_dir(root).unwrap() { + let entry = entry.unwrap(); + let kind = entry.file_type().unwrap(); + assert!(!kind.is_symlink()); + if kind.is_dir() { + count += staging_output_count(&entry.path()); + } else { + assert!(kind.is_file()); + count += 1; + } + } + count +} + +fn observed_directory_paths(root: &Path) -> std::collections::BTreeMap<(u64, u64), PathBuf> { + fn visit(path: &Path, output: &mut std::collections::BTreeMap<(u64, u64), PathBuf>) { + let identity = crate::storage::locks::path_identity(path) + .unwrap() + .fixed_words(); + assert!( + output.insert(identity, path.to_owned()).is_none(), + "directory identity alias in owned fixture" + ); + for entry in fs::read_dir(path).unwrap() { + let entry = entry.unwrap(); + let kind = entry.file_type().unwrap(); + assert!(!kind.is_symlink()); + if kind.is_dir() { + visit(&entry.path(), output); + } + } + } + let mut output = std::collections::BTreeMap::new(); + visit(root, &mut output); + output +} + +#[test] +fn dc_s5_real_terminal_multigrant_outputs_resume_across_controller_restarts_without_edit() { + use crate::storage::maintenance_origin::{ + decode_workspace_output_origin, WorkspaceOriginPhase, WORKSPACE_ORIGIN_FRAME_BYTES, + }; + use crate::storage::maintenance_ownership::ReferenceDomain; + use crate::storage::run_workspace::RunWorkspace; + let root = FixtureRoot::new(); + root.write_revision(0); + let mut source = fs::read_to_string(root.path().join("src/lib.rs")).unwrap(); + for number in 0..STAGING_FIXTURE_FUNCTIONS { + source.push_str(&format!( + "pub fn staged_{number}() -> usize {{ {number} }}\n" + )); + } + fs::write(root.path().join("src/lib.rs"), source).unwrap(); + install(&root).unwrap(); + let (storage, _, _) = committed(&root); + let before = all_function_rows(&root); + let partition = emitted_partition(&root); + let layout = ManagedLayout::new(storage.clone()); + let domain = ReferenceDomain::managed(&layout).unwrap(); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let run_root = run.root().to_owned(); + let staging = run.staging_root(); + let copy_calls = emit_staging_partition(&partition, &staging); + let payloads = observe_staging_files(&staging); + assert!( + copy_calls > 100 && payloads.len() > 100, + "real producer did not create a multigrant fixture" + ); + let registry = run_root.join(".workspace-origins"); + let registry_id = crate::storage::locks::path_identity(®istry).unwrap(); + let root_id = crate::storage::locks::path_identity(&run_root).unwrap(); + let directory_paths = observed_directory_paths(&run_root); + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).unwrap(); + let mut registered = BTreeSet::new(); + let mut registered_directories = BTreeSet::new(); + let mut already_removed = 0; + for entry in fs::read_dir(®istry).unwrap() { + let entry = entry.unwrap(); + assert!(entry.file_type().unwrap().is_file()); + let bytes = fs::read(entry.path()).unwrap(); + assert!(bytes.len() >= WORKSPACE_ORIGIN_FRAME_BYTES); + let receipt_id = crate::storage::locks::path_identity(&entry.path()).unwrap(); + let bound = decode_workspace_output_origin( + &bytes[..WORKSPACE_ORIGIN_FRAME_BYTES], + receipt_id, + None, + &memory, + ) + .unwrap(); + assert_eq!(bound.binding.root, root_id); + assert_eq!(bound.binding.registry, registry_id); + let parent = directory_paths + .get(&bound.binding.parent.fixed_words()) + .expect("real receipt parent disappeared before terminal event"); + let name = + std::str::from_utf8(&bound.binding.name[..bound.binding.name_len as usize]).unwrap(); + let output_path = parent.join(name); + if bound.fifo { + // A FIFO has a genuine creation receipt, but no readable regular + // payload or Complete event. Never open it for a digest here. + assert!(!bound.directory); + assert_eq!(bytes.len(), WORKSPACE_ORIGIN_FRAME_BYTES); + #[cfg(unix)] + { + use std::os::unix::fs::FileTypeExt; + match fs::symlink_metadata(&output_path) { + Ok(metadata) => { + assert!(metadata.file_type().is_fifo()); + assert_eq!( + crate::storage::locks::path_identity(&output_path).unwrap(), + bound.binding.output + ); + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + // The real FIFO producer may already have consumed it. + } + Err(error) => panic!("observe actual FIFO output: {error}"), + } + } + #[cfg(not(unix))] + panic!("FIFO receipt on a platform without FIFO producer support"); + } else if bound.directory { + assert_eq!(bytes.len(), WORKSPACE_ORIGIN_FRAME_BYTES); + assert!(output_path.is_dir()); + assert_eq!( + crate::storage::locks::path_identity(&output_path).unwrap(), + bound.binding.output + ); + assert!(registered_directories.insert(bound.binding.output.fixed_words())); + } else { + if output_path.exists() { + assert_eq!( + bytes.len(), + 2 * WORKSPACE_ORIGIN_FRAME_BYTES, + "surviving real file output lacks Complete event" + ); + let relative = output_path + .strip_prefix(&staging) + .expect("actual staging output escaped staging tree") + .to_owned(); + let payload = payloads + .get(&relative) + .expect("receipt does not match a surviving staging output"); + let metadata = fs::metadata(&output_path).unwrap(); + let fact = decode_workspace_output_origin( + &bytes, + receipt_id, + Some((metadata.len(), payload.digest)), + &memory, + ) + .unwrap(); + assert_eq!(fact.phase, WorkspaceOriginPhase::Complete); + assert!(!fact.directory); + assert_eq!( + fact.binding.output, + crate::storage::locks::path_identity(&output_path).unwrap() + ); + assert!(registered.insert(relative)); + } else { + // The actual spill producer can remove consumed input files. + // Observe their completion framing/binding, without inventing + // integrity evidence for bytes that are no longer present. + if bytes.len() == 2 * WORKSPACE_ORIGIN_FRAME_BYTES { + let completion = &bytes[WORKSPACE_ORIGIN_FRAME_BYTES..]; + assert_eq!(&completion[..8], b"CGWOUT01"); + assert_eq!(completion[8], 1); + assert_eq!(completion[9..208], bytes[9..208]); + assert_eq!(completion[248..992], bytes[248..992]); + assert_eq!(completion[992..], Sha256::digest(&completion[..992])[..]); + } else { + assert_eq!(bytes.len(), WORKSPACE_ORIGIN_FRAME_BYTES); + let file = fs::File::open(entry.path()).unwrap(); + assert_eq!( + crate::storage::locks::created_file_nonce(&file).unwrap(), + Some( + crate::storage::maintenance_controls::workspace_receipt_nonce( + bound.binding.incarnation, + bound.binding.parent, + bound.binding.output, + bound.binding.operation + ) + ) + ); + } + already_removed += 1; + } + } + } + assert_eq!( + registered, + payloads.keys().cloned().collect::>() + ); + for (identity, path) in &directory_paths { + if path.starts_with(&staging) && path != &staging { + assert!( + registered_directories.contains(identity), + "actual spill directory has no genuine creation receipt: {}", + path.display() + ); + } + } + eprintln!("terminal fixture: {} surviving tracked payloads, {} actual directory creation receipts, {already_removed} receipts for producer-consumed inputs", registered.len(), registered_directories.len()); + drop(memory); + let terminal = run.retire_for_maintenance().unwrap(); + assert_eq!(terminal, run_root); + // No compatibility deletion is called for this test. Every observed removal + // must come from the real bounded terminal lane and its durable continuation. + let mut previous = payloads.len(); + let mut saw_partial_progress = false; + let mut last = None; + let attempt_limit = payloads + .len() + .checked_mul(32) + .and_then(|count| count.checked_add(512)) + .expect("bounded fixture attempt count"); + for attempt in 0..attempt_limit { + last = Some(collector_attempt(&root)); + let remaining = if staging.exists() { + staging_output_count(&staging) + } else { + 0 + }; + assert!(remaining <= previous, "collector recreated terminal output"); + if remaining < previous { + assert!( + last.as_ref() + .unwrap() + .last_attempt + .as_ref() + .and_then(|attempt| attempt.attempted_entries) + .is_some_and(|entries| entries > 0), + "output disappeared without a recorded actual mutation grant" + ); + } + if remaining < previous && !saw_partial_progress { + assert!( + remaining > 0, + "more than100 output leaves disappeared in the first mutation grant" + ); + saw_partial_progress = true; + } + previous = remaining; + if !terminal.exists() { + break; + } + if attempt % 64 == 0 { + eprintln!( + "terminal continuation attempt {attempt}: {remaining}/{} producer outputs remain", + payloads.len() + ); + } + } + assert!( + saw_partial_progress, + "actual multigrant output progress was never observed: {last:?}" + ); + assert!( + !terminal.exists(), + "real terminal root did not converge across controller restarts: {last:?}" + ); + assert_eq!(all_function_rows(&root), before); + drain(&root); +} + +#[cfg(unix)] +#[test] +fn dc_s5_real_containment_fifo_retires_with_pin_receipt_and_terminal_root() { + use crate::process_lifecycle::{OutputMode, PreparedChild, SpawnSpec}; + use crate::storage::maintenance_origin::{ + decode_workspace_output_origin, WORKSPACE_ORIGIN_FRAME_BYTES, + }; + use crate::storage::maintenance_ownership::ReferenceDomain; + use crate::storage::run_workspace::RunWorkspace; + use std::os::unix::fs::FileTypeExt; + + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, _, _) = committed(&root); + let before = BehaviorSnapshot::observe(&root, 0).unwrap(); + let layout = ManagedLayout::new(storage); + let domain = ReferenceDomain::managed(&layout).unwrap(); + let run = RunWorkspace::create_scoped(layout.runs_root(), &domain, None).unwrap(); + let run_root = run.root().to_owned(); + let executable = std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .map(PathBuf::from) + .expect("real containment qualification requires the captured shipping executable"); + let mut child = PreparedChild::prepare(SpawnSpec { + program: PathBuf::from("/bin/sh"), + guardian_executable: Some(executable), + args: vec!["-c".into(), "exit 0".into()], + workspace: run_root.clone(), + stderr_path: run_root.join("containment.stderr"), + stdout: OutputMode::Null, + parent_rss_pid: None, + }) + .unwrap(); + let fifo = child.descriptor().lifecycle_dir.join("writers.fifo"); + assert!(fs::symlink_metadata(&fifo).unwrap().file_type().is_fifo()); + let fifo_identity = crate::storage::locks::path_identity(&fifo).unwrap(); + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).unwrap(); + let registry = run_root.join(".workspace-origins"); + let mut bound_fifo = None; + for entry in fs::read_dir(®istry).unwrap() { + let entry = entry.unwrap(); + if entry.file_name().as_encoded_bytes().starts_with(b"fifo-") { + continue; + } + let bytes = fs::read(entry.path()).unwrap(); + if bytes.len() < WORKSPACE_ORIGIN_FRAME_BYTES { + continue; + } + let receipt_identity = crate::storage::locks::path_identity(&entry.path()).unwrap(); + let fact = decode_workspace_output_origin( + &bytes[..WORKSPACE_ORIGIN_FRAME_BYTES], + receipt_identity, + None, + &memory, + ) + .unwrap(); + if fact.fifo && fact.binding.output == fifo_identity { + assert_eq!(bytes.len(), WORKSPACE_ORIGIN_FRAME_BYTES); + let pin = registry.join( + std::str::from_utf8( + &crate::storage::maintenance_controls::workspace_fifo_pin_name(&fact.binding), + ) + .unwrap(), + ); + assert!(fs::symlink_metadata(&pin).unwrap().file_type().is_fifo()); + assert_eq!( + crate::storage::locks::path_identity(&pin).unwrap(), + fifo_identity + ); + assert!(bound_fifo.replace((entry.path(), pin)).is_none()); + } + } + let (receipt, pin) = bound_fifo.expect("real containment producer lacks its bound FIFO origin"); + drop(memory); + child.start().unwrap(); + let outcome = child.wait().unwrap(); + assert!(outcome.success && outcome.containment_empty, "{outcome:?}"); + drop(child); + assert_eq!(run.retire_for_maintenance().unwrap(), run_root); + let mut charged_mutation = false; + let mut last = None; + for _ in 0..4096 { + let summary = collector_attempt(&root); + charged_mutation |= summary + .last_attempt + .as_ref() + .and_then(|attempt| attempt.attempted_entries) + .is_some_and(|entries| entries > 0); + last = Some(summary); + if !run_root.exists() { + break; + } + } + assert!( + charged_mutation, + "actual FIFO retirement lacks a mutation grant" + ); + assert!(!fifo.exists(), "public FIFO survived: {last:?}"); + assert!(!pin.exists(), "private FIFO pin survived: {last:?}"); + assert!(!receipt.exists(), "FIFO receipt survived: {last:?}"); + assert!(!run_root.exists(), "terminal root survived: {last:?}"); + assert_eq!(BehaviorSnapshot::observe(&root, 0).unwrap(), before); +} + +#[test] +fn dc_s5_real_producer_prepared_temporary_is_collected_without_source_edit() { + use crate::artifact_store::{leave_prepared_temporary_for_test, ArtifactStore}; + let mut fixture = CanonicalFixture::new(); + let mut partition = emitted_partition(fixture.root()); + partition.set_artifact_key(fixture.key.clone()); + leave_prepared_temporary_for_test( + &ArtifactStore::new(fixture.artifact_root.clone()), + &fixture.key, + &partition, + ) + .unwrap(); + let prefix = format!(".tmp-{}-", fixture.key); + let temporaries = fs::read_dir(&fixture.artifact_root) + .unwrap() + .map(|entry| entry.unwrap()) + .filter(|entry| entry.file_name().to_str().unwrap().starts_with(&prefix)) + .collect::>(); + assert_eq!( + temporaries.len(), + 1, + "real prepared producer hook did not leave exactly its temporary" + ); + let temporary = temporaries[0].path(); + assert!(temporaries[0].file_type().unwrap().is_dir()); + let receipt = fs::read(temporary.join(".entry-origin")).unwrap(); + assert_eq!( + receipt.len(), + 3 * 512, + "prepared interruption lacks real Complete origin" + ); + for (phase, frame) in receipt.chunks_exact(512).enumerate() { + assert_eq!(&frame[..8], b"CGORIG01"); + assert_eq!(frame[24], phase as u8); + assert_eq!(frame[480..], Sha256::digest(&frame[..480])[..]); + } + let payload = observe_file(&temporary.join("partition.json")); + assert_eq!(payload.digest, receipt[2 * 512 + 264..2 * 512 + 296]); + let mut charged_mutation = false; + for _ in 0..256 { + let summary = collector_attempt(fixture.root()); + charged_mutation |= summary + .last_attempt + .as_ref() + .and_then(|attempt| attempt.attempted_entries) + .is_some_and(|entries| entries > 0); + if !temporary.exists() + && !fixture.directory.exists() + && retired_payloads(&fixture.artifact_root).is_empty() + { + break; + } + } + assert!(charged_mutation); + assert!( + !temporary.exists(), + "real prepared temporary survived no-edit collection" + ); + fixture.finish(); +} + +struct ReferencedBackupFixture { + root: FixtureRoot, + canonical: PathBuf, + backup: PathBuf, + payload: FileObservation, + payload_identity: crate::storage::locks::FileIdentity, + behavior: BehaviorSnapshot, +} +impl ReferencedBackupFixture { + fn new() -> Self { + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, active, manifest) = committed(&root); + // Initial fixture preparation uses the established terminal-run + // recovery path; this case qualifies backup restoration. Dedicated + // terminal/churn cases qualify automatic workspace convergence. + prepare_canonical_fixture_registry(&storage, &active); + let behavior = BehaviorSnapshot::observe(&root, 0).unwrap(); + let key = referenced_keys(&manifest).into_iter().next().unwrap(); + let shard = storage.join("artifacts").join(&key[..2]); + let canonical = shard.join(&key); + let backup = shard.join(format!(".replaced-{key}-{}", u128::MAX)); + let payload = observe_file(&canonical.join("partition.json")); + let payload_identity = + crate::storage::locks::path_identity(&canonical.join("partition.json")).unwrap(); + // Represent the historical replacement interruption using actual + // producer bytes and inode. No ownership or association fact is minted. + fs::rename(&canonical, &backup).unwrap(); + assert!(!canonical.exists()); + assert_eq!(observe_file(&backup.join("partition.json")), payload); + Self { + root, + canonical, + backup, + payload, + payload_identity, + behavior, + } + } +} + +fn backup_control_stage(root: &FixtureRoot) -> Option { + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).unwrap(); + let target = RepositoryIdentity::capture(&selector(root)) + .unwrap() + .maintenance_target(&memory) + .unwrap(); + crate::storage::maintenance_backup::backup_control_stage_for_test(target.target(), &memory) + .unwrap() +} + +#[test] +fn dc_s5_real_referenced_historical_backup_restores_missing_canonical_without_edit() { + let fixture = ReferencedBackupFixture::new(); + let mut last = None; + let mut charged_mutation = false; + let mut saw_restored_publication = false; + let mut completed = false; + for _ in 0..4096 { + let summary = collector_attempt(&fixture.root); + charged_mutation |= summary + .last_attempt + .as_ref() + .and_then(|attempt| attempt.attempted_entries) + .is_some_and(|entries| entries > 0); + last = Some(summary); + let stage = backup_control_stage(&fixture.root); + saw_restored_publication |= stage == Some(4); + if saw_restored_publication && stage == Some(0) { + completed = true; + break; + } + } + assert!(charged_mutation, "restore lacks actual mutation grant"); + assert!( + completed, + "real restore publication did not complete: {last:?}" + ); + assert!( + fixture.canonical.join("partition.json").exists(), + "referenced backup did not restore: {last:?}" + ); + assert_eq!( + observe_file(&fixture.canonical.join("partition.json")), + fixture.payload + ); + assert_eq!( + crate::storage::locks::path_identity(&fixture.canonical.join("partition.json")).unwrap(), + fixture.payload_identity + ); + assert_eq!( + BehaviorSnapshot::observe(&fixture.root, 0).unwrap(), + fixture.behavior + ); +} + +#[test] +fn dc_s5_real_referenced_historical_backup_preserves_foreign_canonical() { + let fixture = ReferencedBackupFixture::new(); + fs::create_dir(&fixture.canonical).unwrap(); + let foreign = fixture.canonical.join("foreign-note"); + fs::write( + &foreign, + b"foreign canonical incarnation must not be adopted", + ) + .unwrap(); + let foreign_before = observe_file(&foreign); + let foreign_identity = crate::storage::locks::path_identity(&foreign).unwrap(); + for _ in 0..64 { + collector_attempt(&fixture.root); + assert_eq!(observe_file(&foreign), foreign_before); + assert_eq!( + crate::storage::locks::path_identity(&foreign).unwrap(), + foreign_identity + ); + assert_eq!( + observe_file(&fixture.backup.join("partition.json")), + fixture.payload + ); + assert!(!fixture.canonical.join("partition.json").exists()); + } + assert_eq!( + BehaviorSnapshot::observe(&fixture.root, 0).unwrap(), + fixture.behavior + ); +} + +fn referenced_backup_fault_resume(point: crate::storage::maintenance_backup::BackupFault) { + use crate::storage::maintenance_backup::{arm_backup_fault, BackupFault}; + let fixture = ReferencedBackupFixture::new(); + let artifact_root = fixture.backup.parent().unwrap().parent().unwrap(); + let guard = arm_backup_fault( + crate::storage::locks::path_identity(artifact_root).unwrap(), + point, + ); + let mut last = None; + for _ in 0..4096 { + last = Some(collector_attempt_observed(&fixture.root)); + if guard.reached() { + break; + } + } + assert!( + guard.reached(), + "real backup effect cut was not reached: {last:?}" + ); + assert!(fixture.canonical.join(".restore-origin").is_file()); + let created = crate::storage::locks::path_identity(&fixture.canonical).unwrap(); + match point { + BackupFault::AfterCreatorSync => { + assert!(!fixture.canonical.join("partition.json").exists()); + assert_eq!( + observe_file(&fixture.backup.join("partition.json")), + fixture.payload + ); + } + BackupFault::AfterPayloadRestore => { + assert!(!fixture.backup.join("partition.json").exists()); + assert_eq!( + observe_file(&fixture.canonical.join("partition.json")), + fixture.payload + ); + } + } + drop(guard); + let mut saw_restored_publication = false; + let mut completed = false; + for _ in 0..4096 { + last = Some(collector_attempt(&fixture.root)); + let stage = backup_control_stage(&fixture.root); + saw_restored_publication |= stage == Some(4); + if saw_restored_publication && stage == Some(0) { + completed = true; + break; + } + } + assert!( + completed, + "interrupted restore publication did not complete: {last:?}" + ); + assert!( + fixture.canonical.join("partition.json").exists(), + "backup cut did not recover: {last:?}" + ); + assert_eq!( + crate::storage::locks::path_identity(&fixture.canonical).unwrap(), + created + ); + assert_eq!( + observe_file(&fixture.canonical.join("partition.json")), + fixture.payload + ); + assert_eq!( + crate::storage::locks::path_identity(&fixture.canonical.join("partition.json")).unwrap(), + fixture.payload_identity + ); + assert_eq!( + BehaviorSnapshot::observe(&fixture.root, 0).unwrap(), + fixture.behavior + ); +} + +#[test] +fn dc_s5_real_referenced_backup_creator_sync_interruption_resumes_without_edit() { + referenced_backup_fault_resume( + crate::storage::maintenance_backup::BackupFault::AfterCreatorSync, + ); +} + +#[test] +fn dc_s5_real_referenced_backup_payload_restore_interruption_resumes_without_edit() { + referenced_backup_fault_resume( + crate::storage::maintenance_backup::BackupFault::AfterPayloadRestore, + ); +} + +fn wait_for_real_preparation( + guard: &crate::storage::maintenance_candidate::CandidateFaultGuard, + owner: &crate::coordinator::CoordinatorClient, + root: &FixtureRoot, +) { + // Startup refresh may legitimately hold reference activity during the + // first advisory pass. Allow its unchanged 60-second continuation cooldown + // plus preparation time before declaring the real hook unreachable. + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(75); + while !guard.reached() { + assert!( + std::time::Instant::now() < deadline, + "actual advisory preparation never reached its root-scoped hook: {}", + coordinator_health(owner, root) + ); + std::thread::sleep(std::time::Duration::from_millis(5)); + } +} + +/// Exercise the actual stdio parser, session and tool transport, which connects +/// to the already held real coordinator owner. This does not construct a second +/// maintenance runtime or a substitute dispatcher. +fn query_through_same_owner_mcp(root: &FixtureRoot, revision: usize) { + let path = root.path().to_owned(); + let (sender, receiver) = std::sync::mpsc::channel(); + let join = std::thread::spawn(move || { + let result = + std::panic::catch_unwind(|| query_through_same_owner_mcp_path(&path, revision)); + let _ = sender.send(result.is_ok()); + }); + let completed = receiver + .recv_timeout(std::time::Duration::from_secs(5)) + .expect( + "actual same-owner MCP query did not progress while advisory preparation was paused", + ); + join.join().unwrap(); + assert!(completed, "actual MCP protocol assertions failed"); +} + +fn query_through_same_owner_mcp_path(root: &Path, revision: usize) { + use crate::adapters::mcp::{serve_mcp_stdio, McpServeOptions}; + let storage = root.join(".codebaseGraph/storage"); + let coordinator_before = fs::read(storage.join("coordinator.json")).unwrap(); + let options = McpServeOptions::parse( + &[ + "--repo-root".into(), + root.to_str().unwrap().into(), + "--refresh-policy".into(), + "off".into(), + ], + "DC-S5 real stdio transport fixture", + ) + .unwrap(); + let messages = [ + json!({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"dc-s5-preparation","version":"1"}}}), + json!({"jsonrpc":"2.0","method":"notifications/initialized","params":{}}), + json!({"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"graph_query","arguments":{"statement":"MATCH (n:Function) RETURN n.label ORDER BY n.label","limit":100,"include_structured_content":true}}}), + ]; + let input = messages + .iter() + .map(|message| format!("{message}\n")) + .collect::(); + let mut output = Vec::new(); + serve_mcp_stdio( + &options, + std::io::Cursor::new(input.into_bytes()), + &mut output, + ) + .unwrap(); + let replies = std::str::from_utf8(&output) + .unwrap() + .lines() + .map(|line| serde_json::from_str::(line).unwrap()) + .collect::>(); + let response = replies + .iter() + .find(|response| response["id"] == 2) + .expect("actual MCP query response missing"); + assert_eq!( + response["result"]["isError"], false, + "actual MCP query stalled/failed during advisory preparation: {response}" + ); + let query = &response["result"]["structuredContent"]; + assert_eq!(query["truncated"], false); + let actual = query["rows"] + .as_array() + .unwrap() + .iter() + .map(|row| row[0].as_str().unwrap().to_owned()) + .collect::>(); + let mut expected = vec![format!("revision_{revision}"), "stable_helper".into()]; + expected.sort(); + assert_eq!(actual, expected); + assert_eq!( + fs::read(storage.join("coordinator.json")).unwrap(), + coordinator_before, + "MCP used a replacement owner rather than the held dispatcher" + ); +} + +fn coordinator_health(client: &crate::coordinator::CoordinatorClient, root: &FixtureRoot) -> Value { + client + .execute_invocation_with_context( + "health", + &crate::api::OperationInvocation { + repo: selector(root), + arguments: json!({}), + output_format: OutputFormat::Typed, + }, + crate::api::ExecutionContext::with_timeout( + std::time::Instant::now(), + std::time::Duration::from_secs(5), + ), + ) + .unwrap() + .payload +} + +fn create_genuine_future_cache(root: &FixtureRoot, storage: &Path) -> (String, String) { + use crate::api::{build_materialization_request_for_test as build_request, MaterializeOptions}; + use crate::artifact_store::{ArtifactStore, ArtifactWriteOutcome}; + let request = build_request(&MaterializeOptions { + source_root: Some(root.path().to_owned()), + storage_root: Some(storage.to_owned()), + config: Some(root.path().join(".codebaseGraph/config.json")), + mode: "full".into(), + use_git: false, + parallel: false, + max_parallelism: Some(1), + ..MaterializeOptions::default() + }) + .unwrap(); + let source = "mod stable;\npub use stable::stable_helper;\npub fn revision_1() -> usize { stable_helper() + 1 }\n".to_owned(); + let snapshot = crate::protocol::SourceSnapshot { + path: "src/lib.rs".into(), + absolute_path: root.path().join("src/lib.rs").to_str().unwrap().into(), + content_hash: format!("{:x}", Sha256::digest(source.as_bytes())), + language: Some("rust".into()), + byte_len: source.len() as u64, + source: Some(source.clone()), + }; + let profiles = crate::profiles::ProfileSet::new(&request.profiles); + let profile = profiles.profile_for_language("rust").unwrap(); + let parse = crate::parser::parse_file(&snapshot, profile).unwrap(); + let mut partition = + crate::partition_builder::build_partition(&request, &snapshot, parse).unwrap(); + let key = ArtifactStore::key_for_request(&request, &snapshot, Some(profile)).unwrap(); + partition.set_artifact_key(key.clone()); + assert_eq!( + ArtifactStore::new(storage.join("artifacts")) + .store_partition(&key, &partition) + .unwrap(), + ArtifactWriteOutcome::Written + ); + assert!(active_origin_matches_payload( + &storage.join("artifacts").join(&key[..2]).join(&key), + &key + )); + (source, key) +} + +#[test] +fn dc_s5_real_mcp_and_writer_progress_during_preparation_and_changed_epoch_blocks_stale_effect() { + use crate::coordinator::{CoordinatorApiConfig, CoordinatorClient}; + use crate::storage::maintenance_candidate::arm_preparation_pause; + use crate::storage::maintenance_ownership::{ + observe_reference_epoch, EpochObservation, ReferenceDomain, ReferenceMutation, + }; + let root = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, active_before, initial_manifest) = committed(&root); + prepare_canonical_fixture_registry(&storage, &active_before); + let artifact_root = storage.join("artifacts"); + let (future_source, future_key) = create_genuine_future_cache(&root, &storage); + let future_directory = artifact_root.join(&future_key[..2]).join(&future_key); + let future_before = observe_file(&future_directory.join("partition.json")); + let future_origin = fs::read(future_directory.join(".entry-origin")).unwrap(); + let mut expected_cache = referenced_keys(&initial_manifest); + expected_cache.insert(future_key.clone()); + assert_eq!( + cache_keys(&storage), + expected_cache, + "future cache must be the only genuine unreferenced candidate" + ); + assert!(!referenced_keys(&initial_manifest).contains(&future_key)); + let layout = ManagedLayout::new(storage.clone()); + let domain = ReferenceDomain::managed(&layout).unwrap(); + let before_epoch = match observe_reference_epoch(&domain).unwrap() { + EpochObservation::Verified(epoch) => epoch.counter(), + other => panic!("future-cache fixture lacks genuine covered E: {other:?}"), + }; + // Reproduce the CI startup race deterministically: the first real cleanup + // attempt must defer while a genuine producer keeps reference activity live. + let startup_activity = ReferenceMutation::begin_in_domain(&domain).unwrap(); + let guard = arm_preparation_pause(&artifact_root).unwrap(); + let refresh = crate::api::RefreshServiceConfig { + backend: crate::api::context::GraphRefreshBackend::Poll, + max_parallelism: 1, + // Source polling still detects the real edit below. Keep periodic + // full rescans outside the fixture window: repeated no-op writers + // otherwise race every advisory epoch observation before its hook. + reconcile_interval: std::time::Duration::from_secs(120), + explicit_overrides: crate::api::RefreshConfigOverrides { + policy: true, + backend: true, + max_parallelism: true, + reconcile_interval: true, + ..Default::default() + }, + ..Default::default() + }; + let owner = CoordinatorClient::connect_or_start(CoordinatorApiConfig::new( + selector(&root), + Some(refresh), + )) + .unwrap(); + let startup_deadline = std::time::Instant::now() + std::time::Duration::from_secs(30); + loop { + let health = coordinator_health(&owner, &root); + if health["maintenance"]["last_attempt"]["status"] == "deferred" { + assert!(!guard.reached()); + break; + } + assert!( + std::time::Instant::now() < startup_deadline, + "actual startup preparation did not observe held reference activity: {health}" + ); + std::thread::sleep(std::time::Duration::from_millis(10)); + } + drop(startup_activity); + wait_for_real_preparation(&guard, &owner, &root); + let preparing = coordinator_health(&owner, &root); + assert_eq!(preparing["maintenance"]["running"], true); + assert!( + preparing["maintenance"]["last_attempt"]["finished_at_unix_ms"].is_null(), + "hook is not pausing the current actual attempt" + ); + let started = preparing["maintenance"]["last_attempt"]["started_at_unix_ms"] + .as_u64() + .unwrap(); + query_through_same_owner_mcp(&root, 0); + fs::write(root.path().join("src/lib.rs"), future_source).unwrap(); + // The held owner's actual refresh service performs the native write. + // Observe its committed manifest while the owner's cleanup stays paused. + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(45); + loop { + let active: crate::storage::managed::ActiveGeneration = + serde_json::from_slice(&fs::read(storage.join("active.json")).unwrap()).unwrap(); + if active.generation_id != active_before { + let generation = layout.generation(&active.generation_id).unwrap(); + let published: NativeManifest = + serde_json::from_slice(&fs::read(generation.manifest_path()).unwrap()).unwrap(); + if published.files["src/lib.rs"].artifact_key.as_deref() == Some(future_key.as_str()) { + break; + } + } + assert!( + std::time::Instant::now() < deadline, + "actual owner refresh writer stalled during advisory preparation: {}", + coordinator_health(&owner, &root) + ); + std::thread::sleep(std::time::Duration::from_millis(10)); + } + query_through_same_owner_mcp(&root, 1); + // A committed pointer/manifest is observable before the refresh writer + // releases source activity. Poll only that real busy state, within the same + // 45-second writer-progress deadline; uncovered/corrupt epochs still fail. + let after_epoch = loop { + match observe_reference_epoch(&domain).unwrap() { + EpochObservation::Verified(epoch) => break epoch.counter(), + EpochObservation::Pending(reason) if reason == "reference_activity_in_use" => { + assert!( + std::time::Instant::now() < deadline, + "writer did not release source activity within its progress deadline" + ); + std::thread::sleep(std::time::Duration::from_millis(10)); + } + other => panic!("writer did not leave genuine covered E: {other:?}"), + } + }; + assert!(after_epoch > before_epoch); + assert_eq!( + observe_file(&future_directory.join("partition.json")), + future_before + ); + drop(guard); + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10); + loop { + let health = coordinator_health(&owner, &root); + let attempt = &health["maintenance"]["last_attempt"]; + if attempt["finished_at_unix_ms"].is_number() + || attempt["started_at_unix_ms"] + .as_u64() + .is_some_and(|value| value > started) + { + break; + } + assert!( + std::time::Instant::now() < deadline, + "resumed actual preparation did not finish/dispose: {health}" + ); + std::thread::sleep(std::time::Duration::from_millis(10)); + } + owner.drain_owned_operation(); + drop(owner); + assert_eq!( + observe_file(&future_directory.join("partition.json")), + future_before, + "stale pre-writer advisory references removed the newly active cache" + ); + assert_eq!( + fs::read(future_directory.join(".entry-origin")).unwrap(), + future_origin + ); + let store = ManagedStore::new(ManagedLayout::new(storage)); + let active = store.read_active_generation().unwrap().unwrap(); + let manifest: NativeManifest = serde_json::from_slice( + &fs::read( + store + .layout() + .generation(active.generation_id) + .unwrap() + .manifest_path(), + ) + .unwrap(), + ) + .unwrap(); + assert!(referenced_keys(&manifest).contains(&future_key)); + BehaviorSnapshot::observe(&root, 1).unwrap(); +} + +#[test] +fn dc_s5_real_owner_cancel_joins_paused_preparation_before_fixture_reuse() { + use crate::coordinator::{CoordinatorApiConfig, CoordinatorClient}; + use crate::storage::maintenance_candidate::arm_preparation_pause; + let mut fixture = CanonicalFixture::new(); + let guard = arm_preparation_pause(&fixture.artifact_root).unwrap(); + let owner = CoordinatorClient::connect_or_start(CoordinatorApiConfig::new( + selector(fixture.root()), + None, + )) + .unwrap(); + wait_for_real_preparation(&guard, &owner, fixture.root()); + query_through_same_owner_mcp(fixture.root(), 0); + let (sender, receiver) = std::sync::mpsc::channel(); + let join = std::thread::spawn(move || { + owner.drain_owned_operation(); + drop(owner); + sender.send(()).unwrap(); + }); + receiver + .recv_timeout(std::time::Duration::from_secs(10)) + .expect( + "real owner did not cancel and join advisory preparation while its pause remained armed", + ); + join.join().unwrap(); + assert!( + guard.cancelled(), + "real cancellation token was not observed by the actual paused preparation" + ); + fixture.assert_active_unchanged_without_runtime_entry(); + assert_eq!( + observe_file(&fixture.directory.join("partition.json")), + fixture.payload + ); + assert_eq!( + fs::read(fixture.directory.join(".entry-origin")).unwrap(), + fixture.origin + ); + assert!(retired_payloads(&fixture.artifact_root).is_empty()); + drop(guard); + for _ in 0..128 { + collector_attempt(fixture.root()); + if !fixture.directory.exists() && retired_payloads(&fixture.artifact_root).is_empty() { + break; + } + } + fixture.finish(); +} + +fn assert_root_migration_accepted(root: &FixtureRoot) { + use crate::storage::maintenance_legacy_bootstrap as legacy; + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).unwrap(); + let target = RepositoryIdentity::capture(&selector(root)) + .unwrap() + .maintenance_target(&memory) + .unwrap(); + let binding = + crate::storage::maintenance_admission::prepare_target_binding(target.target(), &memory) + .unwrap(); + let (storage, active, _) = committed(root); + let generation = ManagedLayout::new(&storage).generation(&active).unwrap(); + let mut scope = legacy::prepare_legacy_scope( + target.target(), + &binding, + legacy::LegacyScope::ManagedAdministration { + storage_root: &storage, + generation_root: generation.root(), + }, + false, + &memory, + ) + .unwrap(); + assert!( + scope.has_accepted_job(&memory).unwrap(), + "single confirmation did not durably accept the exact physical root migration" + ); +} + +fn public_maintenance(root: &FixtureRoot, confirm_legacy_quiescence: bool) -> Value { + execute(OperationRequest::Health(HealthRequest { + repo: selector(root), + confirm_legacy_quiescence, + refresh_status: None, + output_format: OutputFormat::Typed, + })) + .unwrap() +} + +fn copy_actual_legacy_orphan( + root: &FixtureRoot, +) -> crate::storage::maintenance_legacy_staging::CopiedOrphanStagingFixture { + root.write_revision(0); + install(root).unwrap(); + // Copy immediately after the actual public producer, before extra health + // entries can advance modern terminal GC. Only real public COPY/JSON files + // are copied; the helper creates no DB, compact manifest or modern receipt. + let storage = root.path().join(".codebaseGraph/storage"); + let copied = crate::storage::maintenance_legacy_staging::copy_public_orphan_staging_fixture( + root.path(), + &storage, + ) + .unwrap(); + assert!(!copied.payloads.is_empty()); + assert!(!copied.directory.join("manifest.json").exists()); + assert!(!copied.directory.join(".workspace.binding").exists()); + assert!(!copied.directory.join(".entry-origin").exists()); + for payload in &copied.payloads { + assert!(payload.starts_with(&copied.directory)); + assert!(payload + .extension() + .is_some_and(|extension| extension == "json" || extension == "csv")); + assert!(fs::symlink_metadata(payload).unwrap().file_type().is_file()); + } + copied +} + +fn assert_original_payloads_unchanged(paths: &[PathBuf], before: &[FileObservation]) { + for (path, observation) in paths.iter().zip(before) { + assert_eq!( + &observe_file(path), + observation, + "unconfirmed/invalid legacy payload changed: {}", + path.display() + ); + } +} + +fn legacy_regular_files_without_following_links( + root: &Path, +) -> std::collections::BTreeMap { + fn visit( + root: &Path, + path: &Path, + files: &mut std::collections::BTreeMap, + ) { + for entry in fs::read_dir(path).unwrap() { + let entry = entry.unwrap(); + let kind = entry.file_type().unwrap(); + if kind.is_dir() { + visit(root, &entry.path(), files); + } else if kind.is_file() { + files.insert( + entry.path().strip_prefix(root).unwrap().to_owned(), + observe_file(&entry.path()), + ); + } + // Symlinks/FIFOs are never opened for this regular-payload duplicate + // observation. The independently captured foreign link stays exact. + } + } + let mut files = std::collections::BTreeMap::new(); + visit(root, root, &mut files); + files +} + +fn copy_old_native_tree(source: &Path, destination: &Path) { + fs::create_dir_all(destination).unwrap(); + for entry in fs::read_dir(source).unwrap() { + let entry = entry.unwrap(); + let name = entry.file_name(); + // Copy native payloads and historical metadata, never modern cleanup + // provenance. Every copied byte still comes from the actual producer. + if name.as_encoded_bytes().starts_with(b".") { + continue; + } + let kind = entry.file_type().unwrap(); + assert!(!kind.is_symlink()); + let target = destination.join(name); + if kind.is_dir() { + copy_old_native_tree(&entry.path(), &target); + } else { + assert!(kind.is_file()); + fs::copy(entry.path(), target).unwrap(); + } + } +} + +fn assert_copied_native_payloads_unchanged( + root: &Path, + before: &std::collections::BTreeMap, +) { + for (relative, observation) in before { + assert_eq!(&observe_file(&root.join(relative)), observation); + } +} + +#[test] +fn dc_s5_existing_old_current_managed_store_requires_one_confirmation_then_retires_old_generation() +{ + let root = FixtureRoot::new(); + let archive = FixtureRoot::new(); + root.write_revision(0); + install(&root).unwrap(); + let (storage, old_id, old_manifest) = committed(&root); + let layout = ManagedLayout::new(storage.clone()); + let old_name = format!("gen-{old_id}"); + let saved_generation = archive.path().join("historical-generation"); + copy_old_native_tree( + layout.generation(&old_id).unwrap().root(), + &saved_generation, + ); + let saved_cache = archive.path().join("historical-artifacts"); + for key in referenced_keys(&old_manifest) { + let target = saved_cache.join(&key[..2]).join(&key); + fs::create_dir_all(&target).unwrap(); + fs::copy( + storage + .join("artifacts") + .join(&key[..2]) + .join(&key) + .join("partition.json"), + target.join("partition.json"), + ) + .unwrap(); + } + root.write_revision(1); + materialize(&root, "changed").unwrap(); + let (_, active, active_manifest) = committed(&root); + assert_ne!(active, old_id); + let behavior_before = BehaviorSnapshot::observe(&root, 1).unwrap(); + let state = root.path().join(".codebaseGraph"); + let saved_modern = archive.path().join("modern-state"); + fs::rename(&state, &saved_modern).unwrap(); + fs::create_dir(&state).unwrap(); + fs::copy(saved_modern.join("config.json"), state.join("config.json")).unwrap(); + fs::create_dir_all(storage.join("generations")).unwrap(); + for directory in ["runs", "phases", "artifacts"] { + fs::create_dir(storage.join(directory)).unwrap(); + } + fs::copy( + saved_modern.join("storage/active.json"), + storage.join("active.json"), + ) + .unwrap(); + let historical = storage.join("generations").join(old_name); + copy_old_native_tree(&saved_generation, &historical); + let active_name = format!("gen-{active}"); + let active_root = storage.join("generations").join(&active_name); + copy_old_native_tree( + &saved_modern.join("storage/generations").join(active_name), + &active_root, + ); + copy_old_native_tree(&saved_cache, &storage.join("artifacts")); + for key in referenced_keys(&active_manifest) { + let target = storage.join("artifacts").join(&key[..2]).join(&key); + fs::create_dir_all(&target).unwrap(); + fs::copy( + saved_modern + .join("storage/artifacts") + .join(&key[..2]) + .join(&key) + .join("partition.json"), + target.join("partition.json"), + ) + .unwrap(); + } + for path in [ + storage.join("writer.maintenance-cohort.json"), + storage.join("artifacts/.reference-epoch"), + storage.join("artifacts/.use-cohort.json"), + historical.join(".generation-origin"), + active_root.join(".generation-origin"), + ] { + assert!( + !path.exists(), + "old-current fixture accidentally copied modern authority: {}", + path.display() + ); + } + let active_before = legacy_regular_files_without_following_links(&active_root); + let historical_before = legacy_regular_files_without_following_links(&historical); + let cache_before = referenced_keys(&active_manifest) + .into_iter() + .map(|key| { + let path = storage + .join("artifacts") + .join(&key[..2]) + .join(key) + .join("partition.json"); + let observed = observe_file(&path); + (path, observed) + }) + .collect::>(); + let active_pointer = fs::read(storage.join("active.json")).unwrap(); + for _ in 0..32 { + public_maintenance(&root, false); + assert_eq!( + legacy_regular_files_without_following_links(&historical), + historical_before + ); + assert_copied_native_payloads_unchanged(&active_root, &active_before); + for (path, observed) in &cache_before { + assert_eq!(&observe_file(path), observed); + } + assert_eq!( + fs::read(storage.join("active.json")).unwrap(), + active_pointer + ); + assert!(!storage.join("artifacts/.reference-epoch").exists()); + } + let accepted = public_maintenance(&root, true); + let memory = CleanupMemory::new(MAINTENANCE_MEMORY_BYTES).unwrap(); + let target = RepositoryIdentity::capture(&selector(&root)) + .unwrap() + .maintenance_target(&memory) + .unwrap(); + let binding = + crate::storage::maintenance_admission::prepare_target_binding(target.target(), &memory) + .unwrap(); + let mut scope = crate::storage::maintenance_legacy_bootstrap::prepare_legacy_scope( + target.target(), + &binding, + crate::storage::maintenance_legacy_bootstrap::LegacyScope::ManagedAdministration { + storage_root: &storage, + generation_root: &active_root, + }, + true, + &memory, + ) + .unwrap(); + assert!(scope.has_accepted_job(&memory).unwrap(), "single quiescence confirmation was not durably accepted for the actual old current destination: {accepted}"); + drop(scope); + drop(binding); + drop(target); + drop(memory); + let mut last = accepted; + for _ in 0..4096 { + last = public_maintenance(&root, false); + assert_copied_native_payloads_unchanged(&active_root, &active_before); + for (path, observed) in &cache_before { + assert_eq!(&observe_file(path), observed); + } + assert_eq!( + fs::read(storage.join("active.json")).unwrap(), + active_pointer + ); + if !historical.exists() { + break; + } + } + assert!( + !historical.exists(), + "confirmed historical generation did not retire through default-false restarts: {last}" + ); + assert_eq!( + BehaviorSnapshot::observe(&root, 1).unwrap(), + behavior_before + ); +} + +#[test] +fn dc_s5_real_copied_orphan_staging_requires_confirmation_then_retires_without_source_edit() { + let root = FixtureRoot::new(); + let copied = copy_actual_legacy_orphan(&root); + let before = BehaviorSnapshot::observe(&root, 0).unwrap(); + let payload_before = copied + .payloads + .iter() + .map(|path| observe_file(path)) + .collect::>(); + let foreign = copied.directory.join("foreign-user-note"); + fs::write( + &foreign, + b"foreign historical sibling must stay at this physical public path", + ) + .unwrap(); + let foreign_before = observe_file(&foreign); + let foreign_identity = crate::storage::locks::path_identity(&foreign).unwrap(); + let directory_identity = crate::storage::locks::path_identity(&copied.directory).unwrap(); + let external = FixtureRoot::new(); + let external_file = external.path().join("legacy-external-sentinel"); + fs::write(&external_file, b"independent legacy fixture symlink target").unwrap(); + let external_before = observe_file(&external_file); + #[cfg(unix)] + let link = { + let path = copied.directory.join("foreign-external-link"); + std::os::unix::fs::symlink(&external_file, &path).unwrap(); + path + }; + #[cfg(unix)] + let link_before = observe_link(&link); + let mut explained_confirmation = false; + let storage = root.path().join(".codebaseGraph/storage"); + let idle_limit = copied + .payloads + .len() + .checked_mul(32) + .and_then(|count| count.checked_add(512)) + .unwrap(); + let mut modern_idle = false; + for attempt in 0..idle_limit { + let health = public_maintenance(&root, false); + explained_confirmation |= health["maintenance"]["reasons"] + .as_array() + .unwrap() + .iter() + .any(|reason| reason == "legacy_quiescence_required"); + assert_original_payloads_unchanged(&copied.payloads, &payload_before); + let no_runs = fs::read_dir(storage.join("runs")).unwrap().next().is_none(); + let no_private = retired_payloads(&storage.join("artifacts")).is_empty(); + if no_runs && no_private && attempt >= 32 { + modern_idle = true; + break; + } + } + assert!(modern_idle, "real producer's modern work never became idle before the single administrative confirmation"); + assert!( + explained_confirmation, + "real unconfirmed legacy staging lacks its required quiescence reason" + ); + let attempt_limit = copied + .payloads + .len() + .checked_mul(32) + .and_then(|count| count.checked_add(512)) + .unwrap(); + let accepted_health = public_maintenance(&root, true); + assert_root_migration_accepted(&root); + let mut accepted_job = None; + let mut version = 0; + let mut last = accepted_health; + for _ in 0..attempt_limit { + // ONE confirmation above accepts this real bound administrative job. + // Every later public entry restarts with the default false input. + last = public_maintenance(&root, false); + let observed = + crate::storage::maintenance_legacy_staging::observe_accepted_staging_job(root.path()) + .unwrap(); + if let Some(observed) = observed { + assert_eq!(observed.member, directory_identity); + assert_ne!(observed.job, [0; 32]); + if let Some(accepted) = accepted_job { + assert_eq!( + observed.job, accepted, + "default-false restart invented/replaced the accepted member job" + ); + } else { + accepted_job = Some(observed.job); + } + assert!(observed.version >= version); + version = observed.version; + } else if accepted_job.is_some() && copied.payloads.iter().any(|path| path.exists()) { + panic!("accepted member job was lost before its original payloads retired: {last}"); + } else { + assert_root_migration_accepted(&root); + } + assert_eq!(observe_file(&foreign), foreign_before); + assert_eq!( + crate::storage::locks::path_identity(&foreign).unwrap(), + foreign_identity + ); + assert_eq!( + crate::storage::locks::path_identity(&copied.directory).unwrap(), + directory_identity + ); + assert_eq!(observe_file(&external_file), external_before); + #[cfg(unix)] + assert_eq!(observe_link(&link), link_before); + if copied.payloads.iter().all(|path| !path.exists()) { + break; + } + } + assert!( + copied.payloads.iter().all(|path| !path.exists()), + "real confirmed R4 payloads failed to converge across public controller restarts: {last}" + ); + // No renamed duplicate of a known copied payload may remain in this legacy + // root. Unknown foreign state stays and prevents a full clean-state claim. + let remaining = legacy_regular_files_without_following_links(&copied.directory); + for (path, file) in &remaining { + if path == Path::new("foreign-user-note") { + continue; + } + assert!( + !payload_before.iter().any(|old| old.digest == file.digest), + "retired legacy payload remains under another private name: {}", + path.display() + ); + } + assert_eq!(BehaviorSnapshot::observe(&root, 0).unwrap(), before); +} + +#[test] +fn dc_s5_incomplete_copied_orphan_staging_is_preserved_even_with_confirmation() { + let root = FixtureRoot::new(); + let copied = copy_actual_legacy_orphan(&root); + let before = BehaviorSnapshot::observe(&root, 0).unwrap(); + let invalid = copied + .payloads + .iter() + .find(|path| { + path.extension() + .is_some_and(|extension| extension == "json") + }) + .unwrap(); + // This changes only an exclusively owned synthetic legacy artifact, never + // repository source. The actual association validator must reject it. + use std::io::Write; + fs::OpenOptions::new() + .append(true) + .open(invalid) + .unwrap() + .write_all(b"trailing-invalid-json") + .unwrap(); + let payload_before = copied + .payloads + .iter() + .map(|path| observe_file(path)) + .collect::>(); + let mut reported = false; + // One genuine confirmation, followed by ordinary default-false restarts. + // Repeated confirmations add a full cooldown without testing a new rule. + for attempt in 0..=16 { + let health = public_maintenance(&root, attempt == 0); + reported |= health["maintenance"]["pending"] == true + || health["maintenance"]["observation_complete"] == false; + assert_original_payloads_unchanged(&copied.payloads, &payload_before); + } + assert!( + reported, + "incomplete legacy input was presented as complete idle state" + ); + assert_eq!(BehaviorSnapshot::observe(&root, 0).unwrap(), before); +} diff --git a/tests/support/storage_maintenance/mod.rs b/tests/support/storage_maintenance/mod.rs new file mode 100644 index 00000000..a145b9b3 --- /dev/null +++ b/tests/support/storage_maintenance/mod.rs @@ -0,0 +1,139 @@ +//! Isolated fixture inputs and independent filesystem observations for DC-S5. +//! This module is consumed once the production maintenance surfaces are available. + +#[cfg(debug_assertions)] +pub mod process; +pub mod workload; + +use std::collections::BTreeMap; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::SystemTime; + +static FIXTURE_SEQUENCE: AtomicU64 = AtomicU64::new(0); + +pub struct FixtureRoot { + path: PathBuf, +} + +impl FixtureRoot { + pub fn new(label: &str) -> io::Result { + // create_dir, rather than create_dir_all, rejects an existing incarnation. + for _ in 0..100 { + let sequence = FIXTURE_SEQUENCE.fetch_add(1, Ordering::Relaxed); + let path = std::env::temp_dir().join(format!( + "cgraph-s5-{label}-{}-{sequence:016x}", + std::process::id() + )); + match fs::create_dir(&path) { + Ok(()) => return Ok(Self { path }), + Err(error) if error.kind() == io::ErrorKind::AlreadyExists => continue, + Err(error) => return Err(error), + } + } + Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "could not allocate an isolated storage-maintenance fixture", + )) + } + + pub fn path(&self) -> &Path { + &self.path + } + + pub fn write_revision(&self, revision: usize) -> io::Result<()> { + let directory = self.path.join("src"); + fs::create_dir_all(&directory)?; + let stable_source = directory.join("stable.rs"); + if !stable_source.exists() { + fs::write(&stable_source, "pub fn stable_helper() -> usize { 7 }\n")?; + } + fs::write(directory.join("lib.rs"), source_revision(revision)) + } +} + +impl Drop for FixtureRoot { + fn drop(&mut self) { + // This type owns only the directory it exclusively created. Process + // fixtures must independently terminate and reap children before drop. + let _ = fs::remove_dir_all(&self.path); + } +} + +pub fn source_revision(revision: usize) -> String { + format!( + "mod stable;\npub use stable::stable_helper;\n\ + pub fn revision_{revision}() -> usize {{ stable_helper() + {revision} }}\n" + ) +} + +#[derive(Debug, PartialEq, Eq)] +pub enum ObservedKind { + Directory, + File(Vec), + Symlink(PathBuf), + Other, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct ObservedEntry { + pub kind: ObservedKind, + pub modified: Option, + pub readonly: bool, + #[cfg(unix)] + pub unix_mode: u32, +} + +/// Exact observation for small fixture source/sentinel trees. It deliberately +/// does not follow symlinks or silently discard unreadable entries. Large graph +/// trees require a separate reachability/count oracle, rather than loading their +/// database contents through this helper. +pub fn observe_small_tree(root: &Path) -> io::Result> { + fn visit( + root: &Path, + path: &Path, + entries: &mut BTreeMap, + ) -> io::Result<()> { + let metadata = fs::symlink_metadata(path)?; + let kind = if metadata.file_type().is_symlink() { + ObservedKind::Symlink(fs::read_link(path)?) + } else if metadata.is_dir() { + ObservedKind::Directory + } else if metadata.is_file() { + ObservedKind::File(fs::read(path)?) + } else { + ObservedKind::Other + }; + let is_directory = matches!(kind, ObservedKind::Directory); + #[cfg(unix)] + use std::os::unix::fs::PermissionsExt; + entries.insert( + path.strip_prefix(root) + .expect("visited paths remain lexically inside the fixture") + .to_path_buf(), + ObservedEntry { + kind, + modified: metadata.modified().ok(), + readonly: metadata.permissions().readonly(), + #[cfg(unix)] + unix_mode: metadata.permissions().mode(), + }, + ); + if is_directory { + for entry in fs::read_dir(path)? { + visit(root, &entry?.path(), entries)?; + } + } + Ok(()) + } + + let mut entries = BTreeMap::new(); + match fs::symlink_metadata(root) { + Ok(_) => visit(root, root, &mut entries)?, + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => return Err(error), + } + Ok(entries) +} diff --git a/tests/support/storage_maintenance/process.rs b/tests/support/storage_maintenance/process.rs new file mode 100644 index 00000000..2f52ae59 --- /dev/null +++ b/tests/support/storage_maintenance/process.rs @@ -0,0 +1,924 @@ +//! Fixture-owned interruption driver over the real native phase barrier. +//! No numeric PID is used to terminate a process or authorize storage cleanup. +//! Failed/unresolved fixtures retain their directories and real recovery proof. + +use super::{observe_small_tree, workload, FixtureRoot, ObservedEntry}; +use codebase_graph::protocol::NativeSyntaxMaterializationResponse; +use serde_json::Value; +use std::{ + collections::BTreeMap, + fs, + path::{Path, PathBuf}, + process::{Child, Command, ExitStatus, Stdio}, + thread, + time::{Duration, Instant}, +}; + +pub struct FaultFixture { + root: Option, + control: Option, + external_before: BTreeMap, + recovered: bool, +} + +impl FaultFixture { + pub fn new() -> Result { + let root = FixtureRoot::new("phase-fault").map_err(|error| error.to_string())?; + root.write_revision(0).map_err(|error| error.to_string())?; + let control = FixtureRoot::new("phase-control").map_err(|error| error.to_string())?; + let external = control.path().join("external"); + fs::create_dir(&external).map_err(|error| error.to_string())?; + fs::write( + external.join("sentinel.bin"), + b"external fixture data must survive\0\xff", + ) + .map_err(|error| error.to_string())?; + let external_before = observe_small_tree(&external).map_err(|error| error.to_string())?; + Ok(Self { + root: Some(root), + control: Some(control), + external_before, + recovered: false, + }) + } + + pub fn root(&self) -> &FixtureRoot { + self.root.as_ref().expect("owned fixture root") + } + + pub fn start_paused_build(&self) -> Result, String> { + let active = workload::health(self.root())?; + let active_root = fs::canonicalize( + Path::new( + active["database_path"] + .as_str() + .ok_or("active graph has no database path")?, + ) + .parent() + .ok_or("active database has no parent")?, + ) + .map_err(|error| error.to_string())?; + let control = self.control.as_ref().expect("owned fixture controls"); + let barrier = control.path().join("barrier"); + fs::create_dir(&barrier).map_err(|error| error.to_string())?; + let stdout = control.path().join("owner.stdout"); + let stderr = control.path().join("owner.stderr"); + let binary = std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(env!("CARGO_BIN_EXE_codebase-graph"))); + let child = Command::new(binary) + .current_dir(self.root().path()) + .args([ + "build", + "--mode", + "full", + "--no-git", + "--single-thread", + "--max-parallelism", + "1", + "--json", + "--repo-root", + ]) + .arg(self.root().path()) + .env("CODEBASE_GRAPH_TEST_PHASE_BARRIER_DIR", &barrier) + .stdin(Stdio::null()) + .stdout(Stdio::from( + fs::File::create(&stdout).map_err(|error| error.to_string())?, + )) + .stderr(Stdio::from( + fs::File::create(&stderr).map_err(|error| error.to_string())?, + )) + .spawn() + .map_err(|error| error.to_string())?; + Ok(OwnedBuild { + child: Some(child), + root: self.root(), + active_root, + barrier, + stdout, + stderr, + }) + } + + /// Only read/runtime recovery entries are repeated here. The subsequent + /// materialization is issued once, after actual containment is proven empty. + /// Optional request disposal may wait for that next authorized writer. + pub fn wait_for_recovery(&self, phase: &PhaseReady, timeout: Duration) -> Result<(), String> { + let deadline = Instant::now() + timeout; + let mut last = String::new(); + while Instant::now() < deadline { + match workload::health(self.root()) { + Ok(health) => { + if health["graph_readable"] == true + && health["pending_runs"] == 0 + && phase.observation() == ProcessObservation::Gone + && phase.closed_empty_containment() + { + return Ok(()); + } + last = format!( + "readable={} pending_runs={} phase={:?} request_present={} closed_empty={}", + health["graph_readable"], + health["pending_runs"], + phase.observation(), + phase.request_path.exists(), + phase.closed_empty_containment() + ); + } + Err(error) => last = error, + } + thread::sleep(Duration::from_millis(25)); + } + Err(format!("fixture phase recovery did not finish: {last}")) + } + + pub fn assert_external_unchanged(&self) { + let external = self.control.as_ref().unwrap().path().join("external"); + assert_eq!(observe_small_tree(&external).unwrap(), self.external_before); + } + + pub fn finish_recovery( + &mut self, + phase: &PhaseReady, + response: &NativeSyntaxMaterializationResponse, + ) -> Result<(), String> { + if !response.database_written + || response.pending_runs != 0 + || phase.observation() != ProcessObservation::Gone + || phase.request_path.exists() + { + return Err("fixture lacks a completed publication and quiescent failed phase".into()); + } + self.assert_external_unchanged(); + self.recovered = true; + Ok(()) + } +} + +impl Drop for FaultFixture { + fn drop(&mut self) { + if !self.recovered { + // Retain owned evidence on failure rather than delete a possibly + // live candidate. No fixture destructor guesses process authority. + for root in [&mut self.root, &mut self.control] { + if let Some(root) = root.take() { + eprintln!( + "retained unresolved qualification fixture: {}", + root.path().display() + ); + std::mem::forget(root); + } + } + } + } +} + +pub struct OwnedBuild<'a> { + child: Option, + root: &'a FixtureRoot, + active_root: PathBuf, + barrier: PathBuf, + stdout: PathBuf, + stderr: PathBuf, +} + +impl OwnedBuild<'_> { + pub fn wait_for_native_phase(&mut self, timeout: Duration) -> Result { + let deadline = Instant::now() + timeout; + let root = fs::canonicalize(self.root.path()).map_err(|error| error.to_string())?; + while Instant::now() < deadline { + if let Some(status) = self + .child + .as_mut() + .expect("owned owner") + .try_wait() + .map_err(|error| error.to_string())? + { + return Err(format!( + "build ended before interruption barrier ({status}): {}", + self.diagnostics() + )); + } + for entry in fs::read_dir(&self.barrier).map_err(|error| error.to_string())? { + let path = entry.map_err(|error| error.to_string())?.path(); + if path.extension().and_then(|value| value.to_str()) != Some("json") { + continue; + } + let metadata = fs::symlink_metadata(&path).map_err(|error| error.to_string())?; + if !metadata.is_file() || metadata.file_type().is_symlink() { + return Err("invalid phase readiness entry".into()); + } + let ready: Value = + serde_json::from_slice(&fs::read(path).map_err(|error| error.to_string())?) + .map_err(|error| error.to_string())?; + let pid = ready["pid"] + .as_u64() + .and_then(|value| u32::try_from(value).ok()) + .filter(|pid| *pid > 0) + .ok_or("phase readiness has invalid PID")?; + #[cfg(unix)] + if pid > i32::MAX as u32 { + return Err("phase readiness PID is outside native range".into()); + } + let request_path = fs::canonicalize( + ready["request_path"] + .as_str() + .ok_or("phase readiness has no request path")?, + ) + .map_err(|error| error.to_string())?; + if !request_path.starts_with(&root) { + return Err("phase request escaped fixture".into()); + } + let request_bytes = fs::read(&request_path).map_err(|error| error.to_string())?; + let request: Value = + serde_json::from_slice(&request_bytes).map_err(|error| error.to_string())?; + let candidate = Path::new( + request["db_path"] + .as_str() + .ok_or("phase request has no database path")?, + ) + .parent() + .ok_or("phase database has no parent")?; + let candidate = fs::canonicalize(candidate).map_err(|error| error.to_string())?; + if !candidate.starts_with(&root) { + return Err("phase candidate escaped fixture".into()); + } + if candidate == self.active_root { + return Err("phase barrier targets the committed generation instead of an independent candidate".into()); + } + let phase = PhaseReady { + pid, + request_path, + request_bytes, + candidate, + }; + if phase.observation() != ProcessObservation::Running { + return Err("phase did not remain at its real barrier".into()); + } + return Ok(phase); + } + thread::sleep(Duration::from_millis(25)); + } + Err(format!( + "native phase did not reach its real barrier: {}", + self.diagnostics() + )) + } + + pub fn interrupt_owner(&mut self) -> Result { + let child = self + .child + .as_mut() + .ok_or("owned owner was already reaped")?; + if let Some(status) = child.try_wait().map_err(|error| error.to_string())? { + return Err(format!( + "owner ended before requested interruption: {status}" + )); + } + // Keep the real Child handle in self until wait succeeds, so every + // error path still runs the fixture's kill-and-reap destructor. + child.kill().map_err(|error| error.to_string())?; + let status = child.wait().map_err(|error| error.to_string())?; + self.child.take(); + Ok(status) + } + + fn diagnostics(&self) -> String { + fn bounded(path: &Path) -> String { + use std::io::Read; + let mut output = String::new(); + if let Ok(file) = fs::File::open(path) { + let _ = file.take(64 * 1024).read_to_string(&mut output); + } + output + } + format!( + "stdout={} stderr={}", + bounded(&self.stdout), + bounded(&self.stderr) + ) + } +} + +impl Drop for OwnedBuild<'_> { + fn drop(&mut self) { + if let Some(mut child) = self.child.take() { + if child.try_wait().ok().flatten().is_none() { + let _ = child.kill(); + } + let _ = child.wait(); + } + } +} + +pub struct PhaseReady { + pid: u32, + request_path: PathBuf, + request_bytes: Vec, + candidate: PathBuf, +} + +impl PhaseReady { + pub fn assert_candidate_present(&self) { + assert!(self.candidate.is_dir()); + assert!(self.request_path.is_file()); + } + + fn closed_empty_containment(&self) -> bool { + use fs2::FileExt; + if !self.request_path.exists() { + return true; + } + if !fs::read(&self.request_path).is_ok_and(|bytes| bytes == self.request_bytes) { + return false; + } + let Some(root) = self.request_path.parent() else { + return false; + }; + let Ok(entries) = fs::read_dir(root) else { + return false; + }; + let mut leases = Vec::new(); + for entry in entries { + let Ok(entry) = entry else { return false }; + if !entry + .file_name() + .to_string_lossy() + .starts_with(".lifecycle-") + { + continue; + } + let Ok(metadata) = entry.file_type() else { + return false; + }; + if !metadata.is_dir() || metadata.is_symlink() { + return false; + } + let path = entry.path(); + let Ok(lease) = fs::OpenOptions::new() + .read(true) + .write(true) + .open(path.join("lease")) + else { + return false; + }; + if lease.try_lock_exclusive().is_err() { + return false; + } + let retired = + fs::read(path.join("retired")).is_ok_and(|bytes| bytes == b"retired-v2\n"); + let receipt = fs::read(path.join("receipt.json")) + .ok() + .and_then(|bytes| serde_json::from_slice::(&bytes).ok()) + .is_some_and(|receipt| receipt["containment_empty"] == true); + if !retired && !receipt { + return false; + } + leases.push(lease); + } + !leases.is_empty() + } + + fn observation(&self) -> ProcessObservation { + observe_process(self.pid) + } +} + +#[derive(Debug, PartialEq, Eq)] +enum ProcessObservation { + Running, + Gone, + Unknown, +} + +#[cfg(unix)] +fn observe_process(pid: u32) -> ProcessObservation { + // Signal zero is an observation only. It is never a cleanup permit. + if unsafe { libc::kill(pid as libc::pid_t, 0) } == 0 { + return ProcessObservation::Running; + } + if std::io::Error::last_os_error().raw_os_error() == Some(libc::ESRCH) { + ProcessObservation::Gone + } else { + ProcessObservation::Unknown + } +} + +#[cfg(windows)] +fn observe_process(pid: u32) -> ProcessObservation { + use windows_sys::Win32::{ + Foundation::{ + CloseHandle, GetLastError, ERROR_INVALID_PARAMETER, WAIT_OBJECT_0, WAIT_TIMEOUT, + }, + System::Threading::{OpenProcess, WaitForSingleObject, PROCESS_SYNCHRONIZE}, + }; + let handle = unsafe { OpenProcess(PROCESS_SYNCHRONIZE, 0, pid) }; + if handle.is_null() { + return if unsafe { GetLastError() } == ERROR_INVALID_PARAMETER { + ProcessObservation::Gone + } else { + ProcessObservation::Unknown + }; + } + let status = unsafe { WaitForSingleObject(handle, 0) }; + unsafe { + CloseHandle(handle); + } + match status { + WAIT_OBJECT_0 => ProcessObservation::Gone, + WAIT_TIMEOUT => ProcessObservation::Running, + _ => ProcessObservation::Unknown, + } +} + +#[cfg(not(any(unix, windows)))] +fn observe_process(_pid: u32) -> ProcessObservation { + ProcessObservation::Unknown +} + +pub struct ReapedMcp { + status: ExitStatus, +} + +pub struct OwnedMcp<'a> { + child: Option, + stdin: Option, + responses: std::sync::mpsc::Receiver>, + reader: Option>, + next_id: u64, + stderr: PathBuf, + _root: &'a FixtureRoot, +} + +impl FaultFixture { + /// Public setup supplies the actual configuration. Resetting ONLY its closed + /// synthetic managed tree makes the next actual worker create a new root; + /// no FreshRoot/registration/receipt token is assembled by the fixture. + pub fn public_health(&self) -> Result { + workload::health(self.root()) + } + + pub fn reset_closed_managed_storage(&self) -> Result<(), String> { + workload::install(self.root())?; + let health = workload::health(self.root())?; + let storage = fs::canonicalize( + health["storage_root"] + .as_str() + .ok_or("fixture has no managed storage root")?, + ) + .map_err(|error| error.to_string())?; + let root = fs::canonicalize(self.root().path()).map_err(|error| error.to_string())?; + if !storage.starts_with(&root) || storage == root { + return Err("synthetic storage reset escaped fixture".into()); + } + fs::remove_dir_all(&storage).map_err(|error| error.to_string())?; + if storage.exists() { + return Err( + "synthetic managed storage remains before isolated-worker first creation".into(), + ); + } + Ok(()) + } + + pub fn start_mcp(&self) -> Result, String> { + use std::io::{BufRead, BufReader}; + let stderr = self + .control + .as_ref() + .unwrap() + .path() + .join("mcp-owner.stderr"); + let binary = std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(env!("CARGO_BIN_EXE_codebase-graph"))); + let mut child = Command::new(binary) + .args(["mcp", "start", "--repo-root"]) + .arg(self.root().path()) + .args(["--max-parallelism", "1", "--spill-chunk-mib", "1"]) + .current_dir(self.root().path()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::from( + fs::File::create(&stderr).map_err(|error| error.to_string())?, + )) + .spawn() + .map_err(|error| error.to_string())?; + let stdin = child.stdin.take().ok_or("MCP stdin missing")?; + let stdout = child.stdout.take().ok_or("MCP stdout missing")?; + let (sender, responses) = std::sync::mpsc::channel(); + let reader = std::thread::spawn(move || { + for line in BufReader::new(stdout).lines() { + let response = line.map_err(|error| error.to_string()).and_then(|line| { + serde_json::from_str(&line).map_err(|error| { + format!("invalid actual MCP JSON response: {error}: {line}") + }) + }); + if sender.send(response).is_err() { + break; + } + } + }); + let mut mcp = OwnedMcp { + child: Some(child), + stdin: Some(stdin), + responses, + reader: Some(reader), + next_id: 1, + stderr, + _root: self.root(), + }; + let response = mcp.request("initialize", serde_json::json!({"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"dc-s5-isolated-worker","version":"1"}}))?; + if !response["result"].is_object() { + return Err(format!("MCP initialization failed: {response}")); + } + mcp.notify("notifications/initialized", serde_json::json!({}))?; + Ok(mcp) + } + + pub fn finish_worker_fixture( + &mut self, + reaped: ReapedMcp, + response: &NativeSyntaxMaterializationResponse, + ) -> Result<(), String> { + if !reaped.status.success() || !response.database_written { + return Err("isolated fixture lacks a reaped successful owner and successful later real publication".into()); + } + self.assert_external_unchanged(); + self.recovered = true; + Ok(()) + } +} + +impl OwnedMcp<'_> { + fn write(&mut self, value: &Value) -> Result<(), String> { + use std::io::Write; + let input = self.stdin.as_mut().ok_or("MCP input closed")?; + serde_json::to_writer(&mut *input, value).map_err(|error| error.to_string())?; + input + .write_all(b"\n") + .and_then(|_| input.flush()) + .map_err(|error| error.to_string()) + } + fn notify(&mut self, method: &str, params: Value) -> Result<(), String> { + self.write(&serde_json::json!({"jsonrpc":"2.0","method":method,"params":params})) + } + fn request(&mut self, method: &str, params: Value) -> Result { + let id = self.next_id; + self.next_id += 1; + self.write(&serde_json::json!({"jsonrpc":"2.0","id":id,"method":method,"params":params}))?; + loop { + let response = self + .responses + .recv_timeout(Duration::from_secs(30)) + .map_err(|error| { + format!( + "actual MCP response unavailable: {error}; {}", + fs::read_to_string(&self.stderr).unwrap_or_default() + ) + })??; + if response.get("id").is_none() { + continue; + } + if response["id"] != id { + return Err(format!("MCP response identity mismatch: {response}")); + } + return Ok(response); + } + } + pub fn wait_for_revision( + &mut self, + revision: usize, + timeout: Duration, + ) -> Result { + let deadline = Instant::now() + timeout; + let mut last = Value::Null; + while Instant::now() < deadline { + let health = self.request("tools/call", serde_json::json!({"name":"graph_health","arguments":{"include_structured_content":true}}))?; + if health["result"]["isError"] == true { + if health["result"]["structuredContent"]["error"]["retryable"] == true { + last = health; + thread::sleep(Duration::from_millis(25)); + continue; + } + return Err(format!("nonretryable MCP health failure: {health}")); + } + if health["result"]["structuredContent"]["graph_readable"] == true { + let query = self.request("tools/call", serde_json::json!({"name":"graph_query","arguments":{"statement":"MATCH (n:Function) RETURN n.label ORDER BY n.label","limit":100,"include_structured_content":true}}))?; + if query["result"]["isError"] == true { + if query["result"]["structuredContent"]["error"]["retryable"] != true { + return Err(format!("nonretryable MCP graph query failure: {query}")); + } + } else { + let data = &query["result"]["structuredContent"]; + let mut expected = + vec![format!("revision_{revision}"), "stable_helper".to_owned()]; + expected.sort(); + let rows = data["rows"] + .as_array() + .ok_or("MCP graph query rows missing")?; + let actual = rows + .iter() + .map(|row| { + row[0] + .as_str() + .map(str::to_owned) + .ok_or("MCP function label missing") + }) + .collect::, _>>()?; + if data["truncated"] == false && actual == expected { + let worker = health["result"]["structuredContent"]["refresh"] + ["phase_high_water_marks"]["materialization_worker_rss"] + .as_u64(); + if worker.is_some_and(|bytes| bytes > 0) { + let fresh = self.request("tools/call", serde_json::json!({"name":"graph_health","arguments":{"include_structured_content":true}}))?; + if fresh["result"]["isError"] == false + && fresh["result"]["structuredContent"]["graph_readable"] == true + && fresh["result"]["structuredContent"]["active_generation"] + == health["result"]["structuredContent"]["active_generation"] + { + return Ok(fresh["result"]["structuredContent"].clone()); + } + } + } + } + last = query; + } else { + last = health; + } + thread::sleep(Duration::from_millis(25)); + } + Err(format!("actual MCP isolated refresh never reached revision{revision} with worker telemetry: {last}")) + } + pub fn close(mut self) -> Result { + drop(self.stdin.take()); + let deadline = Instant::now() + Duration::from_secs(20); + loop { + let child = self.child.as_mut().unwrap(); + if let Some(status) = child.try_wait().map_err(|error| error.to_string())? { + self.child.take(); + let reader_deadline = Instant::now() + Duration::from_secs(2); + while self + .reader + .as_ref() + .is_some_and(|reader| !reader.is_finished()) + { + if Instant::now() >= reader_deadline { + return Err( + "reaped MCP owner left its stdout reader live; fixture retained".into(), + ); + } + thread::sleep(Duration::from_millis(10)); + } + if let Some(reader) = self.reader.take() { + reader.join().map_err(|_| "MCP stdout reader panicked")?; + } + if !status.success() { + return Err(format!( + "MCP owner failed during graceful EOF: {status}; {}", + fs::read_to_string(&self.stderr).unwrap_or_default() + )); + } + return Ok(ReapedMcp { status }); + } + if Instant::now() >= deadline { + return Err("MCP owner did not join after graceful EOF".into()); + } + thread::sleep(Duration::from_millis(25)); + } + } +} +impl Drop for OwnedMcp<'_> { + fn drop(&mut self) { + drop(self.stdin.take()); + if let Some(child) = self.child.as_mut() { + if child.try_wait().ok().flatten().is_none() { + let _ = child.kill(); + } + let _ = child.wait(); + } + if let Some(reader) = self.reader.take() { + if reader.is_finished() { + let _ = reader.join(); + } else { + eprintln!("unresolved MCP stdout reader detached; fixture roots remain retained"); + } + } + // FaultFixture preserves roots unless a normal EOF/reap token and later + // actual successful publication mark recovery. No descendant PID kill. + } +} + +pub struct CoveredEpochObservation { + storage: PathBuf, + epoch: Value, + use_header: Vec, + publication_binding: Vec, +} +impl CoveredEpochObservation { + pub fn observe(root: &FixtureRoot, health: &Value) -> Result { + use sha2::{Digest, Sha256}; + let storage = fs::canonicalize( + health["storage_root"] + .as_str() + .ok_or("MCP health storage root missing")?, + ) + .map_err(|error| error.to_string())?; + if !storage.starts_with(fs::canonicalize(root.path()).map_err(|error| error.to_string())?) { + return Err("MCP storage escaped fixture".into()); + } + let artifacts = storage.join("artifacts"); + let serial = fs::File::open(artifacts.join(".reference-epoch.lock")) + .map_err(|error| error.to_string())?; + let lock_deadline = Instant::now() + Duration::from_secs(5); + loop { + match fs2::FileExt::try_lock_shared(&serial) { + Ok(()) => break, + Err(error) + if (error.kind() == std::io::ErrorKind::WouldBlock + || (error.raw_os_error().is_some() + && error.raw_os_error() + == fs2::lock_contended_error().raw_os_error())) + && Instant::now() < lock_deadline => + { + thread::sleep(Duration::from_millis(10)) + } + Err(error) => { + return Err(format!( + "cannot observe actual serialized reference epoch: {error}" + )) + } + } + } + let epoch_bytes = + fs::read(artifacts.join(".reference-epoch")).map_err(|error| error.to_string())?; + if epoch_bytes.len() != 2048 + || &epoch_bytes[..8] != b"CGRFE001" + || epoch_bytes[2016..] != Sha256::digest(&epoch_bytes[..2016])[..] + { + return Err("actual epoch envelope invalid".into()); + } + let length = u32::from_le_bytes(epoch_bytes[8..12].try_into().unwrap()) as usize; + if length > 2004 { + return Err("actual epoch payload exceeds frame".into()); + } + let epoch: Value = serde_json::from_slice(&epoch_bytes[12..12 + length]) + .map_err(|error| error.to_string())?; + if epoch["version"] != 2 || epoch["covered"] != true { + return Err(format!( + "actual worker did not retain covered reference epoch: {epoch}" + )); + } + let use_header = + fs::read(artifacts.join(".use.lock")).map_err(|error| error.to_string())?; + if use_header.len() != 72 + || &use_header[..8] != b"CGCOOR02" + || use_header[40..] != Sha256::digest(&use_header[..40])[..] + { + return Err("actual coordination creation header invalid".into()); + } + if epoch["use_incarnation"] != serde_json::json!(use_header[8..40]) { + return Err("covered epoch uses another actual creation nonce".into()); + } + let publication_binding = + fs::read(artifacts.join(".publication.binding")).map_err(|error| error.to_string())?; + if publication_binding.is_empty() { + return Err("actual root publication binding empty".into()); + } + let manifest: codebase_graph::protocol::NativeManifest = serde_json::from_slice( + &fs::read( + health["manifest_path"] + .as_str() + .ok_or("actual manifest path missing")?, + ) + .map_err(|error| error.to_string())?, + ) + .map_err(|error| error.to_string())?; + for entry in manifest.files.values() { + let key = entry + .artifact_key + .as_deref() + .ok_or("actual producer entry lacks artifact key")?; + if key.len() != 64 || !key.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return Err("producer key malformed".into()); + } + let directory = artifacts.join(&key[..2]).join(key); + let origin = + fs::read(directory.join(".entry-origin")).map_err(|error| error.to_string())?; + if origin.len() != 2048 { + return Err(format!( + "actual isolated producer lacks full Published receipt at {}", + directory.display() + )); + } + for (phase, frame) in origin.chunks_exact(512).enumerate() { + if &frame[..8] != b"CGORIG01" + || frame[24] != phase as u8 + || frame[480..] != Sha256::digest(&frame[..480])[..] + { + return Err("actual producer receipt incomplete or corrupt".into()); + } + } + let last = &origin[1536..]; + let actual_key = last[64..96] + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::(); + let payload = + fs::read(directory.join("partition.json")).map_err(|error| error.to_string())?; + if actual_key != key + || payload.len() as u64 != u64::from_le_bytes(last[256..264].try_into().unwrap()) + || last[264..296] != Sha256::digest(&payload)[..] + { + return Err("actual producer receipt does not bind its accepted payload".into()); + } + } + Ok(Self { + storage, + epoch, + use_header, + publication_binding, + }) + } + pub fn assert_same_birth_and_advanced_epoch(&self, previous: &Self) { + assert_eq!(self.storage, previous.storage); + assert_eq!( + self.use_header, previous.use_header, + "followup reenrolled existing root" + ); + assert_eq!(self.publication_binding, previous.publication_binding); + assert_eq!(self.epoch["root"], previous.epoch["root"]); + assert_eq!(self.epoch["incarnation"], previous.epoch["incarnation"]); + assert!( + self.epoch["counter"].as_u64().unwrap() > previous.epoch["counter"].as_u64().unwrap(), + "followup failed to advance covered reference E" + ); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(windows)] + #[test] + fn covered_epoch_observer_retries_actual_contention_until_release() { + use std::sync::mpsc::{self, RecvTimeoutError}; + + let root = FixtureRoot::new("epoch-observer-contention").unwrap(); + let artifacts = root.path().join("artifacts"); + fs::create_dir(&artifacts).unwrap(); + let lock_path = artifacts.join(".reference-epoch.lock"); + let owner = fs::OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .open(&lock_path) + .unwrap(); + fs2::FileExt::try_lock_exclusive(&owner).unwrap(); + // After release, the real observer must reach its unchanged integrity check. + fs::write(artifacts.join(".reference-epoch"), b"invalid fixture epoch").unwrap(); + let health = serde_json::json!({ "storage_root": root.path() }); + let (contended_tx, contended_rx) = mpsc::channel(); + let (result_tx, result_rx) = mpsc::channel(); + + thread::scope(|scope| { + let observer = scope.spawn(|| { + let probe = fs::File::open(&lock_path).unwrap(); + let error = fs2::FileExt::try_lock_shared(&probe).unwrap_err(); + assert!(error.raw_os_error().is_some()); + assert_eq!( + error.raw_os_error(), + fs2::lock_contended_error().raw_os_error() + ); + drop(probe); + contended_tx.send(()).unwrap(); + result_tx + .send(CoveredEpochObservation::observe(&root, &health).map(|_| ())) + .unwrap(); + }); + contended_rx.recv_timeout(Duration::from_secs(2)).unwrap(); + let held_result = result_rx.recv_timeout(Duration::from_millis(100)); + fs2::FileExt::unlock(&owner).unwrap(); + let released_result = result_rx.recv_timeout(Duration::from_secs(2)); + observer.join().unwrap(); + assert_eq!(held_result, Err(RecvTimeoutError::Timeout)); + assert_eq!( + released_result.unwrap().unwrap_err(), + "actual epoch envelope invalid" + ); + }); + } + + #[test] + fn covered_epoch_observer_propagates_missing_serial_lock() { + let root = FixtureRoot::new("epoch-observer-missing-lock").unwrap(); + let artifacts = root.path().join("artifacts"); + fs::create_dir(&artifacts).unwrap(); + let expected = fs::File::open(artifacts.join(".reference-epoch.lock")).unwrap_err(); + let health = serde_json::json!({ "storage_root": root.path() }); + let error = CoveredEpochObservation::observe(&root, &health) + .err() + .expect("missing serial lock must fail"); + assert_eq!(error, expected.to_string()); + } +} diff --git a/tests/support/storage_maintenance/workload.rs b/tests/support/storage_maintenance/workload.rs new file mode 100644 index 00000000..4e89c727 --- /dev/null +++ b/tests/support/storage_maintenance/workload.rs @@ -0,0 +1,650 @@ +//! DC-S5 workload and independent public-response oracles. +//! +//! The schedule includes failure/maintenance boundaries that require real runtime +//! hooks. Merely constructing it is not evidence that those boundaries executed. +//! These helpers never create journals, ownership records, or retirement receipts. + +use super::{observe_small_tree, FixtureRoot, ObservedEntry, ObservedKind}; +use codebase_graph::api::{ + CodebaseGraphApi, ContextRequest, HealthRequest, MaterializationRequest, McpTransport, + OperationRequest, OutputFormat, QueryRequest, RepoSelector, RepositoryLifecycleRequest, + SearchRequest, +}; +use codebase_graph::protocol::{NativeManifest, NativeSyntaxMaterializationResponse}; +use serde_json::{json, Value}; +use std::{ + collections::BTreeMap, + fs, + path::PathBuf, + process::{Command, Stdio}, +}; + +pub const UPDATES: usize = 20; +pub const STABLE_PATH: &str = "src/stable.rs"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FailureBoundary { + ArtifactTemporaryWrite, + ArtifactReplacement, + Publication, + Detachment, + Deletion, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WorkloadStep { + WriteRevision { + revision: usize, + }, + Publish { + revision: usize, + }, + FailAt { + revision: usize, + boundary: FailureBoundary, + }, + UnchangedRefresh { + revision: usize, + }, + MaintenanceWithoutEdit { + revision: usize, + }, +} + +pub fn qualification_schedule() -> Vec { + let boundaries = [ + FailureBoundary::ArtifactTemporaryWrite, + FailureBoundary::ArtifactReplacement, + FailureBoundary::Publication, + FailureBoundary::Detachment, + FailureBoundary::Deletion, + ]; + let mut steps = Vec::new(); + for revision in 1..=UPDATES { + steps.push(WorkloadStep::WriteRevision { revision }); + let boundary = (revision % 4 == 0).then(|| boundaries[revision / 4 - 1]); + // Producer interruptions happen while new source is still unpublished. + // Replacement additionally needs a real producer replacement fixture. + if let Some( + boundary @ (FailureBoundary::ArtifactTemporaryWrite + | FailureBoundary::ArtifactReplacement + | FailureBoundary::Publication), + ) = boundary + { + steps.push(WorkloadStep::FailAt { revision, boundary }); + } + steps.push(WorkloadStep::Publish { revision }); + // Retirement interruptions need an already published successor and the + // real collector's owned, eligible predecessor; a filename is no proof. + if let Some(boundary @ (FailureBoundary::Detachment | FailureBoundary::Deletion)) = boundary + { + steps.push(WorkloadStep::FailAt { revision, boundary }); + } + steps.push(WorkloadStep::UnchangedRefresh { revision }); + steps.push(WorkloadStep::MaintenanceWithoutEdit { revision }); + } + steps +} + +pub struct WorkloadPair { + pub churn: FixtureRoot, + pub control: FixtureRoot, + // Equal repository basenames and equally shaped parent paths keep public + // repo_name/source-unit search documents controlled between both arms. + _churn_workspace: FixtureRoot, + _control_workspace: FixtureRoot, +} + +impl WorkloadPair { + pub fn new() -> std::io::Result { + let churn_workspace = FixtureRoot::new("workload")?; + let control_workspace = FixtureRoot::new("workload")?; + fn repository(workspace: &FixtureRoot) -> std::io::Result { + let path = workspace.path().join("repository"); + fs::create_dir(&path)?; + Ok(FixtureRoot { path }) + } + let churn = repository(&churn_workspace)?; + let control = repository(&control_workspace)?; + let pair = Self { + churn, + control, + _churn_workspace: churn_workspace, + _control_workspace: control_workspace, + }; + pair.churn.write_revision(0)?; + pair.control.write_revision(UPDATES)?; + Ok(pair) + } + + /// Compare source bytes only: the control deliberately has a different + /// creation history, independent storage, and independent timestamps. + pub fn assert_equal_final_sources(&self) { + for path in ["src/lib.rs", STABLE_PATH] { + assert_eq!( + fs::read(self.churn.path().join(path)).unwrap(), + fs::read(self.control.path().join(path)).unwrap(), + "final source differs at {path}" + ); + } + } +} + +fn selector(root: &FixtureRoot) -> RepoSelector { + RepoSelector { + repo_root: Some(root.path().to_path_buf()), + ..RepoSelector::default() + } +} + +fn execute(request: OperationRequest) -> Result { + CodebaseGraphApi::new() + .execute_operation(&request) + .map(|response| response.payload) + .map_err(|error| format!("{}: {}", error.code, error.message)) +} + +/// Install through the public producer; do not synthesize a managed manifest or +/// ownership record. MCP/client and agent-hook installation are explicitly off. +pub fn install(root: &FixtureRoot) -> Result { + execute(OperationRequest::Setup(RepositoryLifecycleRequest { + repo: selector(root), + action: "setup".into(), + output_format: OutputFormat::Typed, + dry_run: false, + mcp_client: Some("none".into()), + agent_hooks: "none".into(), + mcp_config_path: None, + instructions_target: Some("skip".into()), + skip_mcp_config: true, + mcp_transport: McpTransport::Stdio, + mcp_daemon_port: None, + mode: "full".into(), + include_fts: true, + })) +} + +pub fn materialize( + root: &FixtureRoot, + mode: &str, +) -> Result { + let payload = execute(OperationRequest::Materialize(MaterializationRequest { + repo: selector(root), + native_request_path: None, + source_root: None, + mode: mode.into(), + include_fts: true, + use_git: false, + git_diff: false, + git_base: None, + include_patterns: Vec::new(), + exclude_patterns: Vec::new(), + candidate_paths: Vec::new(), + parallel: false, + spill_chunk_mib: None, + max_parallelism: Some(1), + progress: false, + output_format: OutputFormat::Typed, + }))?; + serde_json::from_value(payload).map_err(|error| format!("materialization response: {error}")) +} + +/// Exercise the public refresh-intent path. Explicit Materialize operations +/// deliberately assemble a candidate even for unchanged source and are not a +/// substitute for this CLI watch-once opportunity. +pub fn unchanged_refresh(root: &FixtureRoot) -> Result { + let before = health(root)?; + let generation = before["active_generation"] + .as_str() + .ok_or("missing active generation before refresh")?; + let binary = std::env::var_os("CARGO_BIN_EXE_codebase-graph") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(env!("CARGO_BIN_EXE_codebase-graph"))); + let output = Command::new(binary) + .current_dir(root.path()) + .args([ + "watch", + "--once", + "--mode", + "changed", + "--no-git", + "--single-thread", + "--max-parallelism", + "1", + "--repo-root", + ]) + .arg(root.path()) + .stdin(Stdio::null()) + .output() + .map_err(|error| error.to_string())?; + let stdout = String::from_utf8(output.stdout).map_err(|error| error.to_string())?; + if !output.status.success() { + return Err(format!( + "watch-once failed: status={} stdout={} stderr={}", + output.status, + stdout, + String::from_utf8_lossy(&output.stderr) + )); + } + let events = stdout + .lines() + .filter(|line| line.starts_with("watch ")) + .collect::>(); + if events.len() != 1 { + return Err(format!("expected one refresh event: {stdout}")); + } + let fields = events[0] + .split_whitespace() + .filter_map(|field| field.split_once('=')) + .collect::>(); + for (field, expected) in [ + ("event", "refreshed"), + ("event_count", "0"), + ("changed_paths", "0"), + ("rebuilt", "0"), + ("deleted", "0"), + ("skipped", "true"), + ("database_written", "false"), + ] { + if fields.get(field) != Some(&expected) { + return Err(format!( + "unchanged refresh has unexpected {field}: {stdout}" + )); + } + } + let after = health(root)?; + if after["active_generation"].as_str() != Some(generation) { + return Err("unchanged refresh published another generation".into()); + } + Ok(events[0].into()) +} + +pub(super) fn health(root: &FixtureRoot) -> Result { + execute(OperationRequest::Health(HealthRequest { + repo: selector(root), + confirm_legacy_quiescence: false, + refresh_status: None, + output_format: OutputFormat::Typed, + })) +} + +#[derive(Debug, PartialEq, Eq)] +pub struct StableArtifact { + content_hash: String, + artifact_key: String, + observations: BTreeMap, +} + +impl StableArtifact { + /// Observe only an actual published producer manifest and its referenced + /// cache payload. This is a reuse oracle, never deletion authority. + pub fn observe(root: &FixtureRoot) -> Result { + let health = health(root)?; + if health["graph_readable"] != true { + return Err("fixture has no readable committed graph".into()); + } + let path = health["manifest_path"] + .as_str() + .ok_or("missing manifest path")?; + let path = fs::canonicalize(path).map_err(|error| error.to_string())?; + let fixture = fs::canonicalize(root.path()).map_err(|error| error.to_string())?; + if !path.starts_with(&fixture) { + return Err("published manifest escaped fixture".into()); + } + let manifest: NativeManifest = + serde_json::from_slice(&fs::read(path).map_err(|error| error.to_string())?) + .map_err(|error| error.to_string())?; + if manifest.files.len() != 2 || !manifest.files.contains_key("src/lib.rs") { + return Err("producer manifest must contain exactly the two fixture sources".into()); + } + let entry = manifest + .files + .get(STABLE_PATH) + .ok_or("stable source absent from manifest")?; + let key = entry + .artifact_key + .as_deref() + .ok_or("stable source has no artifact key")?; + if key.len() != 64 || !key.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return Err("producer returned an invalid artifact key".into()); + } + if entry.path != STABLE_PATH || entry.language != "rust" || entry.content_hash.is_empty() { + return Err("stable source manifest identity is incomplete".into()); + } + let storage = health["storage_root"] + .as_str() + .ok_or("fixture producer did not publish a managed storage root")?; + let storage = fs::canonicalize(storage).map_err(|error| error.to_string())?; + if !storage.starts_with(&fixture) { + return Err("managed storage root escaped fixture".into()); + } + let directory = storage.join("artifacts").join(&key[..2]).join(key); + let physical = fs::canonicalize(&directory).map_err(|error| error.to_string())?; + if !physical.starts_with(&storage) { + return Err("referenced artifact escaped managed storage".into()); + } + let observations = observe_small_tree(&directory).map_err(|error| error.to_string())?; + if !observations + .get(&PathBuf::from("partition.json")) + .is_some_and(|entry| matches!(entry.kind, ObservedKind::File(_))) + || observations + .values() + .any(|entry| matches!(entry.kind, ObservedKind::Symlink(_))) + { + return Err("referenced producer artifact is absent or contains a symlink".into()); + } + Ok(Self { + content_hash: entry.content_hash.clone(), + artifact_key: key.into(), + observations, + }) + } + + pub fn assert_reused(&self, current: &Self, response: &NativeSyntaxMaterializationResponse) { + assert!( + response.database_written, + "source update must actually publish" + ); + assert!( + response.artifacts_reused >= 1, + "producer did not report any reused artifact" + ); + assert_eq!( + self, current, + "unchanged source cache bytes/identity/metadata changed" + ); + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct BehaviorSnapshot { + function_rows: Vec, + stable_search: Vec, + revision_search: Vec, + revision_context: Vec, +} + +impl BehaviorSnapshot { + pub fn observe(root: &FixtureRoot, revision: usize) -> Result { + let query = execute(OperationRequest::Query(QueryRequest { + repo: selector(root), + statement: "MATCH (n:Function) RETURN n.label, n.path, n.line_start, n.line_end, n.summary ORDER BY n.path, n.line_start, n.label".into(), + parameters: json!({}), limit: 100, output_format: OutputFormat::Typed, + }))?; + if query["truncated"] != false { + return Err("function query is truncated or lacks completeness".into()); + } + let function_rows = query["rows"] + .as_array() + .ok_or("missing function rows")? + .clone(); + let revision_name = format!("revision_{revision}"); + let mut names = Vec::new(); + for row in &function_rows { + let row = row.as_array().ok_or("function row is not an array")?; + if row.len() != 5 { + return Err("function projection columns changed".into()); + } + names.push(row[0].as_str().ok_or("function name is not a string")?); + } + names.sort_unstable(); + let mut expected = vec![revision_name.as_str(), "stable_helper"]; + expected.sort_unstable(); + if names != expected { + return Err(format!("unexpected reachable function names: {names:?}")); + } + let stable = search(root, "stable_helper")?; + let current = search(root, &revision_name)?; + let selected = current + .iter() + .find(|hit| hit["label"] == revision_name && hit["type"] == "Function") + .ok_or("search did not find current revision function")?; + if !stable + .iter() + .any(|hit| hit["label"] == "stable_helper" && hit["type"] == "Function") + { + return Err("search did not find unchanged helper".into()); + } + let context = execute(OperationRequest::Context(ContextRequest { + repo: selector(root), + query: None, + layer: "semantic".into(), + profile: "definitions".into(), + limit: 100, + budget: 10000, + context_limit: 100, + max_depth: Some(1), + detail: "slim".into(), + node_id: Some( + selected["id"] + .as_str() + .ok_or("search result has no node identity")? + .into(), + ), + node_type: Some("Function".into()), + output_format: OutputFormat::Typed, + }))?; + let context = context["context"] + .as_array() + .ok_or("missing explicit-node context")?; + if context.is_empty() { + return Err("definitions context is unexpectedly empty".into()); + } + let mut revision_context = context + .iter() + .map(project_context) + .collect::, _>>()?; + revision_context.sort_by_key(Value::to_string); + Ok(Self { + function_rows, + stable_search: project_search(&stable)?, + revision_search: project_search(¤t)?, + revision_context, + }) + } +} + +fn search(root: &FixtureRoot, query: &str) -> Result, String> { + let result = execute(OperationRequest::Search(SearchRequest { + repo: selector(root), + query: query.into(), + layer: "semantic".into(), + profile: "definitions".into(), + limit: 100, + budget: 10000, + context_limit: 2, + max_depth: Some(1), + detail: "slim".into(), + output_format: OutputFormat::Typed, + }))?; + result["results"] + .as_array() + .cloned() + .ok_or("missing search results".into()) +} + +/// Omit only repository-specific node IDs/evidence chains. Preserve ranked +/// order, scores, paths, source spans and content; context keeps multiplicity. +fn project_search(rows: &[Value]) -> Result, String> { + rows.iter() + .map(|row| { + project( + row, + &["type", "label", "layer", "path", "rank_score"], + &["span", "summary"], + ) + }) + .collect() +} + +fn project_context(row: &Value) -> Result { + project( + row, + &["type", "label", "layer", "path", "direction", "relation"], + &["span", "summary"], + ) +} + +fn project(row: &Value, required: &[&str], optional: &[&str]) -> Result { + let source = row.as_object().ok_or("response row is not an object")?; + let mut projected = serde_json::Map::new(); + for key in required { + let value = source + .get(*key) + .filter(|value| !value.is_null()) + .ok_or_else(|| format!("response row lacks {key}"))?; + if (*key == "rank_score" && !value.is_number()) + || (*key != "rank_score" && !value.is_string()) + { + return Err(format!("response row has invalid {key} type")); + } + projected.insert((*key).into(), value.clone()); + } + for key in optional { + if let Some(value) = source.get(*key) { + if (*key == "summary" && !value.is_string()) || (*key == "span" && !value.is_object()) { + return Err(format!("response row has invalid {key} type")); + } + projected.insert((*key).into(), value.clone()); + } + } + Ok(Value::Object(projected)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn schedule_covers_twenty_updates_and_each_required_interruption_boundary() { + let schedule = qualification_schedule(); + let publications = schedule + .iter() + .filter_map(|step| match step { + WorkloadStep::Publish { revision } => Some(*revision), + _ => None, + }) + .collect::>(); + assert_eq!(publications, (1..=UPDATES).collect::>()); + let failures = schedule + .iter() + .filter_map(|step| match step { + WorkloadStep::FailAt { boundary, .. } => Some(*boundary), + _ => None, + }) + .collect::>(); + assert_eq!( + failures, + vec![ + FailureBoundary::ArtifactTemporaryWrite, + FailureBoundary::ArtifactReplacement, + FailureBoundary::Publication, + FailureBoundary::Detachment, + FailureBoundary::Deletion, + ] + ); + for revision in 1..=UPDATES { + let start = schedule + .iter() + .position(|step| *step == WorkloadStep::Publish { revision }) + .unwrap(); + let end = schedule + .iter() + .position(|step| *step == WorkloadStep::MaintenanceWithoutEdit { revision }) + .unwrap(); + let written = schedule + .iter() + .position(|step| *step == WorkloadStep::WriteRevision { revision }) + .unwrap(); + assert!(written < start); + if let Some((failure, boundary)) = + schedule + .iter() + .enumerate() + .find_map(|(index, step)| match step { + WorkloadStep::FailAt { + revision: found, + boundary, + } if *found == revision => Some((index, boundary)), + _ => None, + }) + { + match boundary { + FailureBoundary::Detachment | FailureBoundary::Deletion => { + assert!(failure > start) + } + _ => assert!(written < failure && failure < start), + } + } + + assert!(schedule[start..end].contains(&WorkloadStep::UnchangedRefresh { revision })); + } + } + + #[test] + fn independent_control_matches_twenty_source_updates_without_rewriting_stable_input() { + let pair = WorkloadPair::new().unwrap(); + assert_eq!( + pair.churn.path().file_name(), + pair.control.path().file_name() + ); + assert_ne!(pair.churn.path(), pair.control.path()); + let before = observe_small_tree(&pair.churn.path().join(STABLE_PATH)).unwrap(); + for revision in 1..=UPDATES { + pair.churn.write_revision(revision).unwrap(); + assert_eq!( + observe_small_tree(&pair.churn.path().join(STABLE_PATH)).unwrap(), + before + ); + } + pair.assert_equal_final_sources(); + let source = fs::read_to_string(pair.churn.path().join("src/lib.rs")).unwrap(); + assert!(source.contains("revision_20()")); + for revision in 0..UPDATES { + assert!(!source.contains(&format!("revision_{revision}()"))); + } + } + + #[test] + fn search_oracle_omits_only_incarnation_ids_and_keeps_rank_spans_and_content() { + let original = json!({"id":"churn-id", "type":"Function", "label":"revision_20", + "layer":"semantic", "path":"src/lib.rs", "rank_score":1.0, + "span":{"line_start":4,"line_end":4}, "summary":"stable_helper() + 20"}); + let mut other_incarnation = original.clone(); + other_incarnation["id"] = json!("clean-control-id"); + assert_eq!( + project_search(std::slice::from_ref(&original)).unwrap(), + project_search(std::slice::from_ref(&other_incarnation)).unwrap() + ); + for (field, changed) in [ + ("rank_score", json!(0.2)), + ("path", json!("foreign.rs")), + ("span", json!({"line_start":5,"line_end":5})), + ("summary", json!("wrong body")), + ] { + let mut changed_row = other_incarnation.clone(); + changed_row[field] = changed; + assert_ne!( + project_search(std::slice::from_ref(&original)).unwrap(), + project_search(&[changed_row]).unwrap() + ); + } + } + + #[test] + fn oracle_rejects_missing_response_fields_and_preserves_context_multiplicity() { + assert!(project_search(&[json!({"label":"revision_20"})]).is_err()); + let row = json!({"type":"Scope", "label":"scope", "layer":"semantic", "path":"src/lib.rs", + "direction":"out", "relation":"HasScope", "id":"incarnation-id"}); + let projected = project_context(&row).unwrap(); + let one = vec![projected.clone()]; + let duplicate = vec![projected.clone(), projected]; + assert_ne!(one, duplicate); + let mut changed_relation = row.clone(); + changed_relation["relation"] = json!("Defines"); + assert_ne!( + project_context(&row).unwrap(), + project_context(&changed_relation).unwrap() + ); + } +}