diff --git a/.bitcode/v43-deposit-option-admission.json b/.bitcode/v43-deposit-option-admission.json new file mode 100644 index 00000000..e4eb8793 --- /dev/null +++ b/.bitcode/v43-deposit-option-admission.json @@ -0,0 +1,350 @@ +{ + "artifactId": "v43-deposit-option-admission", + "schemaId": "bitcode.v43.depositOptionAdmission.v1", + "version": "V43", + "currentTarget": "V42", + "sourceSafetyVerdict": "source-safe-deposit-option-admission-metadata", + "generatedAt": "deterministic", + "artifactRoot": "v43-deposit-option-admission:00e82f1309f4798e342d3317", + "passed": true, + "objectIds": [ + "DepositAssetPackOptionAdmissionReport", + "DepositOptionAdmissionReceipt", + "DepositOptionReviewDecision", + "Depository index projection", + "object storage projection", + "pack activity synchronization", + "deposit option admission telemetry" + ], + "fieldIds": [ + "reviewDecision", + "admission", + "depositoryIndexProjection", + "storageProjection", + "compensationPreview", + "packsActivitySync", + "telemetry", + "visibility" + ], + "forbiddenPayloadIds": [ + "protected_source_payload", + "raw_source_text", + "unpaid_assetpack_source", + "raw_protected_prompt", + "interpolated_prompt", + "raw_provider_response", + "wallet_private_material", + "settlement_private_payload" + ], + "contractRows": [ + { + "rowId": "depositor-review-decisions", + "owner": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "contract": "DepositOptionReviewDecision records approved, rejected, resynthesis-requested, and pending decisions before any source-safe option can enter the Depository.", + "requiredFields": [ + "approved-for-admission", + "rejected-by-depositor", + "resynthesis-requested", + "pending-depositor-review" + ], + "rowRoot": "v43-deposit-option-admission-contract:01e2927f15d946408f8deb2a", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "admission-receipts", + "owner": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "contract": "DepositOptionAdmissionReceipt admits only approved, policy-eligible, source-safe options and emits deterministic blockers for pending, rejected, resynthesis, or policy-blocked options.", + "requiredFields": [ + "admitted-to-depository", + "not-admitted-policy-blocked", + "DepositOptionAdmissionReceipt" + ], + "rowRoot": "v43-deposit-option-admission-contract:5c71a5d215fd4fc83a129b68", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "depository-index-storage", + "owner": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "contract": "Admitted deposit AssetPacks project only measurement and metadata search indexes plus object-storage metadata and external source pointer roots.", + "requiredFields": [ + "depositoryIndexProjection", + "storageProjection", + "measurements-and-metadata-only" + ], + "rowRoot": "v43-deposit-option-admission-contract:e2eea037c5e42425792853b1", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "compensation-and-packs-sync", + "owner": "uapi/components/base/bitcode/activity/pack-activity-model.ts", + "contract": "Admitted options preserve Gate 6 BTC source-to-shares compensation preview and synchronize to /packs as Depository AssetPack activity without minting BTD.", + "requiredFields": [ + "compensationPreview", + "source-to-shares-largest-remainder", + "depository-assetpack" + ], + "rowRoot": "v43-deposit-option-admission-contract:82f4f8247db0fa99525046c4", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "telemetry-source-safety", + "owner": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "contract": "Admission telemetry emits execution-stream source-safe metadata while protected source, prompts, provider responses, wallet material, and settlement private payloads remain invisible.", + "requiredFields": [ + "deposit-option-admission", + "execution-stream", + "sourceSafeMetadataOnly" + ], + "rowRoot": "v43-deposit-option-admission-contract:fb0ba95c7c2e2c2a368b87dc", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + } + ], + "sourceRoots": { + "activePointer": "BITCODE_SPEC.txt:75f3f6d81f999da998f40cb6", + "spec": "BITCODE_SPEC_V43.md:be395a7424e1cda9f9a6db95", + "delta": "BITCODE_SPEC_V43_DELTA.md:bfdc638747d2b26e3f2e9198", + "notes": "BITCODE_SPEC_V43_NOTES.md:0b63f2a957175089051d64c5", + "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:8fec04876421410dd51ec879", + "roadmap": "SPECIFICATIONS_ROADMAP.md:8bbf62cb7261a6f2bc55e9bc", + "readme": "README.md:7a6eb7df6034f3f7ae18a4b6", + "protocolReadme": "packages/protocol/README.md:d338afce1b165d7da3bf9fc3", + "packageJson": "package.json:4eb71d824b7ac57b57e7eaa7", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:2fa0b3b3648ff6a9ce463290", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:232544cd143619fc8c2597a8", + "optionModel": "packages/pipelines/asset-pack/src/deposit-asset-pack-options.ts:df560ea2544282645f57a6b5", + "policyModel": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-policy.ts:12816b23b2d8e304ab4a1638", + "admissionModel": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts:4d2b0c5e6e10c2b14a75a8c8", + "optionModelTest": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-options.test.ts:66cfc8c728f134087476a3a3", + "policyModelTest": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-policy.test.ts:1af7adccac488298ccd7f048", + "admissionModelTest": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts:9f156ea0d8c603487a7a8152", + "routeModel": "uapi/app/deposit/deposit-route-model.ts:7354c10d543af2cb65ba0ade", + "client": "uapi/app/deposit/DepositPageClient.tsx:f8db0f58dd4ec01c5fca10b6", + "routeModelTest": "uapi/tests/depositRouteModel.test.ts:0fb7d8d4d58d7e231ada4510", + "pageTest": "uapi/tests/depositPageClient.test.tsx:18c44b7a03b74d9ca8b17536", + "packActivityModel": "uapi/components/base/bitcode/activity/pack-activity-model.ts:d848bb7a06bc8f4c46c2fc8a", + "packActivityModelTest": "uapi/tests/packActivityModel.test.ts:7854762895ce63b0de8b6940", + "uapiJestConfig": "uapi/jest.config.cjs:352ee765a17823c7a486fe92", + "packageIndex": "packages/pipelines/asset-pack/src/index.ts:24854bd26b47ce7700e02380", + "packageManifest": "packages/pipelines/asset-pack/package.json:f998601555d9f21de1393f74", + "protocolIndex": "packages/protocol/src/index.js:da82c2ca8c20849a6fa0b931", + "protocolTypes": "packages/protocol/src/index.d.ts:c04dd7f96f54a112437e39f9", + "protocolTest": "packages/protocol/test/v43-deposit-option-admission.test.js:53ae55cfef55536ebc552b65", + "generator": "scripts/generate-v43-deposit-option-admission.mjs:b7101699843c09250a84d41f", + "checker": "scripts/check-v43-gate7-deposit-option-admission.mjs:6a857acf8b23f5fdb15fb02d" + }, + "predicateResults": [ + { + "id": "active-canon-pointer-remains-v42", + "sourcePath": "BITCODE_SPEC.txt", + "passed": true + }, + { + "id": "spec-defines-gate7", + "sourcePath": "BITCODE_SPEC_V43.md", + "passed": true + }, + { + "id": "spec-names-admission-report", + "sourcePath": "BITCODE_SPEC_V43.md", + "passed": true + }, + { + "id": "delta-records-gate7", + "sourcePath": "BITCODE_SPEC_V43_DELTA.md", + "passed": true + }, + { + "id": "notes-records-gate7", + "sourcePath": "BITCODE_SPEC_V43_NOTES.md", + "passed": true + }, + { + "id": "parity-records-gate7", + "sourcePath": "BITCODE_SPEC_V43_PARITY_MATRIX.md", + "passed": true + }, + { + "id": "roadmap-records-gate7", + "sourcePath": "SPECIFICATIONS_ROADMAP.md", + "passed": true + }, + { + "id": "readme-records-gate7", + "sourcePath": "README.md", + "passed": true + }, + { + "id": "protocol-readme-records-gate7", + "sourcePath": "packages/protocol/README.md", + "passed": true + }, + { + "id": "admission-model-defines-report", + "sourcePath": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "passed": true + }, + { + "id": "admission-model-defines-review-decisions", + "sourcePath": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "passed": true + }, + { + "id": "admission-model-defines-admission-states", + "sourcePath": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "passed": true + }, + { + "id": "admission-model-defines-index-storage", + "sourcePath": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "passed": true + }, + { + "id": "admission-model-defines-packs-sync", + "sourcePath": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "passed": true + }, + { + "id": "admission-model-defines-telemetry", + "sourcePath": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "passed": true + }, + { + "id": "admission-model-forbids-source-leakage", + "sourcePath": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "passed": true + }, + { + "id": "route-model-owns-admission", + "sourcePath": "uapi/app/deposit/deposit-route-model.ts", + "passed": true + }, + { + "id": "deposit-client-renders-admission", + "sourcePath": "uapi/app/deposit/DepositPageClient.tsx", + "passed": true + }, + { + "id": "pack-activity-recognizes-admission", + "sourcePath": "uapi/components/base/bitcode/activity/pack-activity-model.ts", + "passed": true + }, + { + "id": "asset-pack-package-exports-admission", + "sourcePath": "packages/pipelines/asset-pack/src/index.ts", + "passed": true + }, + { + "id": "asset-pack-manifest-exports-admission", + "sourcePath": "packages/pipelines/asset-pack/package.json", + "passed": true + }, + { + "id": "uapi-jest-maps-admission", + "sourcePath": "uapi/jest.config.cjs", + "passed": true + }, + { + "id": "admission-test-covers-report", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts", + "passed": true + }, + { + "id": "route-test-covers-admission", + "sourcePath": "uapi/tests/depositRouteModel.test.ts", + "passed": true + }, + { + "id": "page-test-covers-admission", + "sourcePath": "uapi/tests/depositPageClient.test.tsx", + "passed": true + }, + { + "id": "pack-activity-test-covers-admission", + "sourcePath": "uapi/tests/packActivityModel.test.ts", + "passed": true + }, + { + "id": "protocol-test-covers-artifact", + "sourcePath": "packages/protocol/test/v43-deposit-option-admission.test.js", + "passed": true + }, + { + "id": "protocol-package-exports-gate7", + "sourcePath": "packages/protocol/src/index.js", + "passed": true + }, + { + "id": "protocol-types-export-gate7", + "sourcePath": "packages/protocol/src/index.d.ts", + "passed": true + }, + { + "id": "package-json-exposes-gate7", + "sourcePath": "package.json", + "passed": true + }, + { + "id": "gate-workflow-runs-gate7", + "sourcePath": ".github/workflows/bitcode-gate-quality.yml", + "passed": true + }, + { + "id": "canon-workflow-runs-gate7", + "sourcePath": ".github/workflows/bitcode-canon-quality.yml", + "passed": true + }, + { + "id": "generator-exists", + "sourcePath": "scripts/generate-v43-deposit-option-admission.mjs", + "passed": true + }, + { + "id": "checker-exists", + "sourcePath": "scripts/check-v43-gate7-deposit-option-admission.mjs", + "passed": true + } + ], + "coverage": { + "reviewDecisionsImplemented": true, + "approvalDecisionImplemented": true, + "rejectionDecisionImplemented": true, + "resynthesisDecisionImplemented": true, + "admissionReceiptsImplemented": true, + "approvedPolicyEligibleOptionsAdmittedOnly": true, + "depositoryIndexProjectionImplemented": true, + "vectorEmbeddingProjectionReadyForAdmittedOptions": true, + "storageProjectionImplemented": true, + "rawSourceStoredExternally": true, + "compensationPreviewContinued": true, + "compensationPriceAsset": "BTC", + "compensationAllocationMethod": "source-to-shares-largest-remainder", + "packsActivitySynchronizationImplemented": true, + "packsRoute": "/packs", + "packsActivityType": "depository-assetpack", + "telemetryImplemented": true, + "routeAdmissionReadbackImplemented": true, + "btdMintRequiresFutureNeedFitSettlement": true, + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawSourceTextVisible": false, + "unpaidAssetPackSourceVisible": false, + "rawPromptVisible": false, + "interpolatedPromptVisible": false, + "rawProviderResponseVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "requiredPredicateCount": 34, + "passedPredicateCount": 34, + "failedPredicateIds": [] + } +} diff --git a/.bitcode/v43-deposit-policy-compensation.json b/.bitcode/v43-deposit-policy-compensation.json index 70ee7cb6..4461a403 100644 --- a/.bitcode/v43-deposit-policy-compensation.json +++ b/.bitcode/v43-deposit-policy-compensation.json @@ -5,7 +5,7 @@ "currentTarget": "V42", "sourceSafetyVerdict": "source-safe-deposit-policy-compensation-metadata", "generatedAt": "deterministic", - "artifactRoot": "v43-deposit-policy-compensation:06077693236133dff819908a", + "artifactRoot": "v43-deposit-policy-compensation:0d74b0d78748ef57697c114a", "passed": true, "objectIds": [ "DepositAssetPackOptionPolicy", @@ -100,28 +100,28 @@ ], "sourceRoots": { "activePointer": "BITCODE_SPEC.txt:75f3f6d81f999da998f40cb6", - "spec": "BITCODE_SPEC_V43.md:801a9fc58bfeddd4c71140ac", - "delta": "BITCODE_SPEC_V43_DELTA.md:3f942fe82b77655231ddb8ed", - "notes": "BITCODE_SPEC_V43_NOTES.md:540a19ddbba065526ebe0819", - "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:0555bc917046cbad7995966e", - "roadmap": "SPECIFICATIONS_ROADMAP.md:aa1ee2ac496931752e11cd7e", - "readme": "README.md:89be1d13f8d516dc263bd9cf", - "protocolReadme": "packages/protocol/README.md:ecd8d9539e4cacdc7d1a32e6", - "packageJson": "package.json:d5e4b589e6a81ff3ba867017", - "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:715ca61f2961eae12ae64bb8", - "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:c9d194dbe48ea4eafd75f225", + "spec": "BITCODE_SPEC_V43.md:be395a7424e1cda9f9a6db95", + "delta": "BITCODE_SPEC_V43_DELTA.md:bfdc638747d2b26e3f2e9198", + "notes": "BITCODE_SPEC_V43_NOTES.md:0b63f2a957175089051d64c5", + "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:8fec04876421410dd51ec879", + "roadmap": "SPECIFICATIONS_ROADMAP.md:8bbf62cb7261a6f2bc55e9bc", + "readme": "README.md:7a6eb7df6034f3f7ae18a4b6", + "protocolReadme": "packages/protocol/README.md:d338afce1b165d7da3bf9fc3", + "packageJson": "package.json:4eb71d824b7ac57b57e7eaa7", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:2fa0b3b3648ff6a9ce463290", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:232544cd143619fc8c2597a8", "optionModel": "packages/pipelines/asset-pack/src/deposit-asset-pack-options.ts:df560ea2544282645f57a6b5", "policyModel": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-policy.ts:12816b23b2d8e304ab4a1638", "optionModelTest": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-options.test.ts:66cfc8c728f134087476a3a3", "policyModelTest": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-policy.test.ts:1af7adccac488298ccd7f048", - "routeModel": "uapi/app/deposit/deposit-route-model.ts:65e22b7bd3a9dcd6db0b7a47", - "client": "uapi/app/deposit/DepositPageClient.tsx:13f13e38dff99f46eb557179", - "routeModelTest": "uapi/tests/depositRouteModel.test.ts:c755b89c1e977e101aee25c1", - "pageTest": "uapi/tests/depositPageClient.test.tsx:f1526e498bfa332b3eb2cc42", - "packageIndex": "packages/pipelines/asset-pack/src/index.ts:7eddc6519cf67b4044fcd40e", - "packageManifest": "packages/pipelines/asset-pack/package.json:09e67662168fc921a53c696e", - "protocolIndex": "packages/protocol/src/index.js:533d73eb0b2cf01b4ac5c8b5", - "protocolTypes": "packages/protocol/src/index.d.ts:307e3a474c62fcce0b015be6", + "routeModel": "uapi/app/deposit/deposit-route-model.ts:7354c10d543af2cb65ba0ade", + "client": "uapi/app/deposit/DepositPageClient.tsx:f8db0f58dd4ec01c5fca10b6", + "routeModelTest": "uapi/tests/depositRouteModel.test.ts:0fb7d8d4d58d7e231ada4510", + "pageTest": "uapi/tests/depositPageClient.test.tsx:18c44b7a03b74d9ca8b17536", + "packageIndex": "packages/pipelines/asset-pack/src/index.ts:24854bd26b47ce7700e02380", + "packageManifest": "packages/pipelines/asset-pack/package.json:f998601555d9f21de1393f74", + "protocolIndex": "packages/protocol/src/index.js:da82c2ca8c20849a6fa0b931", + "protocolTypes": "packages/protocol/src/index.d.ts:c04dd7f96f54a112437e39f9", "protocolTest": "packages/protocol/test/v43-deposit-policy-compensation.test.js:f84b0c016065b10f796eb775", "generator": "scripts/generate-v43-deposit-policy-compensation.mjs:0117b1222d0f4a4640324b47", "checker": "scripts/check-v43-gate6-deposit-policy-compensation.mjs:03412b725c36f24991a6325e" diff --git a/.bitcode/v43-deposit-route-options.json b/.bitcode/v43-deposit-route-options.json index d47c1272..88b79373 100644 --- a/.bitcode/v43-deposit-route-options.json +++ b/.bitcode/v43-deposit-route-options.json @@ -5,7 +5,7 @@ "currentTarget": "V42", "sourceSafetyVerdict": "source-safe-deposit-route-option-metadata", "generatedAt": "deterministic", - "artifactRoot": "v43-deposit-route-options:cad9aab28407bb9a43dd27aa", + "artifactRoot": "v43-deposit-route-options:f122a3ca36cdc73e0215ce07", "passed": true, "stepIds": [ "connect-source", @@ -111,33 +111,33 @@ ], "sourceRoots": { "activePointer": "BITCODE_SPEC.txt:75f3f6d81f999da998f40cb6", - "spec": "BITCODE_SPEC_V43.md:801a9fc58bfeddd4c71140ac", - "delta": "BITCODE_SPEC_V43_DELTA.md:3f942fe82b77655231ddb8ed", - "notes": "BITCODE_SPEC_V43_NOTES.md:540a19ddbba065526ebe0819", - "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:0555bc917046cbad7995966e", - "roadmap": "SPECIFICATIONS_ROADMAP.md:aa1ee2ac496931752e11cd7e", - "readme": "README.md:89be1d13f8d516dc263bd9cf", - "protocolReadme": "packages/protocol/README.md:ecd8d9539e4cacdc7d1a32e6", - "packageJson": "package.json:d5e4b589e6a81ff3ba867017", - "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:715ca61f2961eae12ae64bb8", - "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:c9d194dbe48ea4eafd75f225", + "spec": "BITCODE_SPEC_V43.md:be395a7424e1cda9f9a6db95", + "delta": "BITCODE_SPEC_V43_DELTA.md:bfdc638747d2b26e3f2e9198", + "notes": "BITCODE_SPEC_V43_NOTES.md:0b63f2a957175089051d64c5", + "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:8fec04876421410dd51ec879", + "roadmap": "SPECIFICATIONS_ROADMAP.md:8bbf62cb7261a6f2bc55e9bc", + "readme": "README.md:7a6eb7df6034f3f7ae18a4b6", + "protocolReadme": "packages/protocol/README.md:d338afce1b165d7da3bf9fc3", + "packageJson": "package.json:4eb71d824b7ac57b57e7eaa7", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:2fa0b3b3648ff6a9ce463290", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:232544cd143619fc8c2597a8", "terminalRoutes": "uapi/app/terminal/terminal-routes.ts:80f478da2673f1a64ada9bfb", - "routeModel": "uapi/app/deposit/deposit-route-model.ts:65e22b7bd3a9dcd6db0b7a47", + "routeModel": "uapi/app/deposit/deposit-route-model.ts:7354c10d543af2cb65ba0ade", "page": "uapi/app/deposit/page.tsx:6bab6a9debab3f4350d0ddca", - "client": "uapi/app/deposit/DepositPageClient.tsx:13f13e38dff99f46eb557179", + "client": "uapi/app/deposit/DepositPageClient.tsx:f8db0f58dd4ec01c5fca10b6", "optionModel": "packages/pipelines/asset-pack/src/deposit-asset-pack-options.ts:df560ea2544282645f57a6b5", "optionModelTest": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-options.test.ts:66cfc8c728f134087476a3a3", - "packageIndex": "packages/pipelines/asset-pack/src/index.ts:7eddc6519cf67b4044fcd40e", - "packageManifest": "packages/pipelines/asset-pack/package.json:09e67662168fc921a53c696e", + "packageIndex": "packages/pipelines/asset-pack/src/index.ts:24854bd26b47ce7700e02380", + "packageManifest": "packages/pipelines/asset-pack/package.json:f998601555d9f21de1393f74", "publicCopy": "uapi/components/base/bitcode/layout/bitcode-public-copy.ts:3a45184ba8d2a4e71858c597", "publicExplainers": "uapi/components/base/bitcode/layout/bitcode-public-explainers.ts:282d915eac3c9f22ba8c07f1", "workspaceSurface": "uapi/components/base/bitcode/layout/workspace-surface.ts:12418d5db9c3ddad6d10c841", "nav": "uapi/components/base/bitcode/layout/nav.tsx:d7d1a8104e780bb7c25f1880", "footer": "uapi/components/base/bitcode/layout/footer.tsx:44749b98e5f63e7ac8f9f574", - "routeModelTest": "uapi/tests/depositRouteModel.test.ts:c755b89c1e977e101aee25c1", - "pageTest": "uapi/tests/depositPageClient.test.tsx:f1526e498bfa332b3eb2cc42", - "protocolIndex": "packages/protocol/src/index.js:533d73eb0b2cf01b4ac5c8b5", - "protocolTypes": "packages/protocol/src/index.d.ts:307e3a474c62fcce0b015be6", + "routeModelTest": "uapi/tests/depositRouteModel.test.ts:0fb7d8d4d58d7e231ada4510", + "pageTest": "uapi/tests/depositPageClient.test.tsx:18c44b7a03b74d9ca8b17536", + "protocolIndex": "packages/protocol/src/index.js:da82c2ca8c20849a6fa0b931", + "protocolTypes": "packages/protocol/src/index.d.ts:c04dd7f96f54a112437e39f9", "protocolTest": "packages/protocol/test/v43-deposit-route-options.test.js:6576a3d4947b9bc23f7a786d", "generator": "scripts/generate-v43-deposit-route-options.mjs:2d92d5e9c25e2e66712953f9", "checker": "scripts/check-v43-gate5-deposit-route-options.mjs:10c6ad1594ea8bd06f97d7d5" diff --git a/.bitcode/v43-packs-activity-master-detail.json b/.bitcode/v43-packs-activity-master-detail.json index fff95e25..dda6d2fc 100644 --- a/.bitcode/v43-packs-activity-master-detail.json +++ b/.bitcode/v43-packs-activity-master-detail.json @@ -5,7 +5,7 @@ "currentTarget": "V42", "sourceSafetyVerdict": "source-safe-packs-activity-master-detail-metadata", "generatedAt": "deterministic", - "artifactRoot": "v43-packs-activity-master-detail:14c2af511e4f0c40641606a3", + "artifactRoot": "v43-packs-activity-master-detail:2f9452517c1357333359e855", "passed": true, "typeIds": [ "deposit-option", @@ -138,17 +138,17 @@ ], "sourceRoots": { "activePointer": "BITCODE_SPEC.txt:75f3f6d81f999da998f40cb6", - "spec": "BITCODE_SPEC_V43.md:801a9fc58bfeddd4c71140ac", - "delta": "BITCODE_SPEC_V43_DELTA.md:3f942fe82b77655231ddb8ed", - "notes": "BITCODE_SPEC_V43_NOTES.md:540a19ddbba065526ebe0819", - "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:0555bc917046cbad7995966e", - "roadmap": "SPECIFICATIONS_ROADMAP.md:aa1ee2ac496931752e11cd7e", - "readme": "README.md:89be1d13f8d516dc263bd9cf", - "protocolReadme": "packages/protocol/README.md:ecd8d9539e4cacdc7d1a32e6", - "packageJson": "package.json:d5e4b589e6a81ff3ba867017", - "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:715ca61f2961eae12ae64bb8", - "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:c9d194dbe48ea4eafd75f225", - "model": "uapi/components/base/bitcode/activity/pack-activity-model.ts:6c610ebddc2949f3716b6bf8", + "spec": "BITCODE_SPEC_V43.md:be395a7424e1cda9f9a6db95", + "delta": "BITCODE_SPEC_V43_DELTA.md:bfdc638747d2b26e3f2e9198", + "notes": "BITCODE_SPEC_V43_NOTES.md:0b63f2a957175089051d64c5", + "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:8fec04876421410dd51ec879", + "roadmap": "SPECIFICATIONS_ROADMAP.md:8bbf62cb7261a6f2bc55e9bc", + "readme": "README.md:7a6eb7df6034f3f7ae18a4b6", + "protocolReadme": "packages/protocol/README.md:d338afce1b165d7da3bf9fc3", + "packageJson": "package.json:4eb71d824b7ac57b57e7eaa7", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:2fa0b3b3648ff6a9ce463290", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:232544cd143619fc8c2597a8", + "model": "uapi/components/base/bitcode/activity/pack-activity-model.ts:d848bb7a06bc8f4c46c2fc8a", "route": "uapi/app/api/packs/activity/route.ts:8a524f1dd16889733ce418a7", "page": "uapi/app/packs/page.tsx:1b3d7a1cc00500a13042e8dd", "client": "uapi/app/packs/PacksPageClient.tsx:d777458df28f47a360c6601c", @@ -157,10 +157,10 @@ "workspaceSurface": "uapi/components/base/bitcode/layout/workspace-surface.ts:12418d5db9c3ddad6d10c841", "publicCopy": "uapi/components/base/bitcode/layout/bitcode-public-copy.ts:3a45184ba8d2a4e71858c597", "publicExplainers": "uapi/components/base/bitcode/layout/bitcode-public-explainers.ts:282d915eac3c9f22ba8c07f1", - "packageIndex": "packages/protocol/src/index.js:533d73eb0b2cf01b4ac5c8b5", - "packageTypes": "packages/protocol/src/index.d.ts:307e3a474c62fcce0b015be6", + "packageIndex": "packages/protocol/src/index.js:da82c2ca8c20849a6fa0b931", + "packageTypes": "packages/protocol/src/index.d.ts:c04dd7f96f54a112437e39f9", "packageTest": "packages/protocol/test/v43-packs-activity-master-detail.test.js:baf3e44b2fbe4e62590c1824", - "uapiTest": "uapi/tests/packActivityModel.test.ts:a4c3deea857830cf57772a4d", + "uapiTest": "uapi/tests/packActivityModel.test.ts:7854762895ce63b0de8b6940", "generator": "scripts/generate-v43-packs-activity-master-detail.mjs:f342bc9b3ebb8de4e7aa2882", "checker": "scripts/check-v43-gate3-packs-activity-master-detail.mjs:5914a444235ffb15bb3272ad" }, diff --git a/.bitcode/v43-read-route-five-step-ux.json b/.bitcode/v43-read-route-five-step-ux.json index 8db8d460..077611f0 100644 --- a/.bitcode/v43-read-route-five-step-ux.json +++ b/.bitcode/v43-read-route-five-step-ux.json @@ -5,7 +5,7 @@ "currentTarget": "V42", "sourceSafetyVerdict": "source-safe-read-route-five-step-metadata", "generatedAt": "deterministic", - "artifactRoot": "v43-read-route-five-step-ux:e1bca988fc966aa1184f23c7", + "artifactRoot": "v43-read-route-five-step-ux:1248824b6cb0951af8b4ba2e", "passed": true, "stepIds": [ "request-read", @@ -117,16 +117,16 @@ ], "sourceRoots": { "activePointer": "BITCODE_SPEC.txt:75f3f6d81f999da998f40cb6", - "spec": "BITCODE_SPEC_V43.md:801a9fc58bfeddd4c71140ac", - "delta": "BITCODE_SPEC_V43_DELTA.md:3f942fe82b77655231ddb8ed", - "notes": "BITCODE_SPEC_V43_NOTES.md:540a19ddbba065526ebe0819", - "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:0555bc917046cbad7995966e", - "roadmap": "SPECIFICATIONS_ROADMAP.md:aa1ee2ac496931752e11cd7e", - "readme": "README.md:89be1d13f8d516dc263bd9cf", - "protocolReadme": "packages/protocol/README.md:ecd8d9539e4cacdc7d1a32e6", - "packageJson": "package.json:d5e4b589e6a81ff3ba867017", - "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:715ca61f2961eae12ae64bb8", - "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:c9d194dbe48ea4eafd75f225", + "spec": "BITCODE_SPEC_V43.md:be395a7424e1cda9f9a6db95", + "delta": "BITCODE_SPEC_V43_DELTA.md:bfdc638747d2b26e3f2e9198", + "notes": "BITCODE_SPEC_V43_NOTES.md:0b63f2a957175089051d64c5", + "parity": "BITCODE_SPEC_V43_PARITY_MATRIX.md:8fec04876421410dd51ec879", + "roadmap": "SPECIFICATIONS_ROADMAP.md:8bbf62cb7261a6f2bc55e9bc", + "readme": "README.md:7a6eb7df6034f3f7ae18a4b6", + "protocolReadme": "packages/protocol/README.md:d338afce1b165d7da3bf9fc3", + "packageJson": "package.json:4eb71d824b7ac57b57e7eaa7", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:2fa0b3b3648ff6a9ce463290", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:232544cd143619fc8c2597a8", "routeModel": "uapi/app/read/read-route-model.ts:a9324b0d92cd6c5176fa7940", "page": "uapi/app/read/page.tsx:37a723ed87feac05c0504249", "client": "uapi/app/read/ReadPageClient.tsx:d14f0fbb65af0edfa68d774f", @@ -138,8 +138,8 @@ "publicCopy": "uapi/components/base/bitcode/layout/bitcode-public-copy.ts:3a45184ba8d2a4e71858c597", "publicExplainers": "uapi/components/base/bitcode/layout/bitcode-public-explainers.ts:282d915eac3c9f22ba8c07f1", "footer": "uapi/components/base/bitcode/layout/footer.tsx:44749b98e5f63e7ac8f9f574", - "packageIndex": "packages/protocol/src/index.js:533d73eb0b2cf01b4ac5c8b5", - "packageTypes": "packages/protocol/src/index.d.ts:307e3a474c62fcce0b015be6", + "packageIndex": "packages/protocol/src/index.js:da82c2ca8c20849a6fa0b931", + "packageTypes": "packages/protocol/src/index.d.ts:c04dd7f96f54a112437e39f9", "packageTest": "packages/protocol/test/v43-read-route-five-step-ux.test.js:fedf825a49244098367f6388", "routeModelTest": "uapi/tests/readRouteModel.test.ts:47d613cef4ab6dce86846c8f", "pageTest": "uapi/tests/readPageClient.test.tsx:9bd621c200f02082d0159cc5", diff --git a/.bitcode/v43-route-vocabulary-inventory.json b/.bitcode/v43-route-vocabulary-inventory.json index a685c692..8e000bd7 100644 --- a/.bitcode/v43-route-vocabulary-inventory.json +++ b/.bitcode/v43-route-vocabulary-inventory.json @@ -5,7 +5,7 @@ "currentTarget": "V42", "sourceSafetyVerdict": "source-safe-route-vocabulary-inventory-metadata", "generatedAt": "deterministic", - "artifactRoot": "v43-route-vocabulary-inventory:70e81bcca62f07362dfcb38c", + "artifactRoot": "v43-route-vocabulary-inventory:7b7ee161c29ff5abb1bd7427", "passed": true, "tokenIds": [ "route:/exchange", @@ -2081,7 +2081,7 @@ }, { "path": "BITCODE_SPEC_V43.md", - "pathRoot": "v43-route-vocabulary-file:460d8c8dbf26f869e8308f65", + "pathRoot": "v43-route-vocabulary-file:9984fe8f4db39e1905d87b6b", "categories": [ "doc", "spec", @@ -2090,7 +2090,7 @@ "tokenCounts": { "route:/exchange": 5, "route:/terminal": 5, - "route:/packs": 18, + "route:/packs": 21, "route:/read": 18, "route:/deposit": 15, "symbol:Exchange": 4, @@ -2104,14 +2104,14 @@ "word:terminal": 5, "word:self-referential": 4 }, - "totalMatches": 125, + "totalMatches": 128, "sourceSafeMetadataOnly": true, "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, { "path": "BITCODE_SPEC_V43_DELTA.md", - "pathRoot": "v43-route-vocabulary-file:d0228ac3d68cb6d5681900dd", + "pathRoot": "v43-route-vocabulary-file:82d8d77824f1a5660bd42b4e", "categories": [ "doc", "spec", @@ -2120,9 +2120,9 @@ "tokenCounts": { "route:/exchange": 4, "route:/terminal": 2, - "route:/packs": 12, + "route:/packs": 13, "route:/read": 8, - "route:/deposit": 9, + "route:/deposit": 10, "symbol:Exchange": 2, "symbol:Terminal": 4, "symbol:Reading": 6, @@ -2133,14 +2133,14 @@ "word:terminal": 2, "word:self-referential": 3 }, - "totalMatches": 62, + "totalMatches": 64, "sourceSafeMetadataOnly": true, "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, { "path": "BITCODE_SPEC_V43_NOTES.md", - "pathRoot": "v43-route-vocabulary-file:233ba30dfc52405c5c397acb", + "pathRoot": "v43-route-vocabulary-file:52986106dbfa43471d544280", "categories": [ "doc", "spec", @@ -2149,7 +2149,7 @@ "tokenCounts": { "route:/exchange": 2, "route:/terminal": 1, - "route:/packs": 8, + "route:/packs": 9, "route:/read": 5, "route:/deposit": 4, "symbol:Exchange": 1, @@ -2163,33 +2163,34 @@ "word:terminal": 1, "word:self-referential": 2 }, - "totalMatches": 38, + "totalMatches": 39, "sourceSafeMetadataOnly": true, "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, { "path": "BITCODE_SPEC_V43_PARITY_MATRIX.md", - "pathRoot": "v43-route-vocabulary-file:08f0c7cb30e04720cbe51840", + "pathRoot": "v43-route-vocabulary-file:9c29ea9ed1747259cb5cf1ae", "categories": [ "doc", - "spec" + "spec", + "telemetry" ], "tokenCounts": { "route:/exchange": 3, "route:/terminal": 1, - "route:/packs": 7, + "route:/packs": 11, "route:/read": 6, - "route:/deposit": 8, + "route:/deposit": 9, "symbol:Packs": 1, "symbol:Reading": 1, - "symbol:PackActivity": 4, + "symbol:PackActivity": 5, "symbol:DepositAssetPackOption": 1, "word:exchange": 3, "word:terminal": 1, "word:self-referential": 2 }, - "totalMatches": 38, + "totalMatches": 44, "sourceSafeMetadataOnly": true, "rawSourceTextSerialized": false, "sourceSnippetSerialized": false @@ -2314,7 +2315,7 @@ }, { "path": "README.md", - "pathRoot": "v43-route-vocabulary-file:ff44a442f13c63b599e9d939", + "pathRoot": "v43-route-vocabulary-file:2c31822c1f25522ed86613c9", "categories": [ "doc", "telemetry" @@ -2322,7 +2323,7 @@ "tokenCounts": { "route:/exchange": 5, "route:/terminal": 5, - "route:/packs": 10, + "route:/packs": 11, "route:/read": 8, "route:/deposit": 7, "symbol:Exchange": 16, @@ -2336,14 +2337,14 @@ "word:terminal": 7, "word:self-referential": 2 }, - "totalMatches": 148, + "totalMatches": 149, "sourceSafeMetadataOnly": true, "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, { "path": "SPECIFICATIONS_ROADMAP.md", - "pathRoot": "v43-route-vocabulary-file:57b411ed167476617c185746", + "pathRoot": "v43-route-vocabulary-file:317e1d3bf5a19dd41302913f", "categories": [ "doc", "spec", @@ -2352,9 +2353,9 @@ "tokenCounts": { "route:/exchange": 4, "route:/terminal": 3, - "route:/packs": 10, - "route:/read": 10, - "route:/deposit": 8, + "route:/packs": 11, + "route:/read": 11, + "route:/deposit": 9, "symbol:Exchange": 25, "symbol:Terminal": 42, "symbol:Reading": 59, @@ -2363,9 +2364,9 @@ "symbol:DepositAssetPackOption": 1, "word:exchange": 20, "word:terminal": 6, - "word:self-referential": 3 + "word:self-referential": 4 }, - "totalMatches": 203, + "totalMatches": 207, "sourceSafeMetadataOnly": true, "rawSourceTextSerialized": false, "sourceSnippetSerialized": false @@ -4795,6 +4796,23 @@ "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, + { + "path": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts", + "pathRoot": "v43-route-vocabulary-file:4b924edfa6b14fbd2fd2ae95", + "categories": [ + "package", + "telemetry", + "test" + ], + "tokenCounts": { + "route:/packs": 1, + "route:/deposit": 1 + }, + "totalMatches": 2, + "sourceSafeMetadataOnly": true, + "rawSourceTextSerialized": false, + "sourceSnippetSerialized": false + }, { "path": "packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-policy.test.ts", "pathRoot": "v43-route-vocabulary-file:afe8e3fb4d29f1b988768184", @@ -5172,6 +5190,23 @@ "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, + { + "path": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts", + "pathRoot": "v43-route-vocabulary-file:18ffce5cb1faa6fe1648d16e", + "categories": [ + "package", + "telemetry" + ], + "tokenCounts": { + "route:/packs": 5, + "route:/deposit": 3, + "symbol:DepositAssetPackOption": 4 + }, + "totalMatches": 12, + "sourceSafeMetadataOnly": true, + "rawSourceTextSerialized": false, + "sourceSnippetSerialized": false + }, { "path": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-policy.ts", "pathRoot": "v43-route-vocabulary-file:c16791dbd3acae9a9143790b", @@ -5920,7 +5955,7 @@ }, { "path": "packages/protocol/README.md", - "pathRoot": "v43-route-vocabulary-file:92166b0423e4bbda7dc2f0ab", + "pathRoot": "v43-route-vocabulary-file:450e9569c135f17e69e2af65", "categories": [ "doc", "package", @@ -5929,7 +5964,7 @@ "tokenCounts": { "route:/exchange": 4, "route:/terminal": 2, - "route:/packs": 7, + "route:/packs": 8, "route:/read": 5, "route:/deposit": 3, "symbol:Exchange": 17, @@ -5942,7 +5977,7 @@ "word:terminal": 6, "word:self-referential": 1 }, - "totalMatches": 132, + "totalMatches": 133, "sourceSafeMetadataOnly": true, "rawSourceTextSerialized": false, "sourceSnippetSerialized": false @@ -7138,6 +7173,22 @@ "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, + { + "path": "packages/protocol/src/canonical/v43-deposit-option-admission.js", + "pathRoot": "v43-route-vocabulary-file:17feaaf05a6ad871b15a66f1", + "categories": [ + "package", + "telemetry" + ], + "tokenCounts": { + "route:/packs": 2, + "route:/deposit": 2 + }, + "totalMatches": 4, + "sourceSafeMetadataOnly": true, + "rawSourceTextSerialized": false, + "sourceSnippetSerialized": false + }, { "path": "packages/protocol/src/canonical/v43-deposit-policy-compensation.js", "pathRoot": "v43-route-vocabulary-file:cd9271147b9b9528c7da1b1e", @@ -7697,6 +7748,22 @@ "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, + { + "path": "packages/protocol/test/v43-deposit-option-admission.test.js", + "pathRoot": "v43-route-vocabulary-file:30ebfe4eeb3561b0ac714a1f", + "categories": [ + "package", + "telemetry", + "test" + ], + "tokenCounts": { + "route:/packs": 1 + }, + "totalMatches": 1, + "sourceSafeMetadataOnly": true, + "rawSourceTextSerialized": false, + "sourceSnippetSerialized": false + }, { "path": "packages/protocol/test/v43-read-route-five-step-ux.test.js", "pathRoot": "v43-route-vocabulary-file:806897d387b0667a60afa16b", @@ -10002,6 +10069,22 @@ "rawSourceTextSerialized": false, "sourceSnippetSerialized": false }, + { + "path": "scripts/check-v43-gate7-deposit-option-admission.mjs", + "pathRoot": "v43-route-vocabulary-file:e663063815fd17742daad76f", + "categories": [ + "script", + "telemetry" + ], + "tokenCounts": { + "route:/packs": 3, + "route:/deposit": 3 + }, + "totalMatches": 6, + "sourceSafeMetadataOnly": true, + "rawSourceTextSerialized": false, + "sourceSnippetSerialized": false + }, { "path": "scripts/code-review/README.md", "pathRoot": "v43-route-vocabulary-file:fef2886ffeed000a3327379c", @@ -11358,7 +11441,9 @@ { "path": "uapi/app/deposit/deposit-route-model.ts", "pathRoot": "v43-route-vocabulary-file:d2f45e9ac13289222bb7dd8d", - "categories": [], + "categories": [ + "telemetry" + ], "tokenCounts": { "route:/deposit": 3 }, @@ -14055,14 +14140,14 @@ }, { "path": "uapi/tests/depositRouteModel.test.ts", - "pathRoot": "v43-route-vocabulary-file:1146183a337e517be808d24b", + "pathRoot": "v43-route-vocabulary-file:ac3d550f04ed47a3919ade98", "categories": [ "test" ], "tokenCounts": { - "route:/deposit": 3 + "route:/deposit": 5 }, - "totalMatches": 3, + "totalMatches": 5, "sourceSafeMetadataOnly": true, "rawSourceTextSerialized": false, "sourceSnippetSerialized": false @@ -15614,34 +15699,34 @@ } ], "coverage": { - "sourceFileCount": 918, + "sourceFileCount": 923, "tokenTotals": { "route:/exchange": 147, "route:/terminal": 1475, - "route:/packs": 160, - "route:/read": 294, - "route:/deposit": 153, + "route:/packs": 184, + "route:/read": 295, + "route:/deposit": 167, "symbol:Exchange": 1964, "symbol:Terminal": 3091, "symbol:Packs": 50, "symbol:Reading": 1334, "symbol:Depositing": 243, - "symbol:PackActivity": 28, - "symbol:DepositAssetPackOption": 29, + "symbol:PackActivity": 29, + "symbol:DepositAssetPackOption": 33, "word:exchange": 1708, "word:terminal": 3735, - "word:self-referential": 30 + "word:self-referential": 31 }, "categoryTotals": { "route": 14, "component": 150, - "test": 222, + "test": 224, "doc": 169, "api": 43, - "telemetry": 655, + "telemetry": 662, "workflow": 12, - "script": 120, - "package": 317, + "script": 121, + "package": 321, "spec": 76 }, "routeVocabularyInventoryComplete": true, diff --git a/.github/workflows/bitcode-canon-quality.yml b/.github/workflows/bitcode-canon-quality.yml index 20897797..d50ebab2 100644 --- a/.github/workflows/bitcode-canon-quality.yml +++ b/.github/workflows/bitcode-canon-quality.yml @@ -343,6 +343,9 @@ jobs: if [ -f scripts/check-v43-gate6-deposit-policy-compensation.mjs ]; then node scripts/check-v43-gate6-deposit-policy-compensation.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests fi + if [ -f scripts/check-v43-gate7-deposit-option-admission.mjs ]; then + node scripts/check-v43-gate7-deposit-option-admission.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests + fi fi else echo "Unexpected BITCODE_SPEC.txt pointer: $POINTER" >&2 diff --git a/.github/workflows/bitcode-gate-quality.yml b/.github/workflows/bitcode-gate-quality.yml index 1a3a268f..00cb67a4 100644 --- a/.github/workflows/bitcode-gate-quality.yml +++ b/.github/workflows/bitcode-gate-quality.yml @@ -472,6 +472,9 @@ jobs: if [ -f scripts/check-v43-gate6-deposit-policy-compensation.mjs ]; then node scripts/check-v43-gate6-deposit-policy-compensation.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests fi + if [ -f scripts/check-v43-gate7-deposit-option-admission.mjs ]; then + node scripts/check-v43-gate7-deposit-option-admission.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests + fi fi else echo "Unexpected BITCODE_SPEC.txt pointer: $POINTER" >&2 @@ -520,6 +523,9 @@ jobs: if [ -f scripts/check-v43-gate6-deposit-policy-compensation.mjs ]; then node scripts/check-v43-gate6-deposit-policy-compensation.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests fi + if [ -f scripts/check-v43-gate7-deposit-option-admission.mjs ]; then + node scripts/check-v43-gate7-deposit-option-admission.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests + fi pnpm --filter @bitcode/protocol test pnpm --filter @bitcode/btd exec jest --config jest.config.cjs --runInBand --forceExit pnpm --filter @bitcode/pipeline-asset-pack exec jest --config jest.config.cjs --passWithNoTests --forceExit diff --git a/BITCODE_SPEC_V43.md b/BITCODE_SPEC_V43.md index 9883a127..e72dfe93 100644 --- a/BITCODE_SPEC_V43.md +++ b/BITCODE_SPEC_V43.md @@ -201,6 +201,21 @@ and `/packs` synchronization remain Gate 7 responsibilities. Gate 7 must implement option accept/reject/resynthesize decisions, admission receipts, Depository indexing, storage projection, compensation preview, telemetry, and `/packs` activity synchronization for approved deposit AssetPacks. +Gate 7 closes with `DepositAssetPackOptionAdmissionReport`, +`DepositOptionAdmissionReceipt`, `DepositOptionReviewDecision`, and the +generated `.bitcode/v43-deposit-option-admission.json` proof. The admission +report consumes source-safe Gate 5 option synthesis plus Gate 6 policy scoring +and emits per-option receipts for pending review, depositor approval, depositor +rejection, depositor-requested resynthesis, and policy-blocked outcomes. Only +approved, policy-eligible, source-safe options are admitted to the Depository. +Admitted options receive source-safe semantic, lexical, and metadata index +projection roots, object-storage metadata roots, external raw-source pointer +roots, preserved BTC source-to-shares compensation preview, execution-stream +telemetry roots, and `/packs` synchronization as `depository-assetpack` +activity. Gate 7 does not mint BTD, transfer rights, broadcast settlement, or +show unpaid AssetPack source; those remain gated by future accepted Need-Fit +settlement. + ## V43 Gate 8 UX/UI Product Excellence Pass Gate 8 must improve route layout, visual hierarchy, copy, progressive disclosure, execution stream usage, empty/error/loading states, keyboard/responsive behavior, and reusable themed components across `/packs`, `/read`, and `/deposit`. It must remove in-app self-referential explanatory copy outside public docs. @@ -217,13 +232,13 @@ Gate 10 must bind every V43 artifact, workflow, generated proof, docs update, ro ### Depositing and asset supply -- Current canonical objects and emitted artifacts: DepositRouteSession, DepositAssetPackOption, DepositOptionSynthesisRequest, DepositAssetPackOptionSynthesis, DepositAssetPackOptionPolicy, DepositAssetPackOptionPolicyReport, Depository record, source admission proof, compensation preview, `.bitcode/v43-deposit-route-options.json`, and `.bitcode/v43-deposit-policy-compensation.json`. -- Current algorithms and derivation rules: connected-source selection, depositor instruction rooting, source path rooting, Depository demand signal roots, Reading demand signal roots, existing supply signal roots, option grouping, measurement projection, source-safe review posture, source criticality scoring, weighted demand scoring, deterministic gross-minus-cost ROI scoring, estimate-only BTD potential, and future-reader BTC source-to-shares compensation route preview. -- Current invariants and fail-closed conditions: invalid repository binding, missing source hints, critical-source block, negative expected value warning/block, missing wallet compensation repair posture, admission/indexing deferred to Gate 7, and protected-source leak. -- Current proof obligations: source-safe option synthesis, option policy report, option review boundary, future admission boundary, compensation route preview, BTD mint boundary, and activity synchronization. -- Current source-bearing implementation basis: `/deposit` route, `DepositRouteSession`, asset-pack pipeline package option synthesis, asset-pack package policy report, existing deposit composer, source-safe route tests, package tests, and generated protocol artifacts. -- Current validating commands and parity basis: `pnpm run check:v43-gate5`, `pnpm run check:v43-gate6`, package tests, API tests, and generated artifacts. -- Current accepted boundaries: options are not BTD mints and do not expose source to readers before settlement. +- Current canonical objects and emitted artifacts: DepositRouteSession, DepositAssetPackOption, DepositOptionSynthesisRequest, DepositAssetPackOptionSynthesis, DepositAssetPackOptionPolicy, DepositAssetPackOptionPolicyReport, DepositAssetPackOptionAdmissionReport, DepositOptionAdmissionReceipt, DepositOptionReviewDecision, Depository record projection, source admission proof, compensation preview, `.bitcode/v43-deposit-route-options.json`, `.bitcode/v43-deposit-policy-compensation.json`, and `.bitcode/v43-deposit-option-admission.json`. +- Current algorithms and derivation rules: connected-source selection, depositor instruction rooting, source path rooting, Depository demand signal roots, Reading demand signal roots, existing supply signal roots, option grouping, measurement projection, source-safe review posture, source criticality scoring, weighted demand scoring, deterministic gross-minus-cost ROI scoring, estimate-only BTD potential, future-reader BTC source-to-shares compensation route preview, depositor approve/reject/resynthesis decisions, approved-policy-eligible admission, Depository index projection, storage metadata projection, and `/packs` activity synchronization. +- Current invariants and fail-closed conditions: invalid repository binding, missing source hints, critical-source block, negative expected value warning/block, missing wallet compensation repair posture, missing depositor review, rejected option, resynthesis request, policy-blocked option, and protected-source leak. +- Current proof obligations: source-safe option synthesis, option policy report, option review boundary, admission receipts, compensation route preview, Depository index/storage projections, BTD mint boundary, telemetry, and activity synchronization. +- Current source-bearing implementation basis: `/deposit` route, `DepositRouteSession`, asset-pack pipeline package option synthesis, asset-pack package policy report, asset-pack package admission report, existing deposit composer, `/packs` activity projection, source-safe route tests, package tests, and generated protocol artifacts. +- Current validating commands and parity basis: `pnpm run check:v43-gate5`, `pnpm run check:v43-gate6`, `pnpm run check:v43-gate7`, package tests, API tests, and generated artifacts. +- Current accepted boundaries: options are not BTD mints and do not expose source to readers before settlement; admitted deposit AssetPacks publish only source-safe metadata and external source pointer roots until a future paid Need-Fit settlement. ### Reading and prompt/inference ownership diff --git a/BITCODE_SPEC_V43_DELTA.md b/BITCODE_SPEC_V43_DELTA.md index 4146d1c4..83275c43 100644 --- a/BITCODE_SPEC_V43_DELTA.md +++ b/BITCODE_SPEC_V43_DELTA.md @@ -108,6 +108,21 @@ requires compensation repair. Admission, Depository indexing, storage projection, `/packs` synchronization, and final approve/reject/resynthesize decisions remain Gate 7 boundaries. +## Gate 7 delta closure + +Gate 7 adds `DepositAssetPackOptionAdmissionReport`, +`DepositOptionAdmissionReceipt`, `DepositOptionReviewDecision`, the generated +`.bitcode/v43-deposit-option-admission.json` artifact, package exports, +protocol tests, workflow checks, `/deposit` approve/reject/resynthesize controls, +admission readback, and `/packs` activity synchronization. Admission consumes +source-safe option synthesis and policy reports; only depositor-approved, +policy-eligible options enter the Depository. Admitted options project +measurement/metadata indexes, object-storage metadata records, external +raw-source pointer roots, BTC source-to-shares compensation preview continuity, +and execution-stream telemetry. Gate 7 still does not mint BTD, transfer rights, +or disclose unpaid AssetPack source; those boundaries stay with future +Need-Fit settlement. + ## Commit-Body Direction V43 gate commits should state the route/product surface changed, the protocol objects preserved, the proof/test commands run, and the source-safety boundaries maintained. Gate PR titles must begin with `V43 Gate N:`. diff --git a/BITCODE_SPEC_V43_NOTES.md b/BITCODE_SPEC_V43_NOTES.md index b0a74cc2..b316f29e 100644 --- a/BITCODE_SPEC_V43_NOTES.md +++ b/BITCODE_SPEC_V43_NOTES.md @@ -63,6 +63,16 @@ Gate 7 remains responsible for depositor approval, reject/resynthesize decisions, Depository admission receipts, indexing, storage projection, telemetry, and `/packs` activity synchronization. +Gate 7 closes depositor approval and admission as source-safe metadata. +`DepositAssetPackOptionAdmissionReport` records review decisions, admission +receipts, Depository index projections, object-storage metadata projections, +BTC source-to-shares compensation preview continuity, execution-stream +telemetry roots, and `/packs` synchronization as `depository-assetpack` +activity. Rejected, pending, resynthesis-requested, or policy-blocked options +must not enter the Depository. Admitted options still do not mint BTD, transfer +rights, reveal unpaid AssetPack source, or serialize prompts, provider +responses, wallet material, or private settlement payloads. + ## V43 copy boundary Outside public docs, avoid self-referential copy such as text explaining that a component is powerful or that Bitcode is doing a thing. Use clear route names, labels, status rows, measurement summaries, empty states, and expandable proof metadata. Public docs may explain the protocol; product UI should operate it. diff --git a/BITCODE_SPEC_V43_PARITY_MATRIX.md b/BITCODE_SPEC_V43_PARITY_MATRIX.md index fa471553..794de11e 100644 --- a/BITCODE_SPEC_V43_PARITY_MATRIX.md +++ b/BITCODE_SPEC_V43_PARITY_MATRIX.md @@ -8,7 +8,7 @@ - Prior canonical anchor: `BITCODE_SPEC_V42.md` - Prior generated proof appendix: `BITCODE_SPEC_V42_PROVEN.md` - Generated structured artifact inventory: `.bitcode/v43-spec-family-report.json`, `.bitcode/v43-canonical-input-report.json`, and future V43 gate artifacts -- Source parity state: V43 Gate 1 parity is documentation/workflow posture; V43 Gate 2 parity is source-safe inventory and migration planning; V43 Gate 3 parity is PackActivity source-safe master-detail implementation; V43 Gate 4 parity is `/read`; V43 Gate 5 parity is `/deposit` and source-safe DepositAssetPackOption synthesis; V43 Gate 6 parity is source-safe deposit policy and compensation scoring +- Source parity state: V43 Gate 1 parity is documentation/workflow posture; V43 Gate 2 parity is source-safe inventory and migration planning; V43 Gate 3 parity is PackActivity source-safe master-detail implementation; V43 Gate 4 parity is `/read`; V43 Gate 5 parity is `/deposit` and source-safe DepositAssetPackOption synthesis; V43 Gate 6 parity is source-safe deposit policy and compensation scoring; V43 Gate 7 parity is source-safe deposit option review, admission, indexing, storage projection, telemetry, and `/packs` synchronization ## Purpose @@ -28,7 +28,7 @@ Audit V43 against `BITCODE_SPEC_V43.md`, V42 active canon, route source, package | Read route | `ReadRouteSession` and five-step Reading UX own Read Request, synthesized Need review, accepted-Need-gated Finding Fits, source-safe AssetPack preview, BTC settlement, and delivery posture | `packages/protocol/src/canonical/v43-read-route-five-step-ux.js`, `.bitcode/v43-read-route-five-step-ux.json`, `uapi/app/read`, `uapi/app/read/read-route-model.ts` | implemented | | Deposit route | `DepositRouteSession`, `/deposit`, source-safe connected-source option synthesis, source-safe option cards, route tests, package tests, generated proof, and retained deposit composer reuse | `packages/protocol/src/canonical/v43-deposit-route-options.js`, `.bitcode/v43-deposit-route-options.json`, `uapi/app/deposit`, `packages/pipelines/asset-pack/src/deposit-asset-pack-options.ts` | implemented | | Criticality/ROI policy | Source criticality, demand, ROI, BTD potential, and BTC source-to-shares compensation posture | `packages/protocol/src/canonical/v43-deposit-policy-compensation.js`, `.bitcode/v43-deposit-policy-compensation.json`, `packages/pipelines/asset-pack/src/deposit-asset-pack-option-policy.ts`, `/deposit` policy readback | implemented | -| Admission sync | Approved deposit options enter Depository and `/packs` activity | future Gate 7 artifact | draft-required | +| Deposit option admission | Approved, policy-eligible deposit options enter source-safe Depository projections and `/packs` activity while rejected, resynthesis, pending, and policy-blocked options stay out | `packages/protocol/src/canonical/v43-deposit-option-admission.js`, `.bitcode/v43-deposit-option-admission.json`, `packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts`, `/deposit` admission readback, `/packs` activity model | implemented | | UX excellence | Self-explanatory, polished, progressive-detail UI without self-referential product copy | future Gate 8 artifact | draft-required | | Rehearsal | Local/staging-testnet cross-route path verifies deposit, read, packs, settlement, compensation, delivery | future Gate 9 artifact | draft-required | | Promotion readiness | V43 generated proof and active V43 / draft V44 posture ready | future Gate 10 artifact | draft-required | @@ -46,7 +46,8 @@ Audit V43 against `BITCODE_SPEC_V43.md`, V42 active canon, route source, package | Gate 4 Read route proof | `V43ReadRouteFiveStepUx` exports, generated artifact, source-safe route model/UI tests, workflow checks, and `check:v43-gate4` exist | implemented | | Gate 5 Deposit route proof | `V43DepositRouteOptions` exports, generated artifact, source-safe route model/UI tests, asset-pack option synthesis tests, workflow checks, and `check:v43-gate5` exist | implemented | | Gate 6 Deposit policy proof | `DepositAssetPackOptionPolicyReport`, generated artifact, source-safe route model/UI tests, asset-pack policy tests, workflow checks, and `check:v43-gate6` exist | implemented | -| Implementation | Route and pipeline source changes are not part of Gates 1 or 2; Gate 3 implements only `/packs` and PackActivity; Gate 4 implements `/read`; Gate 5 implements `/deposit` option synthesis; Gate 6 implements policy scoring while leaving approval/admission/indexing to Gate 7 | accepted boundary | +| Gate 7 Deposit option admission proof | `DepositAssetPackOptionAdmissionReport`, generated artifact, source-safe route model/UI tests, asset-pack admission tests, PackActivity sync tests, workflow checks, and `check:v43-gate7` exist | implemented | +| Implementation | Route and pipeline source changes are not part of Gates 1 or 2; Gate 3 implements only `/packs` and PackActivity; Gate 4 implements `/read`; Gate 5 implements `/deposit` option synthesis; Gate 6 implements policy scoring; Gate 7 implements depositor decisions, admission receipts, index/storage projections, telemetry, and `/packs` synchronization | accepted boundary | ## V43 accepted boundaries @@ -58,7 +59,10 @@ their owning implementation gates. Gate 5 may implement `/deposit` and source-safe option synthesis. Gate 6 may decide source-safe criticality, demand, ROI, BTD potential, and compensation posture, but it must not approve, admit, index, store, or synchronize deposit AssetPacks into `/packs`; those remain Gate -7 responsibilities. +7 responsibilities. Gate 7 may approve, reject, request resynthesis, admit +policy-eligible options, project source-safe index/storage metadata, and +synchronize admitted options into `/packs`; it must not mint BTD, transfer +rights, broadcast settlement, or disclose unpaid AssetPack source. ## V43 completion condition diff --git a/README.md b/README.md index 488c049b..c9f2df0f 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,17 @@ expected value is not treated as positive-ROI supply, and Gate 7 still owns approval, admission, indexing, storage projection, telemetry, and `/packs` activity synchronization. +V43 Gate 7 adds `DepositAssetPackOptionAdmissionReport`, +`.bitcode/v43-deposit-option-admission.json`, +`generate:v43-deposit-option-admission`, +`check:v43-deposit-option-admission`, and `check:v43-gate7`. It implements +depositor approve/reject/resynthesis decisions, source-safe admission receipts, +Depository index and storage projections, BTC source-to-shares compensation +preview continuity, execution-stream telemetry, and `/packs` +`depository-assetpack` synchronization for approved policy-eligible deposit +options. It still does not mint BTD, transfer rights, or reveal unpaid +AssetPack source. + Exchange is inherited V36 canon: market-wide activity master-detail, buy/sell/ bid/ask/cancel/accept/settle/history flows, AssetPack range trading, rights-transfer review, pricing/liquidity/wrapper analysis, settlement diff --git a/SPECIFICATIONS_ROADMAP.md b/SPECIFICATIONS_ROADMAP.md index 366d3724..8da920ef 100644 --- a/SPECIFICATIONS_ROADMAP.md +++ b/SPECIFICATIONS_ROADMAP.md @@ -5,8 +5,8 @@ - Current active canonical pointer: `BITCODE_SPEC.txt` -> `V42` - Current active canon: `BITCODE_SPEC_V42.md` - Current draft target: `BITCODE_SPEC_V43.md`. -- Current working gate: V43 Gate 6 Source Criticality, Demand, ROI, And Compensation Policy. -- Next queued work after V43 Gate 6: deposit option review, approval, admission receipts, Depository indexing, storage projection, telemetry, and `/packs` activity synchronization. +- Current working gate: V43 Gate 7 Deposit Option Review, Admission, And Pack Activity Synchronization. +- Next queued work after V43 Gate 7: UX/UI product excellence across `/packs`, `/read`, and `/deposit`, including polished route layout, progressive detail, execution-stream clarity, empty/error/loading states, responsive/keyboard behavior, and removal of in-app self-referential copy. - Latest closed version: V42 Reliable MVP Experience, which promoted shortest-path Depositing, five-step Reading, ReadNeed review/resynthesis, ReadFitsFinding source-safe preview and quote, settlement rights transfer, repository delivery, AI-reading demonstration, local/staging MVP rehearsal, and V42 promotion readiness. - Recent V42 canonical promotion anchor: V42 canonical promotion updated `BITCODE_SPEC.txt` to `V42`, generated `BITCODE_SPEC_V42_PROVEN.md`, preserved active V42 / draft V43 runtime posture, and closed reliable MVP experience canon. - Recent V42 opening anchor: reliable MVP experience opens over promoted V41 with V42 SPEC, DELTA, NOTES, and PARITY files, `check:v42-gate1`, active V41 / draft V42 posture, and a nine-gate plan for shortest-path Depositing, five-step Reading, ReadNeed product closure, ReadFitsFinding preview and quote closure, settlement and repository delivery, AI-reading demonstration, local/staging rehearsal, and promotion readiness. @@ -23,6 +23,7 @@ - V43 Gate 4 closure anchor: route-product cleanup now owns package-backed `V43ReadRouteFiveStepUx`, deterministic `.bitcode/v43-read-route-five-step-ux.json`, `ReadRouteSession`, `/read`, five-step Reading UX, Read Request, synthesized Need review, accepted-Need-gated Finding Fits, source-safe AssetPack preview, BTC settlement/delivery posture, retained execution stream readback, route/nav/footer ownership, focused UAPI/protocol tests, workflow wiring, and `check:v43-gate4`. - V43 Gate 5 closure anchor: route-product cleanup now owns package-backed `V43DepositRouteOptions`, deterministic `.bitcode/v43-deposit-route-options.json`, `DepositRouteSession`, `/deposit`, five-step Depositing UX, connected-source option synthesis, `DepositAssetPackOptionSynthesis`, source-safe AssetPack option cards, option measurement roots, demand signal roots, retained deposit composer reuse, route/nav/footer ownership, focused UAPI/package/protocol tests, workflow wiring, and `check:v43-gate5`. - V43 Gate 6 closure anchor: route-product cleanup now owns package-backed `DepositAssetPackOptionPolicyReport`, deterministic `.bitcode/v43-deposit-policy-compensation.json`, source criticality scoring, likely demand scoring, deterministic estimated-gross-minus-development-cost ROI, estimate-only BTD potential, future-reader BTC source-to-shares compensation route preview, critical-source pre-admission blocking, `/deposit` policy readback, source-safe route/package/protocol tests, workflow wiring, and `check:v43-gate6`. +- V43 Gate 7 closure anchor: route-product cleanup now owns package-backed `DepositAssetPackOptionAdmissionReport`, deterministic `.bitcode/v43-deposit-option-admission.json`, approve/reject/resynthesis review decisions, source-safe admission receipts, approved-policy-eligible Depository admission, semantic/lexical/metadata index projection, object-storage metadata and external source pointer projection, BTC source-to-shares compensation preview continuity, execution-stream telemetry, `/packs` `depository-assetpack` synchronization, focused route/package/activity/protocol tests, workflow wiring, and `check:v43-gate7`. - Recent V41 closure anchor: V41 canonical promotion updated `BITCODE_SPEC.txt` to `V41`, generated `BITCODE_SPEC_V41_PROVEN.md`, preserved active V41 / draft V42 runtime posture, and closed prompt-program excellence canon. - Recent V40 closure anchor: V40 canonical promotion updated `BITCODE_SPEC.txt` to `V40`, generated `BITCODE_SPEC_V40_PROVEN.md`, preserved active V40 / draft V41 runtime posture, and closed exhaustive commercial application testing canon. - Recent V39 closure anchor: V39 canonical promotion updated `BITCODE_SPEC.txt` to `V39`, generated `BITCODE_SPEC_V39_PROVEN.md`, preserved active V39 / draft V40 runtime posture, and closed commercial Reading readiness canon. diff --git a/package.json b/package.json index acff48d9..1e81929a 100644 --- a/package.json +++ b/package.json @@ -345,6 +345,9 @@ "generate:v43-deposit-policy-compensation": "node scripts/generate-v43-deposit-policy-compensation.mjs", "check:v43-deposit-policy-compensation": "node scripts/generate-v43-deposit-policy-compensation.mjs --check", "check:v43-gate6": "node scripts/check-v43-gate6-deposit-policy-compensation.mjs", + "generate:v43-deposit-option-admission": "node scripts/generate-v43-deposit-option-admission.mjs", + "check:v43-deposit-option-admission": "node scripts/generate-v43-deposit-option-admission.mjs --check", + "check:v43-gate7": "node scripts/check-v43-gate7-deposit-option-admission.mjs", "generate:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs", "check:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs --check", "check:v38-gate2": "node scripts/check-v38-gate2-inference-surface-inventory.mjs", diff --git a/packages/pipelines/asset-pack/package.json b/packages/pipelines/asset-pack/package.json index c597bbb4..1a20b60d 100644 --- a/packages/pipelines/asset-pack/package.json +++ b/packages/pipelines/asset-pack/package.json @@ -14,6 +14,7 @@ "./reading-local-staging-rehearsal": "./src/reading-local-staging-rehearsal.ts", "./deposit-asset-pack-options": "./src/deposit-asset-pack-options.ts", "./deposit-asset-pack-option-policy": "./src/deposit-asset-pack-option-policy.ts", + "./deposit-asset-pack-option-admission": "./src/deposit-asset-pack-option-admission.ts", "./read-need": "./src/read-need.ts", "./read-need-review-resynthesis": "./src/read-need-review-resynthesis.ts", "./read-fits-finding-runtime": "./src/read-fits-finding-runtime.ts", diff --git a/packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts b/packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts new file mode 100644 index 00000000..d1bc5e78 --- /dev/null +++ b/packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts @@ -0,0 +1,112 @@ +import { + buildDepositAssetPackOptionAdmissionReport, + assertDepositAssetPackOptionAdmissionReportSourceSafe, +} from '../deposit-asset-pack-option-admission'; +import { buildDepositAssetPackOptionPolicyReport } from '../deposit-asset-pack-option-policy'; +import { buildDepositAssetPackOptionSynthesis } from '../deposit-asset-pack-options'; + +describe('deposit-asset-pack-option-admission', () => { + it('admits approved policy-eligible options with source-safe index, storage, telemetry, and packs activity receipts', () => { + const synthesis = buildDepositAssetPackOptionSynthesis({ + repositoryFullName: 'engineeredsoftware/ENGI', + sourceBranch: 'main', + sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a', + sourcePathHints: ['uapi/app/deposit/DepositPageClient.tsx'], + depositoryDemandSignals: [{ id: 'depository-demand', weight: 0.8 }], + readingDemandSignals: [{ id: 'reading-demand', weight: 0.86 }], + }); + const policy = buildDepositAssetPackOptionPolicyReport({ + synthesis, + sourceCriticalitySignals: [{ id: 'sub-critical', severity: 'sub-critical', weight: 0.82 }], + developmentCostSats: 1200, + expectedSettlementSats: 6800, + depositorWalletId: 'wallet-1', + }); + + const report = buildDepositAssetPackOptionAdmissionReport({ + synthesis, + policy, + reviewerId: 'depositor-1', + telemetryRunId: 'deposit-run-1', + decisions: [ + { + optionId: synthesis.options[0].optionId, + decision: 'approved-for-admission', + feedback: 'Approve bounded route extraction option.', + }, + ], + }); + const admitted = report.receipts[0]; + + expect(report.schema).toBe('bitcode.deposit.asset-pack-option-admission-report'); + expect(report.report).toBe('DepositAssetPackOptionAdmissionReport'); + expect(report.approvedCount).toBe(1); + expect(report.admittedCount).toBe(1); + expect(admitted.admission.state).toBe('admitted-to-depository'); + expect(admitted.admission.depositoryAssetPackId).toMatch(/^depository-assetpack-/u); + expect(admitted.depositoryIndexProjection).toMatchObject({ + state: 'indexed-for-finding-fits', + vectorEmbeddingState: 'projection-ready', + searchDisclosure: 'measurements-and-metadata-only', + }); + expect(admitted.storageProjection).toMatchObject({ + state: 'projected-to-object-storage', + rawSourceStoredExternally: true, + protectedSourceVisible: false, + unpaidAssetPackSourceVisible: false, + }); + expect(admitted.compensationPreview).toMatchObject({ + state: 'compensation-preview-ready', + priceAsset: 'BTC', + allocationMethod: 'source-to-shares-largest-remainder', + }); + expect(admitted.packsActivitySync).toMatchObject({ + state: 'synchronized-to-packs', + route: '/packs', + activityType: 'depository-assetpack', + }); + expect(admitted.telemetry).toMatchObject({ + eventType: 'deposit-option-admission', + channel: 'execution-stream', + sourceSafeMetadataOnly: true, + }); + expect(assertDepositAssetPackOptionAdmissionReportSourceSafe(report)).toEqual({ + admitted: true, + reason: 'source_safe_deposit_asset_pack_option_admission_report', + }); + }); + + it('keeps rejected, resynthesis, pending, and policy-blocked options out of the Depository', () => { + const synthesis = buildDepositAssetPackOptionSynthesis({ + repositoryFullName: 'engineeredsoftware/ENGI', + sourceBranch: 'main', + sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a', + sourcePathHints: ['packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts'], + }); + const policy = buildDepositAssetPackOptionPolicyReport({ + synthesis, + sourceCriticalitySignals: [{ id: 'critical', severity: 'critical', weight: 1 }], + developmentCostSats: 7000, + expectedSettlementSats: 1000, + }); + + const report = buildDepositAssetPackOptionAdmissionReport({ + synthesis, + policy, + decisions: [ + { optionId: synthesis.options[0].optionId, decision: 'approved-for-admission' }, + { optionId: synthesis.options[1].optionId, decision: 'rejected-by-depositor' }, + { optionId: synthesis.options[2].optionId, decision: 'resynthesis-requested' }, + ], + }); + + expect(report.admittedCount).toBe(0); + expect(report.blockedCount).toBe(1); + expect(report.receipts[0].admission.state).toBe('not-admitted-policy-blocked'); + expect(report.receipts[0].admission.blockers).toContain('policy-blocked-before-admission'); + expect(report.receipts[1].admission.state).toBe('not-admitted-rejected'); + expect(report.receipts[2].admission.state).toBe('not-admitted-resynthesis-requested'); + expect(report.receipts.every((receipt) => receipt.packsActivitySync.state === 'not-synchronized')).toBe(true); + expect(assertDepositAssetPackOptionAdmissionReportSourceSafe(report).admitted).toBe(true); + }); +}); diff --git a/packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts b/packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts new file mode 100644 index 00000000..8ee2e45e --- /dev/null +++ b/packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts @@ -0,0 +1,572 @@ +import type { + DepositAssetPackOption, + DepositAssetPackOptionSynthesis, +} from './deposit-asset-pack-options'; +import type { + DepositAssetPackOptionPolicyEvaluation, + DepositAssetPackOptionPolicyReport, +} from './deposit-asset-pack-option-policy'; + +export type DepositOptionReviewDecisionState = + | 'pending-depositor-review' + | 'approved-for-admission' + | 'rejected-by-depositor' + | 'resynthesis-requested'; + +export type DepositOptionAdmissionState = + | 'admitted-to-depository' + | 'not-admitted-pending-review' + | 'not-admitted-rejected' + | 'not-admitted-resynthesis-requested' + | 'not-admitted-policy-blocked'; + +export interface DepositOptionReviewDecision { + optionId: string; + decision: DepositOptionReviewDecisionState; + reviewerId?: string | null; + feedback?: string | null; + decidedAt?: string | null; +} + +export interface DepositAssetPackOptionAdmissionInput { + synthesis: DepositAssetPackOptionSynthesis; + policy: DepositAssetPackOptionPolicyReport; + decisions?: DepositOptionReviewDecision[] | null; + reviewerId?: string | null; + storageNamespace?: string | null; + depositoryIndexNamespace?: string | null; + telemetryRunId?: string | null; + createdAt?: string | null; +} + +export interface DepositOptionAdmissionReceipt { + schema: 'bitcode.deposit.asset-pack-option-admission-receipt'; + optionId: string; + optionKind: DepositAssetPackOption['kind']; + title: string; + reviewDecision: { + state: DepositOptionReviewDecisionState; + reviewerRoot: string | null; + feedbackRoot: string | null; + decisionRoot: string; + }; + admission: { + state: DepositOptionAdmissionState; + depositoryAssetPackId: string | null; + admittedAt: string | null; + blockers: string[]; + warnings: string[]; + admissionRoot: string; + }; + depositoryIndexProjection: { + state: 'indexed-for-finding-fits' | 'not-indexed'; + namespaceRoot: string; + semanticIndexRoot: string | null; + lexicalIndexRoot: string | null; + metadataIndexRoot: string | null; + vectorEmbeddingState: 'projection-ready' | 'not-projected'; + searchDisclosure: 'measurements-and-metadata-only'; + }; + storageProjection: { + state: 'projected-to-object-storage' | 'not-projected'; + namespaceRoot: string; + metadataRecordRoot: string | null; + rawSourcePointerRoot: string | null; + rawSourceStoredExternally: true; + protectedSourceVisible: false; + unpaidAssetPackSourceVisible: false; + }; + compensationPreview: { + state: 'compensation-preview-ready' | 'not-eligible-for-compensation'; + priceAsset: 'BTC'; + allocationMethod: 'source-to-shares-largest-remainder'; + depositorShareBasisPoints: number; + protocolTreasuryBasisPoints: number; + compensationRouteRoot: string | null; + }; + packsActivitySync: { + state: 'synchronized-to-packs' | 'not-synchronized'; + route: '/packs'; + activityType: 'depository-assetpack' | 'deposit-option'; + activityId: string; + activityRoot: string; + }; + telemetry: { + eventType: 'deposit-option-admission'; + channel: 'execution-stream'; + runRoot: string | null; + eventRoot: string; + sourceSafeMetadataOnly: true; + }; + visibility: { + sourceSafeMetadataOnly: true; + protectedSourceVisible: false; + rawSourceTextVisible: false; + unpaidAssetPackSourceVisible: false; + rawPromptVisible: false; + interpolatedPromptVisible: false; + rawProviderResponseVisible: false; + walletPrivateMaterialVisible: false; + settlementPrivatePayloadVisible: false; + }; + roots: { + optionRoot: string; + policyEvaluationRoot: string | null; + admissionReceiptRoot: string; + depositoryIndexRoot: string | null; + storageProjectionRoot: string | null; + packsActivityRoot: string; + telemetryRoot: string; + }; +} + +export interface DepositAssetPackOptionAdmissionReport { + schema: 'bitcode.deposit.asset-pack-option-admission-report'; + report: 'DepositAssetPackOptionAdmissionReport'; + reportId: string; + route: '/deposit'; + packsRoute: '/packs'; + createdAt: string; + synthesisRequestId: string; + policyReportId: string; + optionCount: number; + approvedCount: number; + rejectedCount: number; + resynthesisRequestedCount: number; + admittedCount: number; + blockedCount: number; + receipts: DepositOptionAdmissionReceipt[]; + aggregateAdmission: { + reviewPolicy: 'depositor-decision-required'; + admissionPolicy: 'approved-policy-eligible-options-only'; + indexingPolicy: 'source-safe-measurement-metadata-search-projection'; + storagePolicy: 'metadata-and-external-source-pointer-only'; + packsSynchronization: 'admitted-options-project-to-packs-activity'; + }; + sourceSafety: { + sourceSafeMetadataOnly: true; + protectedSourceVisible: false; + rawSourceTextVisible: false; + unpaidAssetPackSourceVisible: false; + rawPromptVisible: false; + interpolatedPromptVisible: false; + rawProviderResponseVisible: false; + walletPrivateMaterialVisible: false; + settlementPrivatePayloadVisible: false; + }; + roots: { + admissionReportRoot: string; + synthesisRoot: string; + policyReportRoot: string; + receiptRoots: string[]; + packsActivityRoots: string[]; + }; +} + +const FORBIDDEN_SOURCE_MARKERS = [ + 'PRIVATE_SOURCE_DO_NOT_SERIALIZE', + `BEGIN_${'PRIVATE'}_KEY`, + 'wallet_private_material', + 'raw_provider_response', + 'unpaid_assetpack_source', + 'protected source body', +]; + +function stableStringify(value: unknown): string { + if (value === null || typeof value !== 'object') return JSON.stringify(value); + if (Array.isArray(value)) return `[${value.map((entry) => stableStringify(entry)).join(',')}]`; + return `{${Object.keys(value as Record) + .sort() + .map((key) => `${JSON.stringify(key)}:${stableStringify((value as Record)[key])}`) + .join(',')}}`; +} + +function stableHash(value: unknown) { + const text = typeof value === 'string' ? value : stableStringify(value); + let hash = 2166136261; + for (let index = 0; index < text.length; index += 1) { + hash ^= text.charCodeAt(index); + hash = Math.imul(hash, 16777619); + } + return (hash >>> 0).toString(16).padStart(8, '0'); +} + +function root(prefix: string, value: unknown) { + return `${prefix}:${stableHash(value)}`; +} + +function normalizedText(value: string | null | undefined) { + const normalized = value?.trim(); + return normalized ? normalized : null; +} + +function normalizeDecisions(decisions: DepositOptionReviewDecision[] | null | undefined) { + return new Map( + (decisions || []) + .filter((decision) => normalizedText(decision.optionId)) + .map((decision) => [ + normalizedText(decision.optionId) as string, + { + optionId: normalizedText(decision.optionId) as string, + decision: decision.decision || 'pending-depositor-review', + reviewerId: normalizedText(decision.reviewerId), + feedback: normalizedText(decision.feedback), + decidedAt: normalizedText(decision.decidedAt), + }, + ]), + ); +} + +function policyByOption(policy: DepositAssetPackOptionPolicyReport) { + return new Map(policy.evaluations.map((evaluation) => [evaluation.optionId, evaluation])); +} + +function blockerState(input: { + option: DepositAssetPackOption; + evaluation: DepositAssetPackOptionPolicyEvaluation | null; + decision: DepositOptionReviewDecisionState; +}) { + const blockers: string[] = []; + const warnings: string[] = []; + + if (input.option.reviewBoundary.state !== 'reviewable-source-safe-option') { + blockers.push(input.option.reviewBoundary.state); + } + + if (!input.evaluation) { + blockers.push('missing-policy-evaluation'); + } else { + blockers.push(...input.evaluation.sourceCriticality.blockers); + blockers.push(...input.evaluation.compensation.blockers); + warnings.push(...input.evaluation.sourceCriticality.warnings); + warnings.push(...input.evaluation.compensation.warnings); + if (input.evaluation.policyDecision === 'blocked-before-admission') { + blockers.push('policy-blocked-before-admission'); + } + if (input.evaluation.compensation.state !== 'eligible-if-approved-and-selected') { + blockers.push('compensation-route-repair-required-before-admission'); + } + } + + if (input.decision === 'pending-depositor-review') blockers.push('depositor-review-required'); + if (input.decision === 'rejected-by-depositor') blockers.push('depositor-rejected-option'); + if (input.decision === 'resynthesis-requested') blockers.push('depositor-requested-resynthesis'); + + return { blockers: [...new Set(blockers)], warnings: [...new Set(warnings)] }; +} + +function admissionStateFor(input: { + decision: DepositOptionReviewDecisionState; + blockers: string[]; +}): DepositOptionAdmissionState { + if (input.blockers.length === 0 && input.decision === 'approved-for-admission') return 'admitted-to-depository'; + if (input.decision === 'rejected-by-depositor') return 'not-admitted-rejected'; + if (input.decision === 'resynthesis-requested') return 'not-admitted-resynthesis-requested'; + if (input.decision === 'pending-depositor-review') return 'not-admitted-pending-review'; + return 'not-admitted-policy-blocked'; +} + +function buildReceipt(input: { + option: DepositAssetPackOption; + evaluation: DepositAssetPackOptionPolicyEvaluation | null; + decision: DepositOptionReviewDecision | null; + createdAt: string; + reviewerId: string | null; + storageNamespace: string; + depositoryIndexNamespace: string; + telemetryRunId: string | null; +}): DepositOptionAdmissionReceipt { + const decisionState = input.decision?.decision || 'pending-depositor-review'; + const reviewerId = input.decision?.reviewerId || input.reviewerId; + const feedback = input.decision?.feedback || null; + const { blockers, warnings } = blockerState({ + option: input.option, + evaluation: input.evaluation, + decision: decisionState, + }); + const admissionState = admissionStateFor({ decision: decisionState, blockers }); + const admitted = admissionState === 'admitted-to-depository'; + const reviewerRoot = reviewerId ? root('deposit-option-reviewer', reviewerId) : null; + const feedbackRoot = feedback ? root('deposit-option-review-feedback', feedback) : null; + const decisionRoot = root('deposit-option-review-decision', { + optionId: input.option.optionId, + decisionState, + reviewerRoot, + feedbackRoot, + decidedAt: input.decision?.decidedAt || input.createdAt, + }); + const depositoryAssetPackId = admitted + ? `depository-assetpack-${stableHash({ + optionId: input.option.optionId, + optionRoot: input.option.roots.optionRoot, + decisionRoot, + })}` + : null; + const admissionRoot = root('deposit-option-admission', { + optionId: input.option.optionId, + depositoryAssetPackId, + admissionState, + blockers, + warnings, + decisionRoot, + }); + const namespaceRoot = root('deposit-option-admission-namespace', { + storageNamespace: input.storageNamespace, + depositoryIndexNamespace: input.depositoryIndexNamespace, + }); + const semanticIndexRoot = admitted + ? root('deposit-option-semantic-index', { + optionRoot: input.option.roots.optionRoot, + measurementRoot: input.option.roots.measurementRoot, + demandRoot: input.option.roots.demandAlignmentRoot, + }) + : null; + const lexicalIndexRoot = admitted + ? root('deposit-option-lexical-index', { + title: input.option.title, + kind: input.option.kind, + sourceBindingRoot: input.option.roots.sourceBindingRoot, + }) + : null; + const metadataIndexRoot = admitted + ? root('deposit-option-metadata-index', { + depositoryAssetPackId, + sourceBindingRoot: input.option.roots.sourceBindingRoot, + policyEvaluationRoot: input.evaluation?.roots.policyEvaluationRoot || null, + }) + : null; + const metadataRecordRoot = admitted + ? root('deposit-option-storage-metadata-record', { + depositoryAssetPackId, + optionRoot: input.option.roots.optionRoot, + policyEvaluationRoot: input.evaluation?.roots.policyEvaluationRoot || null, + admissionRoot, + }) + : null; + const rawSourcePointerRoot = admitted + ? root('deposit-option-external-source-pointer', { + depositoryAssetPackId, + sourceBindingRoot: input.option.roots.sourceBindingRoot, + sourcePathRoots: input.option.sourceBinding.sourcePathRoots, + }) + : null; + const compensationRouteRoot = + admitted && input.evaluation?.compensation.state === 'eligible-if-approved-and-selected' + ? input.evaluation.compensation.compensationRouteRoot + : null; + const activityType = admitted ? 'depository-assetpack' : 'deposit-option'; + const activityId = `${activityType}:${input.option.optionId}`; + const activityRoot = root('deposit-option-packs-activity', { + activityId, + activityType, + depositoryAssetPackId, + admissionRoot, + compensationRouteRoot, + }); + const telemetryRoot = root('deposit-option-admission-telemetry', { + eventType: 'deposit-option-admission', + telemetryRunId: input.telemetryRunId, + optionId: input.option.optionId, + admissionState, + activityRoot, + }); + const receiptRoot = root('deposit-option-admission-receipt', { + optionRoot: input.option.roots.optionRoot, + policyEvaluationRoot: input.evaluation?.roots.policyEvaluationRoot || null, + admissionRoot, + activityRoot, + telemetryRoot, + }); + + return { + schema: 'bitcode.deposit.asset-pack-option-admission-receipt', + optionId: input.option.optionId, + optionKind: input.option.kind, + title: input.option.title, + reviewDecision: { + state: decisionState, + reviewerRoot, + feedbackRoot, + decisionRoot, + }, + admission: { + state: admissionState, + depositoryAssetPackId, + admittedAt: admitted ? input.createdAt : null, + blockers, + warnings, + admissionRoot, + }, + depositoryIndexProjection: { + state: admitted ? 'indexed-for-finding-fits' : 'not-indexed', + namespaceRoot, + semanticIndexRoot, + lexicalIndexRoot, + metadataIndexRoot, + vectorEmbeddingState: admitted ? 'projection-ready' : 'not-projected', + searchDisclosure: 'measurements-and-metadata-only', + }, + storageProjection: { + state: admitted ? 'projected-to-object-storage' : 'not-projected', + namespaceRoot, + metadataRecordRoot, + rawSourcePointerRoot, + rawSourceStoredExternally: true, + protectedSourceVisible: false, + unpaidAssetPackSourceVisible: false, + }, + compensationPreview: { + state: compensationRouteRoot ? 'compensation-preview-ready' : 'not-eligible-for-compensation', + priceAsset: 'BTC', + allocationMethod: 'source-to-shares-largest-remainder', + depositorShareBasisPoints: input.evaluation?.compensation.depositorShareBasisPoints ?? 0, + protocolTreasuryBasisPoints: input.evaluation?.compensation.protocolTreasuryBasisPoints ?? 0, + compensationRouteRoot, + }, + packsActivitySync: { + state: admitted ? 'synchronized-to-packs' : 'not-synchronized', + route: '/packs', + activityType, + activityId, + activityRoot, + }, + telemetry: { + eventType: 'deposit-option-admission', + channel: 'execution-stream', + runRoot: input.telemetryRunId ? root('deposit-option-admission-run', input.telemetryRunId) : null, + eventRoot: telemetryRoot, + sourceSafeMetadataOnly: true, + }, + visibility: { + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + rawSourceTextVisible: false, + unpaidAssetPackSourceVisible: false, + rawPromptVisible: false, + interpolatedPromptVisible: false, + rawProviderResponseVisible: false, + walletPrivateMaterialVisible: false, + settlementPrivatePayloadVisible: false, + }, + roots: { + optionRoot: input.option.roots.optionRoot, + policyEvaluationRoot: input.evaluation?.roots.policyEvaluationRoot || null, + admissionReceiptRoot: receiptRoot, + depositoryIndexRoot: admitted ? root('deposit-option-depository-index', { semanticIndexRoot, lexicalIndexRoot, metadataIndexRoot }) : null, + storageProjectionRoot: admitted ? root('deposit-option-storage-projection', { metadataRecordRoot, rawSourcePointerRoot }) : null, + packsActivityRoot: activityRoot, + telemetryRoot, + }, + }; +} + +export function buildDepositAssetPackOptionAdmissionReport( + input: DepositAssetPackOptionAdmissionInput, +): DepositAssetPackOptionAdmissionReport { + const createdAt = normalizedText(input.createdAt) || 'deterministic'; + const reviewerId = normalizedText(input.reviewerId); + const storageNamespace = normalizedText(input.storageNamespace) || 'deposit-asset-pack-options'; + const depositoryIndexNamespace = normalizedText(input.depositoryIndexNamespace) || 'bitcode-depository'; + const telemetryRunId = normalizedText(input.telemetryRunId); + const decisions = normalizeDecisions(input.decisions); + const evaluations = policyByOption(input.policy); + const receipts = input.synthesis.options.map((option) => + buildReceipt({ + option, + evaluation: evaluations.get(option.optionId) || null, + decision: decisions.get(option.optionId) || null, + createdAt, + reviewerId, + storageNamespace, + depositoryIndexNamespace, + telemetryRunId, + }), + ); + const receiptRoots = receipts.map((receipt) => receipt.roots.admissionReceiptRoot); + const packsActivityRoots = receipts.map((receipt) => receipt.roots.packsActivityRoot); + const reportRoot = root('deposit-asset-pack-option-admission-report', { + synthesisRoot: input.synthesis.roots.synthesisRoot, + policyReportRoot: input.policy.roots.policyReportRoot, + receiptRoots, + createdAt, + }); + + return { + schema: 'bitcode.deposit.asset-pack-option-admission-report', + report: 'DepositAssetPackOptionAdmissionReport', + reportId: reportRoot, + route: '/deposit', + packsRoute: '/packs', + createdAt, + synthesisRequestId: input.synthesis.requestId, + policyReportId: input.policy.reportId, + optionCount: receipts.length, + approvedCount: receipts.filter((receipt) => receipt.reviewDecision.state === 'approved-for-admission').length, + rejectedCount: receipts.filter((receipt) => receipt.reviewDecision.state === 'rejected-by-depositor').length, + resynthesisRequestedCount: receipts.filter((receipt) => receipt.reviewDecision.state === 'resynthesis-requested').length, + admittedCount: receipts.filter((receipt) => receipt.admission.state === 'admitted-to-depository').length, + blockedCount: receipts.filter((receipt) => receipt.admission.state === 'not-admitted-policy-blocked').length, + receipts, + aggregateAdmission: { + reviewPolicy: 'depositor-decision-required', + admissionPolicy: 'approved-policy-eligible-options-only', + indexingPolicy: 'source-safe-measurement-metadata-search-projection', + storagePolicy: 'metadata-and-external-source-pointer-only', + packsSynchronization: 'admitted-options-project-to-packs-activity', + }, + sourceSafety: { + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + rawSourceTextVisible: false, + unpaidAssetPackSourceVisible: false, + rawPromptVisible: false, + interpolatedPromptVisible: false, + rawProviderResponseVisible: false, + walletPrivateMaterialVisible: false, + settlementPrivatePayloadVisible: false, + }, + roots: { + admissionReportRoot: reportRoot, + synthesisRoot: input.synthesis.roots.synthesisRoot, + policyReportRoot: input.policy.roots.policyReportRoot, + receiptRoots, + packsActivityRoots, + }, + }; +} + +export function assertDepositAssetPackOptionAdmissionReportSourceSafe( + report: DepositAssetPackOptionAdmissionReport, +) { + const serialized = stableStringify(report); + const noForbiddenMarkers = FORBIDDEN_SOURCE_MARKERS.every((marker) => !serialized.includes(marker)); + const sourceSafe = + report.schema === 'bitcode.deposit.asset-pack-option-admission-report' && + report.route === '/deposit' && + report.packsRoute === '/packs' && + report.sourceSafety.sourceSafeMetadataOnly === true && + report.sourceSafety.protectedSourceVisible === false && + report.sourceSafety.rawSourceTextVisible === false && + report.sourceSafety.unpaidAssetPackSourceVisible === false && + report.sourceSafety.rawPromptVisible === false && + report.sourceSafety.interpolatedPromptVisible === false && + report.sourceSafety.rawProviderResponseVisible === false && + report.sourceSafety.walletPrivateMaterialVisible === false && + report.sourceSafety.settlementPrivatePayloadVisible === false && + report.receipts.every( + (receipt) => + receipt.visibility.sourceSafeMetadataOnly === true && + receipt.storageProjection.rawSourceStoredExternally === true && + receipt.storageProjection.protectedSourceVisible === false && + receipt.storageProjection.unpaidAssetPackSourceVisible === false && + receipt.telemetry.sourceSafeMetadataOnly === true, + ) && + noForbiddenMarkers; + + return { + admitted: sourceSafe, + reason: sourceSafe + ? 'source_safe_deposit_asset_pack_option_admission_report' + : 'deposit_option_admission_source_safety_boundary_violation', + }; +} diff --git a/packages/pipelines/asset-pack/src/index.ts b/packages/pipelines/asset-pack/src/index.ts index 810603c6..2803567e 100644 --- a/packages/pipelines/asset-pack/src/index.ts +++ b/packages/pipelines/asset-pack/src/index.ts @@ -439,6 +439,7 @@ export * from './reading-interface-product-parity'; export * from './reading-local-staging-rehearsal'; export * from './deposit-asset-pack-options'; export * from './deposit-asset-pack-option-policy'; +export * from './deposit-asset-pack-option-admission'; export * from './embedding-config'; export * from './asset-pack-disclosure'; export * from './read-need-review-resynthesis'; diff --git a/packages/protocol/README.md b/packages/protocol/README.md index f8d7e79f..81a18cef 100644 --- a/packages/protocol/README.md +++ b/packages/protocol/README.md @@ -131,6 +131,18 @@ compensation route preview while keeping Gate 7 responsible for depositor approval, admission, indexing, storage projection, telemetry, and `/packs` activity synchronization. +V43 Gate 7 adds `DepositAssetPackOptionAdmissionReport` through +`packages/protocol/src/canonical/v43-deposit-option-admission.js`, +`packages/protocol/test/v43-deposit-option-admission.test.js`, +`.bitcode/v43-deposit-option-admission.json`, +`generate:v43-deposit-option-admission`, +`check:v43-deposit-option-admission`, and `check:v43-gate7`. It binds +depositor approve/reject/resynthesis decisions, source-safe admission receipts, +Depository index/storage projections, BTC source-to-shares compensation preview +continuity, execution-stream telemetry, and `/packs` `depository-assetpack` +synchronization while keeping BTD mint, rights transfer, settlement broadcast, +and unpaid AssetPack source disclosure outside deposit-side admission. + Historical V39 promotion moved this package through the `V39` active, `V40` draft posture. V40 promotion has since advanced the current package posture to `V40` active, `V41` draft. diff --git a/packages/protocol/src/canonical/v43-deposit-option-admission.js b/packages/protocol/src/canonical/v43-deposit-option-admission.js new file mode 100644 index 00000000..c9eed419 --- /dev/null +++ b/packages/protocol/src/canonical/v43-deposit-option-admission.js @@ -0,0 +1,255 @@ +// @ts-check + +import crypto from 'node:crypto'; +import { existsSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const DEFAULT_REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..'); + +export const V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH = '.bitcode/v43-deposit-option-admission.json'; +export const V43_DEPOSIT_OPTION_ADMISSION_SCHEMA_ID = 'bitcode.v43.depositOptionAdmission.v1'; +export const V43_DEPOSIT_OPTION_ADMISSION_VERSION = 'V43'; +export const V43_DEPOSIT_OPTION_ADMISSION_CURRENT_TARGET = 'V42'; +export const V43_DEPOSIT_OPTION_ADMISSION_SOURCE_SAFETY_VERDICT = + 'source-safe-deposit-option-admission-metadata'; + +export const V43_DEPOSIT_OPTION_ADMISSION_OBJECT_IDS = Object.freeze([ + 'DepositAssetPackOptionAdmissionReport', + 'DepositOptionAdmissionReceipt', + 'DepositOptionReviewDecision', + 'Depository index projection', + 'object storage projection', + 'pack activity synchronization', + 'deposit option admission telemetry', +]); + +export const V43_DEPOSIT_OPTION_ADMISSION_FIELD_IDS = Object.freeze([ + 'reviewDecision', + 'admission', + 'depositoryIndexProjection', + 'storageProjection', + 'compensationPreview', + 'packsActivitySync', + 'telemetry', + 'visibility', +]); + +export const V43_DEPOSIT_OPTION_ADMISSION_FORBIDDEN_PAYLOAD_IDS = Object.freeze([ + 'protected_source_payload', + 'raw_source_text', + 'unpaid_assetpack_source', + 'raw_protected_prompt', + 'interpolated_prompt', + 'raw_provider_response', + 'wallet_private_material', + 'settlement_private_payload', +]); + +const SOURCE_ROOTS = Object.freeze({ + activePointer: 'BITCODE_SPEC.txt', + spec: 'BITCODE_SPEC_V43.md', + delta: 'BITCODE_SPEC_V43_DELTA.md', + notes: 'BITCODE_SPEC_V43_NOTES.md', + parity: 'BITCODE_SPEC_V43_PARITY_MATRIX.md', + roadmap: 'SPECIFICATIONS_ROADMAP.md', + readme: 'README.md', + protocolReadme: 'packages/protocol/README.md', + packageJson: 'package.json', + gateWorkflow: '.github/workflows/bitcode-gate-quality.yml', + canonWorkflow: '.github/workflows/bitcode-canon-quality.yml', + optionModel: 'packages/pipelines/asset-pack/src/deposit-asset-pack-options.ts', + policyModel: 'packages/pipelines/asset-pack/src/deposit-asset-pack-option-policy.ts', + admissionModel: 'packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts', + optionModelTest: 'packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-options.test.ts', + policyModelTest: 'packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-policy.test.ts', + admissionModelTest: 'packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts', + routeModel: 'uapi/app/deposit/deposit-route-model.ts', + client: 'uapi/app/deposit/DepositPageClient.tsx', + routeModelTest: 'uapi/tests/depositRouteModel.test.ts', + pageTest: 'uapi/tests/depositPageClient.test.tsx', + packActivityModel: 'uapi/components/base/bitcode/activity/pack-activity-model.ts', + packActivityModelTest: 'uapi/tests/packActivityModel.test.ts', + uapiJestConfig: 'uapi/jest.config.cjs', + packageIndex: 'packages/pipelines/asset-pack/src/index.ts', + packageManifest: 'packages/pipelines/asset-pack/package.json', + protocolIndex: 'packages/protocol/src/index.js', + protocolTypes: 'packages/protocol/src/index.d.ts', + protocolTest: 'packages/protocol/test/v43-deposit-option-admission.test.js', + generator: 'scripts/generate-v43-deposit-option-admission.mjs', + checker: 'scripts/check-v43-gate7-deposit-option-admission.mjs', +}); + +function digest(value) { + return crypto.createHash('sha256').update(value).digest('hex').slice(0, 24); +} + +function readSource(repoRoot, sourcePath) { + const absolutePath = path.join(repoRoot, sourcePath); + return existsSync(absolutePath) ? readFileSync(absolutePath, 'utf8') : ''; +} + +function predicateResult(id, sourcePath, passed) { + return { id, sourcePath, passed: Boolean(passed) }; +} + +export const V43_DEPOSIT_OPTION_ADMISSION_CONTRACT_ROWS = Object.freeze([ + { + rowId: 'depositor-review-decisions', + owner: SOURCE_ROOTS.admissionModel, + contract: + 'DepositOptionReviewDecision records approved, rejected, resynthesis-requested, and pending decisions before any source-safe option can enter the Depository.', + requiredFields: ['approved-for-admission', 'rejected-by-depositor', 'resynthesis-requested', 'pending-depositor-review'], + }, + { + rowId: 'admission-receipts', + owner: SOURCE_ROOTS.admissionModel, + contract: + 'DepositOptionAdmissionReceipt admits only approved, policy-eligible, source-safe options and emits deterministic blockers for pending, rejected, resynthesis, or policy-blocked options.', + requiredFields: ['admitted-to-depository', 'not-admitted-policy-blocked', 'DepositOptionAdmissionReceipt'], + }, + { + rowId: 'depository-index-storage', + owner: SOURCE_ROOTS.admissionModel, + contract: + 'Admitted deposit AssetPacks project only measurement and metadata search indexes plus object-storage metadata and external source pointer roots.', + requiredFields: ['depositoryIndexProjection', 'storageProjection', 'measurements-and-metadata-only'], + }, + { + rowId: 'compensation-and-packs-sync', + owner: SOURCE_ROOTS.packActivityModel, + contract: + 'Admitted options preserve Gate 6 BTC source-to-shares compensation preview and synchronize to /packs as Depository AssetPack activity without minting BTD.', + requiredFields: ['compensationPreview', 'source-to-shares-largest-remainder', 'depository-assetpack'], + }, + { + rowId: 'telemetry-source-safety', + owner: SOURCE_ROOTS.admissionModel, + contract: + 'Admission telemetry emits execution-stream source-safe metadata while protected source, prompts, provider responses, wallet material, and settlement private payloads remain invisible.', + requiredFields: ['deposit-option-admission', 'execution-stream', 'sourceSafeMetadataOnly'], + }, +]); + +function buildPredicateResults(repoRoot) { + const sources = Object.fromEntries( + Object.entries(SOURCE_ROOTS).map(([key, sourcePath]) => [key, readSource(repoRoot, sourcePath)]), + ); + + return [ + predicateResult('active-canon-pointer-remains-v42', SOURCE_ROOTS.activePointer, sources.activePointer.trim() === 'V42'), + predicateResult('spec-defines-gate7', SOURCE_ROOTS.spec, sources.spec.includes('V43 Gate 7 Deposit Option Review, Approval, And Admission')), + predicateResult('spec-names-admission-report', SOURCE_ROOTS.spec, sources.spec.includes('DepositAssetPackOptionAdmissionReport') && sources.spec.includes('DepositOptionAdmissionReceipt')), + predicateResult('delta-records-gate7', SOURCE_ROOTS.delta, sources.delta.includes('Gate 7') && sources.delta.includes('v43-deposit-option-admission')), + predicateResult('notes-records-gate7', SOURCE_ROOTS.notes, sources.notes.includes('Gate 7') && sources.notes.includes('admission receipts')), + predicateResult('parity-records-gate7', SOURCE_ROOTS.parity, sources.parity.includes('Deposit option admission') && sources.parity.includes('v43-deposit-option-admission')), + predicateResult('roadmap-records-gate7', SOURCE_ROOTS.roadmap, sources.roadmap.includes('V43 Gate 7 closure anchor')), + predicateResult('readme-records-gate7', SOURCE_ROOTS.readme, sources.readme.includes('V43 Gate 7')), + predicateResult('protocol-readme-records-gate7', SOURCE_ROOTS.protocolReadme, sources.protocolReadme.includes('V43 Gate 7')), + predicateResult('admission-model-defines-report', SOURCE_ROOTS.admissionModel, sources.admissionModel.includes('DepositAssetPackOptionAdmissionReport') && sources.admissionModel.includes('buildDepositAssetPackOptionAdmissionReport')), + predicateResult('admission-model-defines-review-decisions', SOURCE_ROOTS.admissionModel, sources.admissionModel.includes('approved-for-admission') && sources.admissionModel.includes('rejected-by-depositor') && sources.admissionModel.includes('resynthesis-requested')), + predicateResult('admission-model-defines-admission-states', SOURCE_ROOTS.admissionModel, sources.admissionModel.includes('admitted-to-depository') && sources.admissionModel.includes('not-admitted-policy-blocked')), + predicateResult('admission-model-defines-index-storage', SOURCE_ROOTS.admissionModel, sources.admissionModel.includes('depositoryIndexProjection') && sources.admissionModel.includes('storageProjection') && sources.admissionModel.includes('rawSourceStoredExternally: true')), + predicateResult('admission-model-defines-packs-sync', SOURCE_ROOTS.admissionModel, sources.admissionModel.includes('packsActivitySync') && sources.admissionModel.includes("'depository-assetpack'")), + predicateResult('admission-model-defines-telemetry', SOURCE_ROOTS.admissionModel, sources.admissionModel.includes("eventType: 'deposit-option-admission'") && sources.admissionModel.includes("channel: 'execution-stream'")), + predicateResult('admission-model-forbids-source-leakage', SOURCE_ROOTS.admissionModel, sources.admissionModel.includes('protectedSourceVisible: false') && sources.admissionModel.includes('rawProviderResponseVisible: false') && sources.admissionModel.includes('settlementPrivatePayloadVisible: false')), + predicateResult('route-model-owns-admission', SOURCE_ROOTS.routeModel, sources.routeModel.includes('DepositAssetPackOptionAdmissionReport') && sources.routeModel.includes('admissionAndIndexingOwnedByGate7')), + predicateResult('deposit-client-renders-admission', SOURCE_ROOTS.client, sources.client.includes('DepositAssetPackOptionAdmissionReport') && sources.client.includes('Approve for Depository') && sources.client.includes('Resynthesis queued')), + predicateResult('pack-activity-recognizes-admission', SOURCE_ROOTS.packActivityModel, sources.packActivityModel.includes('deposit-option-admission') && sources.packActivityModel.includes('depository-assetpack')), + predicateResult('asset-pack-package-exports-admission', SOURCE_ROOTS.packageIndex, sources.packageIndex.includes("export * from './deposit-asset-pack-option-admission'")), + predicateResult('asset-pack-manifest-exports-admission', SOURCE_ROOTS.packageManifest, sources.packageManifest.includes('"./deposit-asset-pack-option-admission"')), + predicateResult('uapi-jest-maps-admission', SOURCE_ROOTS.uapiJestConfig, sources.uapiJestConfig.includes('@bitcode/pipeline-asset-pack/deposit-asset-pack-option-admission')), + predicateResult('admission-test-covers-report', SOURCE_ROOTS.admissionModelTest, sources.admissionModelTest.includes('buildDepositAssetPackOptionAdmissionReport') && sources.admissionModelTest.includes('synchronized-to-packs')), + predicateResult('route-test-covers-admission', SOURCE_ROOTS.routeModelTest, sources.routeModelTest.includes('DepositAssetPackOptionAdmissionReport') && sources.routeModelTest.includes('admitted-to-depository')), + predicateResult('page-test-covers-admission', SOURCE_ROOTS.pageTest, sources.pageTest.includes('DepositAssetPackOptionAdmissionReport') && sources.pageTest.includes('Approve for Depository')), + predicateResult('pack-activity-test-covers-admission', SOURCE_ROOTS.packActivityModelTest, sources.packActivityModelTest.includes('deposit-option-admission') && sources.packActivityModelTest.includes('depository-assetpack')), + predicateResult('protocol-test-covers-artifact', SOURCE_ROOTS.protocolTest, sources.protocolTest.includes('buildV43DepositOptionAdmission')), + predicateResult('protocol-package-exports-gate7', SOURCE_ROOTS.protocolIndex, sources.protocolIndex.includes('buildV43DepositOptionAdmission')), + predicateResult('protocol-types-export-gate7', SOURCE_ROOTS.protocolTypes, sources.protocolTypes.includes('buildV43DepositOptionAdmission')), + predicateResult('package-json-exposes-gate7', SOURCE_ROOTS.packageJson, sources.packageJson.includes('"generate:v43-deposit-option-admission"') && sources.packageJson.includes('"check:v43-gate7"')), + predicateResult('gate-workflow-runs-gate7', SOURCE_ROOTS.gateWorkflow, sources.gateWorkflow.includes('check-v43-gate7-deposit-option-admission.mjs')), + predicateResult('canon-workflow-runs-gate7', SOURCE_ROOTS.canonWorkflow, sources.canonWorkflow.includes('check-v43-gate7-deposit-option-admission.mjs')), + predicateResult('generator-exists', SOURCE_ROOTS.generator, sources.generator.includes('buildV43DepositOptionAdmission')), + predicateResult('checker-exists', SOURCE_ROOTS.checker, sources.checker.includes('V43 Gate 7 deposit option admission check')), + ]; +} + +export function buildV43DepositOptionAdmission({ repoRoot = DEFAULT_REPO_ROOT } = {}) { + const predicateResults = buildPredicateResults(repoRoot); + const failedPredicateIds = predicateResults + .filter((predicate) => !predicate.passed) + .map((predicate) => predicate.id); + const sourceRoots = Object.fromEntries( + Object.entries(SOURCE_ROOTS).map(([key, sourcePath]) => [key, `${sourcePath}:${digest(readSource(repoRoot, sourcePath))}`]), + ); + const artifactRoot = `v43-deposit-option-admission:${digest(JSON.stringify({ + objectIds: V43_DEPOSIT_OPTION_ADMISSION_OBJECT_IDS, + fieldIds: V43_DEPOSIT_OPTION_ADMISSION_FIELD_IDS, + forbiddenPayloadIds: V43_DEPOSIT_OPTION_ADMISSION_FORBIDDEN_PAYLOAD_IDS, + contractRows: V43_DEPOSIT_OPTION_ADMISSION_CONTRACT_ROWS.map((row) => row.rowId), + sourceRoots, + failedPredicateIds, + }))}`; + + return { + artifactId: 'v43-deposit-option-admission', + schemaId: V43_DEPOSIT_OPTION_ADMISSION_SCHEMA_ID, + version: V43_DEPOSIT_OPTION_ADMISSION_VERSION, + currentTarget: V43_DEPOSIT_OPTION_ADMISSION_CURRENT_TARGET, + sourceSafetyVerdict: V43_DEPOSIT_OPTION_ADMISSION_SOURCE_SAFETY_VERDICT, + generatedAt: 'deterministic', + artifactRoot, + passed: failedPredicateIds.length === 0, + objectIds: [...V43_DEPOSIT_OPTION_ADMISSION_OBJECT_IDS], + fieldIds: [...V43_DEPOSIT_OPTION_ADMISSION_FIELD_IDS], + forbiddenPayloadIds: [...V43_DEPOSIT_OPTION_ADMISSION_FORBIDDEN_PAYLOAD_IDS], + contractRows: V43_DEPOSIT_OPTION_ADMISSION_CONTRACT_ROWS.map((row) => ({ + ...row, + rowRoot: `v43-deposit-option-admission-contract:${digest(row.rowId)}`, + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + unpaidAssetPackSourceVisible: false, + })), + sourceRoots, + predicateResults, + coverage: { + reviewDecisionsImplemented: true, + approvalDecisionImplemented: true, + rejectionDecisionImplemented: true, + resynthesisDecisionImplemented: true, + admissionReceiptsImplemented: true, + approvedPolicyEligibleOptionsAdmittedOnly: true, + depositoryIndexProjectionImplemented: true, + vectorEmbeddingProjectionReadyForAdmittedOptions: true, + storageProjectionImplemented: true, + rawSourceStoredExternally: true, + compensationPreviewContinued: true, + compensationPriceAsset: 'BTC', + compensationAllocationMethod: 'source-to-shares-largest-remainder', + packsActivitySynchronizationImplemented: true, + packsRoute: '/packs', + packsActivityType: 'depository-assetpack', + telemetryImplemented: true, + routeAdmissionReadbackImplemented: true, + btdMintRequiresFutureNeedFitSettlement: true, + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + rawSourceTextVisible: false, + unpaidAssetPackSourceVisible: false, + rawPromptVisible: false, + interpolatedPromptVisible: false, + rawProviderResponseVisible: false, + walletPrivateMaterialVisible: false, + settlementPrivatePayloadVisible: false, + requiredPredicateCount: predicateResults.length, + passedPredicateCount: predicateResults.length - failedPredicateIds.length, + failedPredicateIds, + }, + }; +} + +export const V43_DEPOSIT_OPTION_ADMISSION_SOURCE_ROOTS = Object.freeze( + Object.fromEntries(Object.entries(SOURCE_ROOTS).map(([key, sourcePath]) => [key, sourcePath])), +); diff --git a/packages/protocol/src/index.d.ts b/packages/protocol/src/index.d.ts index 0d40f110..032ae1f5 100644 --- a/packages/protocol/src/index.d.ts +++ b/packages/protocol/src/index.d.ts @@ -641,6 +641,17 @@ export const V43_DEPOSIT_POLICY_FIELD_IDS: readonly string[]; export const V43_DEPOSIT_POLICY_FORBIDDEN_PAYLOAD_IDS: readonly string[]; export const V43_DEPOSIT_POLICY_COMPENSATION_CONTRACT_ROWS: readonly Record[]; export function buildV43DepositPolicyCompensation(input?: Record): BitcodeProtocolReport; +export const V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH: string; +export const V43_DEPOSIT_OPTION_ADMISSION_CURRENT_TARGET: string; +export const V43_DEPOSIT_OPTION_ADMISSION_SCHEMA_ID: string; +export const V43_DEPOSIT_OPTION_ADMISSION_VERSION: string; +export const V43_DEPOSIT_OPTION_ADMISSION_SOURCE_SAFETY_VERDICT: string; +export const V43_DEPOSIT_OPTION_ADMISSION_SOURCE_ROOTS: Readonly>; +export const V43_DEPOSIT_OPTION_ADMISSION_OBJECT_IDS: readonly string[]; +export const V43_DEPOSIT_OPTION_ADMISSION_FIELD_IDS: readonly string[]; +export const V43_DEPOSIT_OPTION_ADMISSION_FORBIDDEN_PAYLOAD_IDS: readonly string[]; +export const V43_DEPOSIT_OPTION_ADMISSION_CONTRACT_ROWS: readonly Record[]; +export function buildV43DepositOptionAdmission(input?: Record): BitcodeProtocolReport; export const EXCHANGE_INTENT_ORDER_CONTRACTS_ARTIFACT_PATH: string; export const EXCHANGE_INTENT_ORDER_CONTRACTS_CURRENT_TARGET: string; export const EXCHANGE_INTENT_ORDER_CONTRACTS_SCHEMA_ID: string; diff --git a/packages/protocol/src/index.js b/packages/protocol/src/index.js index e52ab3a2..e88288d6 100644 --- a/packages/protocol/src/index.js +++ b/packages/protocol/src/index.js @@ -725,6 +725,19 @@ export { V43_DEPOSIT_POLICY_OBJECT_IDS, buildV43DepositPolicyCompensation } from './canonical/v43-deposit-policy-compensation.js'; +export { + V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH, + V43_DEPOSIT_OPTION_ADMISSION_CONTRACT_ROWS, + V43_DEPOSIT_OPTION_ADMISSION_CURRENT_TARGET, + V43_DEPOSIT_OPTION_ADMISSION_FIELD_IDS, + V43_DEPOSIT_OPTION_ADMISSION_FORBIDDEN_PAYLOAD_IDS, + V43_DEPOSIT_OPTION_ADMISSION_OBJECT_IDS, + V43_DEPOSIT_OPTION_ADMISSION_SCHEMA_ID, + V43_DEPOSIT_OPTION_ADMISSION_SOURCE_ROOTS, + V43_DEPOSIT_OPTION_ADMISSION_SOURCE_SAFETY_VERDICT, + V43_DEPOSIT_OPTION_ADMISSION_VERSION, + buildV43DepositOptionAdmission +} from './canonical/v43-deposit-option-admission.js'; export { EXCHANGE_INTENT_ACTION_KINDS, EXCHANGE_INTENT_ORDER_CONTRACTS_ARTIFACT_PATH, diff --git a/packages/protocol/test/v43-deposit-option-admission.test.js b/packages/protocol/test/v43-deposit-option-admission.test.js new file mode 100644 index 00000000..2c35c25f --- /dev/null +++ b/packages/protocol/test/v43-deposit-option-admission.test.js @@ -0,0 +1,51 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { + V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH, + V43_DEPOSIT_OPTION_ADMISSION_FIELD_IDS, + V43_DEPOSIT_OPTION_ADMISSION_FORBIDDEN_PAYLOAD_IDS, + V43_DEPOSIT_OPTION_ADMISSION_OBJECT_IDS, + V43_DEPOSIT_OPTION_ADMISSION_SCHEMA_ID, + V43_DEPOSIT_OPTION_ADMISSION_SOURCE_SAFETY_VERDICT, + buildV43DepositOptionAdmission, +} from '../src/canonical/v43-deposit-option-admission.js'; + +test('V43 deposit option admission artifact binds source-safe admission contracts', () => { + const report = buildV43DepositOptionAdmission(); + + assert.equal(V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH, '.bitcode/v43-deposit-option-admission.json'); + assert.equal(report.artifactId, 'v43-deposit-option-admission'); + assert.equal(report.schemaId, V43_DEPOSIT_OPTION_ADMISSION_SCHEMA_ID); + assert.equal(report.version, 'V43'); + assert.equal(report.currentTarget, 'V42'); + assert.equal(report.sourceSafetyVerdict, V43_DEPOSIT_OPTION_ADMISSION_SOURCE_SAFETY_VERDICT); + assert.ok(report.artifactRoot.startsWith('v43-deposit-option-admission:')); + assert.deepEqual(report.objectIds, [...V43_DEPOSIT_OPTION_ADMISSION_OBJECT_IDS]); + assert.deepEqual(report.fieldIds, [...V43_DEPOSIT_OPTION_ADMISSION_FIELD_IDS]); + assert.deepEqual(report.forbiddenPayloadIds, [...V43_DEPOSIT_OPTION_ADMISSION_FORBIDDEN_PAYLOAD_IDS]); + assert.equal(report.contractRows.length, 5); + assert.equal(report.coverage.reviewDecisionsImplemented, true); + assert.equal(report.coverage.approvalDecisionImplemented, true); + assert.equal(report.coverage.rejectionDecisionImplemented, true); + assert.equal(report.coverage.resynthesisDecisionImplemented, true); + assert.equal(report.coverage.admissionReceiptsImplemented, true); + assert.equal(report.coverage.approvedPolicyEligibleOptionsAdmittedOnly, true); + assert.equal(report.coverage.depositoryIndexProjectionImplemented, true); + assert.equal(report.coverage.storageProjectionImplemented, true); + assert.equal(report.coverage.compensationPreviewContinued, true); + assert.equal(report.coverage.compensationPriceAsset, 'BTC'); + assert.equal(report.coverage.compensationAllocationMethod, 'source-to-shares-largest-remainder'); + assert.equal(report.coverage.packsActivitySynchronizationImplemented, true); + assert.equal(report.coverage.packsRoute, '/packs'); + assert.equal(report.coverage.packsActivityType, 'depository-assetpack'); + assert.equal(report.coverage.telemetryImplemented, true); + assert.equal(report.coverage.routeAdmissionReadbackImplemented, true); + assert.equal(report.coverage.btdMintRequiresFutureNeedFitSettlement, true); + assert.equal(report.coverage.sourceSafeMetadataOnly, true); + assert.equal(report.coverage.protectedSourceVisible, false); + assert.equal(report.coverage.rawSourceTextVisible, false); + assert.equal(report.coverage.unpaidAssetPackSourceVisible, false); + assert.equal(report.coverage.rawProviderResponseVisible, false); + assert.equal(report.coverage.walletPrivateMaterialVisible, false); + assert.equal(report.coverage.settlementPrivatePayloadVisible, false); +}); diff --git a/scripts/check-v43-gate7-deposit-option-admission.mjs b/scripts/check-v43-gate7-deposit-option-admission.mjs new file mode 100644 index 00000000..5c5ebb9a --- /dev/null +++ b/scripts/check-v43-gate7-deposit-option-admission.mjs @@ -0,0 +1,186 @@ +#!/usr/bin/env node + +import { execFileSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH, + buildV43DepositOptionAdmission, +} from '../packages/protocol/src/canonical/v43-deposit-option-admission.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const defaultRepoRoot = path.resolve(__dirname, '..'); + +function read(root, relativePath) { + return readFileSync(path.join(root, relativePath), 'utf8'); +} + +function exists(root, relativePath) { + return existsSync(path.join(root, relativePath)); +} + +function git(root, args) { + return execFileSync('git', args, { cwd: root, encoding: 'utf8' }).trim(); +} + +function assertCheck(failures, condition, message) { + if (!condition) failures.push(message); +} + +function parseArgs(argv) { + const args = { repoRoot: defaultRepoRoot, skipBranchCheck: false, skipUapiTests: false, skipPackageTests: false }; + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--repo-root') args.repoRoot = path.resolve(argv[++index]); + else if (arg === '--skip-branch-check') args.skipBranchCheck = true; + else if (arg === '--skip-uapi-tests') args.skipUapiTests = true; + else if (arg === '--skip-package-tests') args.skipPackageTests = true; + else if (arg === '--help' || arg === '-h') args.help = true; + else throw new Error(`Unknown argument ${arg}`); + } + return args; +} + +function printHelp() { + process.stdout.write( + [ + 'Usage: node scripts/check-v43-gate7-deposit-option-admission.mjs [--skip-branch-check] [--skip-uapi-tests] [--skip-package-tests] [--repo-root ]', + '', + 'Checks V43 Gate 7 deposit option review decisions, source-safe admission receipts, Depository indexing, storage projection, /packs synchronization, generated artifact, docs, workflows, and tests.', + ].join('\n'), + ); + process.stdout.write('\n'); +} + +function run(root, command, args) { + execFileSync(command, args, { cwd: root, stdio: 'pipe', encoding: 'utf8' }); +} + +function main() { + const args = parseArgs(process.argv.slice(2)); + if (args.help) { + printHelp(); + return; + } + + const root = args.repoRoot; + const failures = []; + const pointer = read(root, 'BITCODE_SPEC.txt').trim(); + + assertCheck(failures, pointer === 'V42', `BITCODE_SPEC.txt must remain V42 during V43 gate work. Observed ${pointer || 'empty'}.`); + + if (!args.skipBranchCheck) { + const branch = git(root, ['branch', '--show-current']); + assertCheck( + failures, + branch === 'version/v43' || /^v43\/gate-\d+-[a-z0-9][a-z0-9-]*$/u.test(branch), + `V43 work must occur on version/v43 or v43/gate-N-* branches. Observed ${branch || 'detached HEAD'}.`, + ); + } + + for (const relativePath of [ + V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH, + 'packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts', + 'packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts', + 'uapi/app/deposit/deposit-route-model.ts', + 'uapi/app/deposit/DepositPageClient.tsx', + 'uapi/components/base/bitcode/activity/pack-activity-model.ts', + 'uapi/tests/depositRouteModel.test.ts', + 'uapi/tests/depositPageClient.test.tsx', + 'uapi/tests/packActivityModel.test.ts', + 'uapi/jest.config.cjs', + 'packages/protocol/src/canonical/v43-deposit-option-admission.js', + 'packages/protocol/test/v43-deposit-option-admission.test.js', + 'scripts/generate-v43-deposit-option-admission.mjs', + 'scripts/check-v43-gate7-deposit-option-admission.mjs', + 'BITCODE_SPEC_V43.md', + 'BITCODE_SPEC_V43_DELTA.md', + 'BITCODE_SPEC_V43_NOTES.md', + 'BITCODE_SPEC_V43_PARITY_MATRIX.md', + 'SPECIFICATIONS_ROADMAP.md', + 'README.md', + 'packages/protocol/README.md', + '.github/workflows/bitcode-gate-quality.yml', + '.github/workflows/bitcode-canon-quality.yml', + 'package.json', + ]) { + assertCheck(failures, exists(root, relativePath), `Missing required V43 Gate 7 file: ${relativePath}`); + } + + const artifact = buildV43DepositOptionAdmission({ repoRoot: root }); + assertCheck(failures, artifact.passed, `V43 deposit option admission predicates failed: ${artifact.coverage.failedPredicateIds.join(', ')}`); + assertCheck(failures, artifact.coverage.reviewDecisionsImplemented === true, 'Review decisions must be implemented.'); + assertCheck(failures, artifact.coverage.approvalDecisionImplemented === true, 'Approval decision must be implemented.'); + assertCheck(failures, artifact.coverage.rejectionDecisionImplemented === true, 'Rejection decision must be implemented.'); + assertCheck(failures, artifact.coverage.resynthesisDecisionImplemented === true, 'Resynthesis decision must be implemented.'); + assertCheck(failures, artifact.coverage.admissionReceiptsImplemented === true, 'Admission receipts must be implemented.'); + assertCheck(failures, artifact.coverage.approvedPolicyEligibleOptionsAdmittedOnly === true, 'Only approved policy-eligible options may be admitted.'); + assertCheck(failures, artifact.coverage.depositoryIndexProjectionImplemented === true, 'Depository index projection must be implemented.'); + assertCheck(failures, artifact.coverage.storageProjectionImplemented === true, 'Storage projection must be implemented.'); + assertCheck(failures, artifact.coverage.compensationPreviewContinued === true, 'Compensation preview continuity must be implemented.'); + assertCheck(failures, artifact.coverage.compensationPriceAsset === 'BTC', 'Compensation price asset must be BTC.'); + assertCheck(failures, artifact.coverage.packsActivitySynchronizationImplemented === true, '/packs activity synchronization must be implemented.'); + assertCheck(failures, artifact.coverage.packsActivityType === 'depository-assetpack', '/packs activity type must be depository-assetpack.'); + assertCheck(failures, artifact.coverage.telemetryImplemented === true, 'Admission telemetry must be implemented.'); + assertCheck(failures, artifact.coverage.routeAdmissionReadbackImplemented === true, '/deposit admission readback must be implemented.'); + assertCheck(failures, artifact.coverage.btdMintRequiresFutureNeedFitSettlement === true, 'BTD minting must remain future Need-Fit settlement gated.'); + assertCheck(failures, artifact.coverage.sourceSafeMetadataOnly === true, 'Artifact must be source-safe metadata only.'); + assertCheck(failures, artifact.coverage.protectedSourceVisible === false, 'Artifact must not expose protected source.'); + assertCheck(failures, artifact.coverage.rawSourceTextVisible === false, 'Artifact must not expose raw source text.'); + assertCheck(failures, artifact.coverage.unpaidAssetPackSourceVisible === false, 'Artifact must not expose unpaid AssetPack source.'); + assertCheck(failures, artifact.coverage.rawProviderResponseVisible === false, 'Artifact must not expose raw provider responses.'); + assertCheck(failures, artifact.coverage.walletPrivateMaterialVisible === false, 'Artifact must not expose wallet private material.'); + assertCheck(failures, artifact.coverage.settlementPrivatePayloadVisible === false, 'Artifact must not expose settlement private payload.'); + + const serialized = `${JSON.stringify(artifact, null, 2)}\n`; + assertCheck( + failures, + exists(root, V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH) && + read(root, V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH) === serialized, + `${V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH} must be generated and current.`, + ); + + const packageJson = read(root, 'package.json'); + const gateWorkflow = read(root, '.github/workflows/bitcode-gate-quality.yml'); + const canonWorkflow = read(root, '.github/workflows/bitcode-canon-quality.yml'); + assertCheck(failures, packageJson.includes('"generate:v43-deposit-option-admission"'), 'package.json must expose generate:v43-deposit-option-admission.'); + assertCheck(failures, packageJson.includes('"check:v43-deposit-option-admission"'), 'package.json must expose check:v43-deposit-option-admission.'); + assertCheck(failures, packageJson.includes('"check:v43-gate7"'), 'package.json must expose check:v43-gate7.'); + assertCheck(failures, gateWorkflow.includes('check-v43-gate7-deposit-option-admission.mjs'), 'Gate workflow must run V43 Gate 7 checker.'); + assertCheck(failures, canonWorkflow.includes('check-v43-gate7-deposit-option-admission.mjs'), 'Canon workflow must run V43 Gate 7 checker.'); + + if (!args.skipPackageTests) { + try { + run(root, 'pnpm', ['--filter', '@bitcode/pipeline-asset-pack', 'exec', 'jest', 'deposit-asset-pack-option-admission.test.ts', '--runInBand']); + } catch { + failures.push('asset-pack deposit-asset-pack-option-admission.test.ts must pass.'); + } + } + + if (!args.skipUapiTests) { + try { + run(root, 'pnpm', ['--dir', 'uapi', 'exec', 'jest', 'depositRouteModel.test.ts', 'depositPageClient.test.tsx', 'packActivityModel.test.ts', '--runInBand']); + } catch { + failures.push('uapi depositRouteModel.test.ts, depositPageClient.test.tsx, and packActivityModel.test.ts must pass.'); + } + } + + if (failures.length > 0) { + process.stderr.write('V43 Gate 7 deposit option admission check failed:\n'); + for (const failure of failures.filter(Boolean)) process.stderr.write(`- ${failure}\n`); + process.exitCode = 1; + return; + } + + process.stdout.write('V43 Gate 7 deposit option admission check passed.\n'); +} + +try { + main(); +} catch (error) { + const detail = error instanceof Error ? error.message : String(error); + process.stderr.write(`${detail}\n`); + process.exitCode = 1; +} diff --git a/scripts/generate-v43-deposit-option-admission.mjs b/scripts/generate-v43-deposit-option-admission.mjs new file mode 100644 index 00000000..5e9f69e7 --- /dev/null +++ b/scripts/generate-v43-deposit-option-admission.mjs @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH, + buildV43DepositOptionAdmission, +} from '../packages/protocol/src/canonical/v43-deposit-option-admission.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const repoRoot = path.resolve(__dirname, '..'); +const check = process.argv.includes('--check'); +const artifact = buildV43DepositOptionAdmission({ repoRoot }); +const serialized = `${JSON.stringify(artifact, null, 2)}\n`; +const artifactPath = path.join(repoRoot, V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH); + +if (check) { + if (!existsSync(artifactPath) || readFileSync(artifactPath, 'utf8') !== serialized) { + process.stderr.write( + `${V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH} is stale. Run pnpm run generate:v43-deposit-option-admission.\n`, + ); + process.exitCode = 1; + } +} else { + mkdirSync(path.dirname(artifactPath), { recursive: true }); + writeFileSync(artifactPath, serialized); + process.stdout.write(`wrote ${V43_DEPOSIT_OPTION_ADMISSION_ARTIFACT_PATH}\n`); +} diff --git a/uapi/app/deposit/DepositPageClient.tsx b/uapi/app/deposit/DepositPageClient.tsx index c005832f..945697ad 100644 --- a/uapi/app/deposit/DepositPageClient.tsx +++ b/uapi/app/deposit/DepositPageClient.tsx @@ -32,9 +32,14 @@ import { TerminalShellBridgeProvider } from '@/app/terminal/terminal-shell-bridg import { deriveTerminalTransactionReadiness } from '@/app/terminal/terminal-transaction-readiness-source'; import { buildDepositRouteSession, readDepositRouteStage, writeDepositRouteStage } from './deposit-route-model'; +import type { + DepositOptionReviewDecision, + DepositOptionReviewDecisionState, +} from '@bitcode/pipeline-asset-pack/deposit-asset-pack-option-admission'; const DEPOSIT_OPTION_PIPELINE_ID = 'DepositAssetPackOptionSynthesis'; const DEPOSIT_OPTION_POLICY_ID = 'DepositAssetPackOptionPolicy'; +const DEPOSIT_OPTION_ADMISSION_ID = 'DepositAssetPackOptionAdmissionReport'; function shortIdentifier(value: string | null | undefined) { if (!value) return 'pending'; @@ -93,7 +98,7 @@ export default function DepositPageClient() { ].join('\n'), ); const [optionsRequested, setOptionsRequested] = useState(false); - const [reviewedOptionId, setReviewedOptionId] = useState(null); + const [optionReviewDecisions, setOptionReviewDecisions] = useState>({}); const readCurrentSearchParams = useCallback( () => @@ -248,62 +253,81 @@ export default function DepositPageClient() { ), [liveRuns], ); - const depositRouteSession = useMemo( + const optionReviewDecisionRecords = useMemo( () => - buildDepositRouteSession({ - transactionId: selectedTransactionId || selectedRun?.id || null, - depositStage: routeDepositStage, - repositoryFullName: repositoryContext?.selectedRepository?.fullName || null, - sourceBranch: repositoryContext?.selectedBranch || null, - sourceCommit: repositoryContext?.selectedCommit || null, - depositorInstructions, - sourcePathHints, - depositoryDemandSignals: [ - { - id: 'depository-gap-source-safe-pack-options', - label: 'Depository benefits from reviewable source-safe AssetPack supply options.', - weight: 0.72, - }, - ], - readingDemandSignals: [ - { - id: 'reading-demand-fit-ready-source-supply', - label: 'Reading demand needs searchable, proof-bearing source supply for Finding Fits.', - weight: 0.8, - }, - ], - existingDepositorySignals: [ - { - id: 'existing-supply-compensation-route', - label: 'Existing supply expects proof roots, vector projections, and compensation previews.', - weight: 0.58, - }, - ], - sourceCriticalitySignals, - developmentCostSats: Math.max(1600, 1200 + sourcePathHints.length * 240), - expectedSettlementSats: Math.max(4200, 3600 + sourcePathHints.length * 360 + liveRuns.length * 90), - depositorWalletId: preferredSignerAddress ? 'connected-depositor-wallet' : null, - hasRepositorySource: Boolean(repositoryContext?.selectedRepository), - optionsRequested, - hasReviewedOption: Boolean(reviewedOptionId), - hasSubmittedDeposit, - hasDepositoryReadback, - }), + Object.entries(optionReviewDecisions).map(([optionId, decision]) => ({ + optionId, + decision, + reviewerId: user?.id || preferredSignerAddress || null, + })), + [optionReviewDecisions, preferredSignerAddress, user?.id], + ); + const depositRouteInput = useMemo( + () => ({ + transactionId: selectedTransactionId || selectedRun?.id || null, + depositStage: routeDepositStage, + repositoryFullName: repositoryContext?.selectedRepository?.fullName || null, + sourceBranch: repositoryContext?.selectedBranch || null, + sourceCommit: repositoryContext?.selectedCommit || null, + depositorInstructions, + sourcePathHints, + depositoryDemandSignals: [ + { + id: 'depository-gap-source-safe-pack-options', + label: 'Depository benefits from reviewable source-safe AssetPack supply options.', + weight: 0.72, + }, + ], + readingDemandSignals: [ + { + id: 'reading-demand-fit-ready-source-supply', + label: 'Reading demand needs searchable, proof-bearing source supply for Finding Fits.', + weight: 0.8, + }, + ], + existingDepositorySignals: [ + { + id: 'existing-supply-compensation-route', + label: 'Existing supply expects proof roots, vector projections, and compensation previews.', + weight: 0.58, + }, + ], + sourceCriticalitySignals, + developmentCostSats: Math.max(1600, 1200 + sourcePathHints.length * 240), + expectedSettlementSats: Math.max(4200, 3600 + sourcePathHints.length * 360 + liveRuns.length * 90), + depositorWalletId: preferredSignerAddress ? 'connected-depositor-wallet' : null, + reviewerId: user?.id || preferredSignerAddress || null, + hasRepositorySource: Boolean(repositoryContext?.selectedRepository), + optionsRequested, + hasReviewedOption: optionReviewDecisionRecords.length > 0, + hasSubmittedDeposit, + hasDepositoryReadback, + }), [ depositorInstructions, hasDepositoryReadback, hasSubmittedDeposit, + liveRuns.length, optionsRequested, + optionReviewDecisionRecords.length, + preferredSignerAddress, repositoryContext, - reviewedOptionId, routeDepositStage, selectedRun?.id, selectedTransactionId, sourceCriticalitySignals, sourcePathHints, - preferredSignerAddress, + user?.id, ], ); + const depositRouteSession = useMemo( + () => + buildDepositRouteSession({ + ...depositRouteInput, + optionReviewDecisions: optionReviewDecisionRecords, + }), + [depositRouteInput, optionReviewDecisionRecords], + ); const sessionRows = [ { label: 'Repository', value: depositRouteSession.routeState.repositoryFullName || 'select repository' }, @@ -312,8 +336,10 @@ export default function DepositPageClient() { { label: 'Transaction', value: shortIdentifier(depositRouteSession.routeState.transactionId) }, { label: 'Pipeline', value: DEPOSIT_OPTION_PIPELINE_ID }, { label: 'Policy', value: DEPOSIT_OPTION_POLICY_ID }, + { label: 'Admission', value: DEPOSIT_OPTION_ADMISSION_ID }, { label: 'Option roots', value: String(depositRouteSession.synthesis.roots.optionRoots.length) }, { label: 'Positive ROI options', value: String(depositRouteSession.policy.reviewablePositiveRoiCount) }, + { label: 'Admitted options', value: String(depositRouteSession.admission.admittedCount) }, ]; const recentDepositRuns = useMemo( @@ -355,6 +381,79 @@ export default function DepositPageClient() { [refreshLiveRuns, replaceDepositRouteTransaction, repositoryContext, selectedRun], ); + const handleOptionReviewDecision = useCallback( + async (optionId: string, decision: DepositOptionReviewDecisionState) => { + const nextDecisions = { + ...optionReviewDecisions, + [optionId]: decision, + }; + setOptionsRequested(true); + setOptionReviewDecisions(nextDecisions); + + const nextDecisionRecords = Object.entries(nextDecisions).map(([entryOptionId, entryDecision]) => ({ + optionId: entryOptionId, + decision: entryDecision, + reviewerId: user?.id || preferredSignerAddress || null, + })); + const nextSession = buildDepositRouteSession({ + ...depositRouteInput, + optionsRequested: true, + hasReviewedOption: true, + optionReviewDecisions: nextDecisionRecords, + }); + const receipt = nextSession.admission.receipts.find((entry) => entry.optionId === optionId); + const admitted = receipt?.admission.state === 'admitted-to-depository'; + replaceDepositSearchParams( + writeDepositRouteStage(readCurrentSearchParams(), admitted ? 'read-depository-state' : 'review-options'), + ); + + if (!receipt) return; + + try { + await handleRecordActivity({ + type: admitted ? 'pipeline:deposit-option-admission' : 'pipeline:deposit-option-review', + status: 'completed', + summary: admitted + ? `Admitted ${receipt.title} to the Depository.` + : `Recorded ${decision.replace(/-/g, ' ')} for ${receipt.title}.`, + selectAfterRecord: admitted, + output: { + assetPackTitle: receipt.title, + depositAdmission: nextSession.admission, + admissionState: receipt.admission.state, + depositoryAssetPackId: receipt.admission.depositoryAssetPackId, + compensationState: receipt.compensationPreview.state, + packActivitySyncState: receipt.packsActivitySync.state, + packsActivityRoot: receipt.packsActivitySync.activityRoot, + }, + context: { + source: 'deposit-option-review-admission', + workbench: 'deposit-option-review', + optionId, + reviewDecision: decision, + admissionState: receipt.admission.state, + depositoryAssetPackId: receipt.admission.depositoryAssetPackId, + compensationState: receipt.compensationPreview.state, + packActivitySyncState: receipt.packsActivitySync.state, + packActivityType: receipt.packsActivitySync.activityType, + packsRoute: receipt.packsActivitySync.route, + }, + }); + } catch (error) { + setRunsLoadError(error instanceof Error ? error.message : 'Unable to record deposit option review.'); + } + }, + [ + depositRouteInput, + handleRecordActivity, + optionReviewDecisions, + preferredSignerAddress, + readCurrentSearchParams, + replaceDepositSearchParams, + user?.id, + ], + ); + const handleRepositorySourceBranchChange = useCallback( (branch: string) => { const nextParams = readCurrentSearchParams(); @@ -508,10 +607,14 @@ export default function DepositPageClient() {
{depositRouteSession.synthesis.options.map((option) => { - const reviewed = reviewedOptionId === option.optionId; + const reviewDecision = optionReviewDecisions[option.optionId] || 'pending-depositor-review'; + const reviewed = reviewDecision !== 'pending-depositor-review'; const policyEvaluation = depositRouteSession.policy.evaluations.find( (evaluation) => evaluation.optionId === option.optionId, ); + const admissionReceipt = depositRouteSession.admission.receipts.find( + (receipt) => receipt.optionId === option.optionId, + ); return (
+ {admissionReceipt ? ( +
+
Admission
+
+ {admissionReceipt.admission.state} / {admissionReceipt.packsActivitySync.state} +
+
+ ) : null} ) : null} {option.measurements.map((measurement) => ( @@ -584,16 +695,40 @@ export default function DepositPageClient() { ))} - +
+ +
+ + +
+

+ {reviewed ? reviewDecision.replace(/-/g, ' ') : 'Pending depositor review'} +

+
); })} @@ -702,7 +837,7 @@ export default function DepositPageClient() { )} Open pack activity diff --git a/uapi/app/deposit/deposit-route-model.ts b/uapi/app/deposit/deposit-route-model.ts index 7a915d67..428fd48c 100644 --- a/uapi/app/deposit/deposit-route-model.ts +++ b/uapi/app/deposit/deposit-route-model.ts @@ -10,6 +10,12 @@ import { type DepositAssetPackOptionPolicyReport, type DepositOptionCriticalitySignal, } from '@bitcode/pipeline-asset-pack/deposit-asset-pack-option-policy'; +import { + assertDepositAssetPackOptionAdmissionReportSourceSafe, + buildDepositAssetPackOptionAdmissionReport, + type DepositAssetPackOptionAdmissionReport, + type DepositOptionReviewDecision, +} from '@bitcode/pipeline-asset-pack/deposit-asset-pack-option-admission'; export type DepositRouteStepId = | 'connect-source' @@ -27,6 +33,8 @@ export interface DepositRouteSessionInput extends DepositOptionSynthesisRequest developmentCostSats?: number | null; expectedSettlementSats?: number | null; depositorWalletId?: string | null; + optionReviewDecisions?: DepositOptionReviewDecision[] | null; + reviewerId?: string | null; hasRepositorySource?: boolean; optionsRequested?: boolean; hasReviewedOption?: boolean; @@ -58,14 +66,16 @@ export interface DepositRouteSession { pipelineOwnership: { depositOptionPipeline: 'DepositAssetPackOptionSynthesis'; depositOptionPolicy: 'DepositAssetPackOptionPolicy'; + depositOptionAdmission: 'DepositAssetPackOptionAdmissionReport'; reviewRequiredBeforeDepositAdmission: true; sourceCriticalityDemandRoiPolicyOwnedByGate6: true; sourceCriticalityDemandRoiPolicyDeferredToGate6: true; - admissionAndIndexingDeferredToGate7: true; + admissionAndIndexingOwnedByGate7: true; retainedTerminalDebugCompatible: true; }; synthesis: DepositAssetPackOptionSynthesis; policy: DepositAssetPackOptionPolicyReport; + admission: DepositAssetPackOptionAdmissionReport; disclosure: { sourceSafetyClass: 'source_safe_deposit_option_route_metadata'; lowDetailDefault: true; @@ -129,34 +139,53 @@ function normalizedText(value: string | null | undefined) { return normalized ? normalized : null; } -function resolveActiveStep(input: DepositRouteSessionInput): DepositRouteStepId { +function hasReviewDecision(input: DepositRouteSessionInput) { + return Boolean( + input.hasReviewedOption || + input.optionReviewDecisions?.some((decision) => decision.decision !== 'pending-depositor-review'), + ); +} + +function resolveActiveStep(input: DepositRouteSessionInput, admittedCount = 0): DepositRouteStepId { if (input.depositStage && DEPOSIT_ROUTE_STAGE_IDS.includes(input.depositStage)) return input.depositStage; if (input.hasDepositoryReadback) return 'read-depository-state'; - if (input.hasSubmittedDeposit) return 'submit-deposit'; - if (input.hasReviewedOption) return 'review-options'; + if (input.hasSubmittedDeposit || admittedCount > 0) return 'submit-deposit'; + if (hasReviewDecision(input)) return 'review-options'; if (input.optionsRequested || input.hasRepositorySource) return 'synthesize-options'; return 'connect-source'; } -function stepState(input: DepositRouteSessionInput, stepId: DepositRouteStepId, activeStepId: DepositRouteStepId): DepositRouteStepState { +function stepState( + input: DepositRouteSessionInput, + stepId: DepositRouteStepId, + activeStepId: DepositRouteStepId, + admittedCount: number, +): DepositRouteStepState { + const reviewed = hasReviewDecision(input); + const submitted = Boolean(input.hasSubmittedDeposit || admittedCount > 0); if (stepId === activeStepId) return 'current'; if (stepId === 'connect-source') return input.hasRepositorySource ? 'complete' : 'ready'; if (stepId === 'synthesize-options') return input.optionsRequested ? 'complete' : input.hasRepositorySource ? 'ready' : 'blocked'; - if (stepId === 'review-options') return input.hasReviewedOption ? 'complete' : input.optionsRequested ? 'ready' : 'blocked'; - if (stepId === 'submit-deposit') return input.hasSubmittedDeposit ? 'complete' : input.hasReviewedOption ? 'ready' : 'blocked'; - return input.hasDepositoryReadback ? 'complete' : input.hasSubmittedDeposit ? 'ready' : 'blocked'; + if (stepId === 'review-options') return reviewed ? 'complete' : input.optionsRequested ? 'ready' : 'blocked'; + if (stepId === 'submit-deposit') return submitted ? 'complete' : reviewed ? 'ready' : 'blocked'; + return input.hasDepositoryReadback ? 'complete' : submitted ? 'ready' : 'blocked'; } -function stepBlockers(input: DepositRouteSessionInput, stepId: DepositRouteStepId): string[] { +function stepBlockers(input: DepositRouteSessionInput, stepId: DepositRouteStepId, admittedCount: number): string[] { const blockers: string[] = []; + const reviewed = hasReviewDecision(input); + const submitted = Boolean(input.hasSubmittedDeposit || admittedCount > 0); if (stepId !== 'connect-source' && !input.hasRepositorySource) blockers.push('repository source required'); if (['review-options', 'submit-deposit', 'read-depository-state'].includes(stepId) && !input.optionsRequested) { blockers.push('deposit AssetPack options required'); } - if (['submit-deposit', 'read-depository-state'].includes(stepId) && !input.hasReviewedOption) { + if (['submit-deposit', 'read-depository-state'].includes(stepId) && !reviewed) { blockers.push('depositor option review required'); } - if (stepId === 'read-depository-state' && !input.hasSubmittedDeposit) blockers.push('submitted deposit required'); + if (['submit-deposit', 'read-depository-state'].includes(stepId) && reviewed && admittedCount === 0) { + blockers.push('approved admissible option required'); + } + if (stepId === 'read-depository-state' && !submitted) blockers.push('submitted deposit required'); return blockers; } @@ -173,7 +202,6 @@ export function writeDepositRouteStage(params: URLSearchParams, stage: DepositRo } export function buildDepositRouteSession(input: DepositRouteSessionInput = {}): DepositRouteSession { - const activeStepId = resolveActiveStep(input); const repositoryFullName = normalizedText(input.repositoryFullName); const sourceBranch = normalizedText(input.sourceBranch); const sourceCommit = normalizedText(input.sourceCommit); @@ -196,10 +224,19 @@ export function buildDepositRouteSession(input: DepositRouteSessionInput = {}): depositorWalletId: input.depositorWalletId, createdAt: input.createdAt, }); + const admission = buildDepositAssetPackOptionAdmissionReport({ + synthesis, + policy, + decisions: input.optionReviewDecisions, + reviewerId: input.reviewerId, + telemetryRunId: normalizedText(input.transactionId), + createdAt: input.createdAt, + }); + const activeStepId = resolveActiveStep(input, admission.admittedCount); const steps = DEPOSIT_ROUTE_STEPS.map((step) => ({ ...step, - state: stepState(input, step.id, activeStepId), - blockers: stepBlockers(input, step.id), + state: stepState(input, step.id, activeStepId, admission.admittedCount), + blockers: stepBlockers(input, step.id, admission.admittedCount), })); const seed = JSON.stringify({ transactionId: normalizedText(input.transactionId), @@ -209,6 +246,7 @@ export function buildDepositRouteSession(input: DepositRouteSessionInput = {}): sourceCommit, synthesisRoot: synthesis.roots.synthesisRoot, policyReportRoot: policy.roots.policyReportRoot, + admissionReportRoot: admission.roots.admissionReportRoot, steps: steps.map((step) => ({ id: step.id, state: step.state, blockers: step.blockers })), }); @@ -228,14 +266,16 @@ export function buildDepositRouteSession(input: DepositRouteSessionInput = {}): pipelineOwnership: { depositOptionPipeline: 'DepositAssetPackOptionSynthesis', depositOptionPolicy: 'DepositAssetPackOptionPolicy', + depositOptionAdmission: 'DepositAssetPackOptionAdmissionReport', reviewRequiredBeforeDepositAdmission: true, sourceCriticalityDemandRoiPolicyOwnedByGate6: true, sourceCriticalityDemandRoiPolicyDeferredToGate6: true, - admissionAndIndexingDeferredToGate7: true, + admissionAndIndexingOwnedByGate7: true, retainedTerminalDebugCompatible: true, }, synthesis, policy, + admission, disclosure: { sourceSafetyClass: 'source_safe_deposit_option_route_metadata', lowDetailDefault: true, @@ -255,18 +295,21 @@ export function buildDepositRouteSession(input: DepositRouteSessionInput = {}): export function assertDepositRouteSessionSourceSafe(session: DepositRouteSession) { const synthesisSafety = assertDepositAssetPackOptionSynthesisSourceSafe(session.synthesis); const policySafety = assertDepositAssetPackOptionPolicyReportSourceSafe(session.policy); + const admissionSafety = assertDepositAssetPackOptionAdmissionReportSourceSafe(session.admission); const sourceSafe = synthesisSafety.admitted && policySafety.admitted && + admissionSafety.admitted && session.schema === 'bitcode.deposit.route-session' && session.route === '/deposit' && session.stageCount === 5 && session.pipelineOwnership.depositOptionPipeline === 'DepositAssetPackOptionSynthesis' && session.pipelineOwnership.depositOptionPolicy === 'DepositAssetPackOptionPolicy' && + session.pipelineOwnership.depositOptionAdmission === 'DepositAssetPackOptionAdmissionReport' && session.pipelineOwnership.reviewRequiredBeforeDepositAdmission === true && session.pipelineOwnership.sourceCriticalityDemandRoiPolicyOwnedByGate6 === true && session.pipelineOwnership.sourceCriticalityDemandRoiPolicyDeferredToGate6 === true && - session.pipelineOwnership.admissionAndIndexingDeferredToGate7 === true && + session.pipelineOwnership.admissionAndIndexingOwnedByGate7 === true && session.disclosure.sourceSafetyClass === 'source_safe_deposit_option_route_metadata' && session.disclosure.protectedSourceVisible === false && session.disclosure.rawSourceTextVisible === false && diff --git a/uapi/components/base/bitcode/activity/pack-activity-model.ts b/uapi/components/base/bitcode/activity/pack-activity-model.ts index 34d6c7e3..57e6e9c2 100644 --- a/uapi/components/base/bitcode/activity/pack-activity-model.ts +++ b/uapi/components/base/bitcode/activity/pack-activity-model.ts @@ -263,8 +263,18 @@ function inferPackActivityType(record: BitcodeActivityRecord): PackActivityType .join(' ') .toLowerCase(); + if ( + includesAny(haystack, [ + 'depository assetpack', + 'depository asset pack', + 'deposit admission', + 'deposit-option-admission', + 'admitted to the depository', + ]) + ) { + return 'depository-assetpack'; + } if (includesAny(haystack, ['deposit option', 'deposit-option', 'option synthesis'])) return 'deposit-option'; - if (includesAny(haystack, ['depository assetpack', 'depository asset pack', 'deposit admission'])) return 'depository-assetpack'; if (includesAny(haystack, ['finding fits', 'fits finding', 'read-fits', 'fit preview', 'assetpack preview'])) return 'read-need-fit-preview'; if (includesAny(haystack, ['settled assetpack', 'settled asset pack', 'rights transfer'])) return 'settled-assetpack'; if (includesAny(haystack, ['settlement', 'btc', 'finality'])) return 'settlement'; @@ -313,7 +323,8 @@ function buildMeasurements(record: BitcodeActivityRecord): PackActivityMeasureme ['token-total', ['total_tokens', 'tokenTotal', 'totalTokens'], 'tokens'], ['duration', ['duration_ms', 'durationMs', 'runtimeMs'], 'ms'], ['cost', ['total_cost', 'totalCost'], 'USD'], - ['candidate-count', ['candidateCount', 'fitCandidateCount', 'targetKindCount'], 'count'], + ['candidate-count', ['candidateCount', 'fitCandidateCount', 'targetKindCount', 'optionCount'], 'count'], + ['admitted-count', ['admittedCount'], 'count'], ['closure-criteria', ['closureCriteriaCount', 'closureCount'], 'count'], ]; @@ -329,6 +340,8 @@ function buildMeasurements(record: BitcodeActivityRecord): PackActivityMeasureme 'depositMeasurementRoot', 'assetPackMeasurementRoot', 'readNeedMeasurementRoot', + 'admissionReportRoot', + 'admissionRoot', ]); if (measurementRoot) { measurements.push({ diff --git a/uapi/jest.config.cjs b/uapi/jest.config.cjs index dd7f3eb0..a637587e 100644 --- a/uapi/jest.config.cjs +++ b/uapi/jest.config.cjs @@ -54,6 +54,7 @@ module.exports = { '^@bitcode/pipeline-asset-pack/reading-pipeline-contract$': '/../packages/pipelines/asset-pack/src/reading-pipeline-contract.ts', '^@bitcode/pipeline-asset-pack/deposit-asset-pack-options$': '/../packages/pipelines/asset-pack/src/deposit-asset-pack-options.ts', '^@bitcode/pipeline-asset-pack/deposit-asset-pack-option-policy$': '/../packages/pipelines/asset-pack/src/deposit-asset-pack-option-policy.ts', + '^@bitcode/pipeline-asset-pack/deposit-asset-pack-option-admission$': '/../packages/pipelines/asset-pack/src/deposit-asset-pack-option-admission.ts', '^@bitcode/pipeline-asset-pack/src/(.+)$': '/../packages/pipelines/asset-pack/src/$1', '^@bitcode/([^/]+)/src/(.+)$': '/../packages/$1/src/$2', '^@bitcode/supabase/ssr/server$': '/tests/mocks/supabaseServerClient.ts', diff --git a/uapi/tests/depositPageClient.test.tsx b/uapi/tests/depositPageClient.test.tsx index 3f4091e6..af083014 100644 --- a/uapi/tests/depositPageClient.test.tsx +++ b/uapi/tests/depositPageClient.test.tsx @@ -151,7 +151,10 @@ describe('DepositPageClient', () => { expect(screen.getByText('Source-safe deposit state')).toBeInTheDocument(); expect(screen.getAllByText('DepositAssetPackOptionSynthesis').length).toBeGreaterThan(0); expect(screen.getAllByText('DepositAssetPackOptionPolicy').length).toBeGreaterThan(0); + expect(screen.getAllByText('DepositAssetPackOptionAdmissionReport').length).toBeGreaterThan(0); expect(screen.getAllByText(/BTC source-to-shares preview/u).length).toBeGreaterThan(0); + expect(screen.getAllByText(/Approve for Depository/u).length).toBeGreaterThan(0); + expect(screen.getAllByText(/not-admitted-pending-review/u).length).toBeGreaterThan(0); expect(screen.getByTestId('deposit-option-capability-slice')).toBeInTheDocument(); expect(screen.getByTestId('deposit-option-implementation-pattern')).toBeInTheDocument(); expect(screen.getByTestId('deposit-option-proof-operations-slice')).toBeInTheDocument(); diff --git a/uapi/tests/depositRouteModel.test.ts b/uapi/tests/depositRouteModel.test.ts index e79a8bee..29e331c0 100644 --- a/uapi/tests/depositRouteModel.test.ts +++ b/uapi/tests/depositRouteModel.test.ts @@ -25,6 +25,12 @@ describe('deposit-route-model', () => { ], depositorWalletId: 'wallet-depositor-1', optionsRequested: true, + optionReviewDecisions: [ + { + optionId: 'will-be-ignored-until-synthesis-option-id-is-known', + decision: 'pending-depositor-review', + }, + ], }); expect(session.schema).toBe('bitcode.deposit.route-session'); @@ -41,10 +47,11 @@ describe('deposit-route-model', () => { expect(session.pipelineOwnership).toMatchObject({ depositOptionPipeline: 'DepositAssetPackOptionSynthesis', depositOptionPolicy: 'DepositAssetPackOptionPolicy', + depositOptionAdmission: 'DepositAssetPackOptionAdmissionReport', reviewRequiredBeforeDepositAdmission: true, sourceCriticalityDemandRoiPolicyOwnedByGate6: true, sourceCriticalityDemandRoiPolicyDeferredToGate6: true, - admissionAndIndexingDeferredToGate7: true, + admissionAndIndexingOwnedByGate7: true, retainedTerminalDebugCompatible: true, }); expect(session.synthesis.schema).toBe('bitcode.deposit.asset-pack-option-synthesis'); @@ -52,6 +59,8 @@ describe('deposit-route-model', () => { expect(session.policy.schema).toBe('bitcode.deposit.asset-pack-option-policy-report'); expect(session.policy.reviewablePositiveRoiCount).toBeGreaterThan(0); expect(session.policy.aggregatePolicy.compensationPolicy).toBe('future-reader-btc-source-to-shares-route-preview'); + expect(session.admission.schema).toBe('bitcode.deposit.asset-pack-option-admission-report'); + expect(session.admission.receipts.every((receipt) => receipt.admission.state === 'not-admitted-pending-review')).toBe(true); expect(session.synthesis.options[0].reviewBoundary.state).toBe('reviewable-source-safe-option'); expect(session.disclosure).toMatchObject({ sourceSafetyClass: 'source_safe_deposit_option_route_metadata', @@ -106,10 +115,46 @@ describe('deposit-route-model', () => { expect(session.policy.blockedCount).toBe(3); expect(session.policy.evaluations.every((evaluation) => evaluation.policyDecision === 'blocked-before-admission')).toBe(true); - expect(session.pipelineOwnership.admissionAndIndexingDeferredToGate7).toBe(true); + expect(session.pipelineOwnership.admissionAndIndexingOwnedByGate7).toBe(true); expect(assertDepositRouteSessionSourceSafe(session).admitted).toBe(true); }); + it('admits approved policy-eligible deposit options into source-safe Depository projections', () => { + const initial = buildDepositRouteSession({ + repositoryFullName: 'engineeredsoftware/ENGI', + sourceBranch: 'main', + sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a', + sourcePathHints: ['uapi/app/deposit/DepositPageClient.tsx'], + sourceCriticalitySignals: [{ id: 'sub-critical', severity: 'sub-critical', weight: 0.85 }], + depositorWalletId: 'wallet-depositor-1', + optionsRequested: true, + }); + const approved = buildDepositRouteSession({ + repositoryFullName: 'engineeredsoftware/ENGI', + sourceBranch: 'main', + sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a', + sourcePathHints: ['uapi/app/deposit/DepositPageClient.tsx'], + sourceCriticalitySignals: [{ id: 'sub-critical', severity: 'sub-critical', weight: 0.85 }], + depositorWalletId: 'wallet-depositor-1', + optionsRequested: true, + optionReviewDecisions: [ + { + optionId: initial.synthesis.options[0].optionId, + decision: 'approved-for-admission', + reviewerId: 'depositor-1', + }, + ], + }); + + expect(approved.admission.approvedCount).toBe(1); + expect(approved.admission.admittedCount).toBe(1); + expect(approved.admission.receipts[0].admission.state).toBe('admitted-to-depository'); + expect(approved.admission.receipts[0].depositoryIndexProjection.state).toBe('indexed-for-finding-fits'); + expect(approved.admission.receipts[0].storageProjection.state).toBe('projected-to-object-storage'); + expect(approved.admission.receipts[0].packsActivitySync.state).toBe('synchronized-to-packs'); + expect(assertDepositRouteSessionSourceSafe(approved).admitted).toBe(true); + }); + it('reads and writes the route-owned depositStage query parameter', () => { const params = new URLSearchParams('transactionId=deposit-run-2'); const withStage = writeDepositRouteStage(params, 'submit-deposit'); diff --git a/uapi/tests/packActivityModel.test.ts b/uapi/tests/packActivityModel.test.ts index fa11f2d6..3bdd68f2 100644 --- a/uapi/tests/packActivityModel.test.ts +++ b/uapi/tests/packActivityModel.test.ts @@ -96,4 +96,35 @@ describe('pack-activity-model', () => { expect(detail.proofRoots.length).toBeGreaterThan(0); expect(assertPackActivitySourceSafe(detail)).toBe(true); }); + + it('projects approved deposit admission receipts as Depository AssetPack activity', () => { + const record = normalizePackActivityRecord({ + ...baseRecord, + id: 'pack-activity-deposit-admission', + title: 'Pipeline execution', + summary: 'Admitted Repository capability AssetPack option to the Depository.', + state: 'completed', + payload: { + type: 'pipeline:deposit-option-admission', + assetPackTitle: 'Repository capability AssetPack option', + optionCount: 3, + admittedCount: 1, + admissionState: 'admitted-to-depository', + compensationState: 'compensation-preview-ready', + packActivitySyncState: 'synchronized-to-packs', + admissionReportRoot: 'deposit-admission-report-root', + protectedSource: 'protected source body', + rawProviderResponse: 'raw provider response', + }, + }); + + expect(record.type).toBe('depository-assetpack'); + expect(record.assetPackTitle).toBe('Repository capability AssetPack option'); + expect(record.measurements.some((measurement) => measurement.id === 'admitted-count')).toBe(true); + expect(record.compensationState).toBe('compensation-preview-ready'); + expect(record.proofRoots.map((proofRoot) => proofRoot.root)).toContain('deposit-admission-report-root'); + expect(assertPackActivitySourceSafe(record)).toBe(true); + expect(JSON.stringify(record)).not.toContain('protected source body'); + expect(JSON.stringify(record)).not.toContain('raw provider response'); + }); });