@atmem/architect 0.0.0-stage → 0.2.0-alpha.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/CONSTITUTION.md +144 -0
  2. package/README.md +33 -2
  3. package/agent-skills/discovery/SKILL.md +48 -0
  4. package/agent-skills/discovery/topics.json +139 -0
  5. package/agent-skills/generation-capabilities.json +760 -0
  6. package/agent-skills/service-lifecycle.json +21 -0
  7. package/agent-skills/specialists/architecture/SKILL.md +15 -0
  8. package/agent-skills/specialists/aws/SKILL.md +17 -0
  9. package/agent-skills/specialists/azure/SKILL.md +7 -0
  10. package/agent-skills/specialists/composition/SKILL.md +17 -0
  11. package/agent-skills/specialists/devops/SKILL.md +10 -0
  12. package/agent-skills/specialists/finops/SKILL.md +7 -0
  13. package/agent-skills/specialists/gcp/SKILL.md +7 -0
  14. package/agent-skills/specialists/ibm/SKILL.md +7 -0
  15. package/agent-skills/specialists/manifest.json +13 -0
  16. package/agent-skills/specialists/pulumi/SKILL.md +7 -0
  17. package/agent-skills/specialists/security/SKILL.md +7 -0
  18. package/agent-skills/specialists/terraform/SKILL.md +14 -0
  19. package/agent-skills/upstream/aws/LICENSE +175 -0
  20. package/agent-skills/upstream/aws/SKILL.md +125 -0
  21. package/agent-skills/upstream/azure/LICENSE +21 -0
  22. package/agent-skills/upstream/azure/SKILL.md +387 -0
  23. package/agent-skills/upstream/gcp/LICENSE +202 -0
  24. package/agent-skills/upstream/gcp/SKILL.md +159 -0
  25. package/agent-skills/upstream/manifest.json +57 -0
  26. package/agent-skills/upstream/pulumi/LICENSE +201 -0
  27. package/agent-skills/upstream/pulumi/SKILL.md +501 -0
  28. package/agent-skills/upstream/terraform/LICENSE +373 -0
  29. package/agent-skills/upstream/terraform/SKILL.md +316 -0
  30. package/agent-skills/workflows/workflows.json +601 -0
  31. package/cli/architect.mjs +3 -0
  32. package/cli/runtime.mjs +115 -0
  33. package/dist/assets/html2canvas.esm-QH1iLAAe.js +22 -0
  34. package/dist/assets/index-B97MkVMs.css +1 -0
  35. package/dist/assets/index-qtCM2D7O.js +21271 -0
  36. package/dist/assets/index.es-C6C0sneB.js +18 -0
  37. package/dist/assets/purify.es-CAi2OJFm.js +3 -0
  38. package/dist/assets/visual-export-DjbjgQbA.js +173 -0
  39. package/dist/favicon.svg +2 -0
  40. package/dist/fonts/DM-Sans-OFL.txt +93 -0
  41. package/dist/fonts/Manrope-OFL.txt +93 -0
  42. package/dist/fonts/NotoSans-Regular.ttf +0 -0
  43. package/dist/fonts/OFL.txt +94 -0
  44. package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
  45. package/dist/fonts/manrope-latin-wght-normal.woff2 +0 -0
  46. package/dist/index.html +4 -0
  47. package/dist/licenses/aws-architect-LICENSE.txt +17 -0
  48. package/dist/licenses/aws-mcp-LICENSE.txt +175 -0
  49. package/dist/licenses/aws-mcp-NOTICE.txt +1 -0
  50. package/dist/licenses/cloud-emulation-notices.txt +36619 -0
  51. package/dist/licenses/dependency-notices.txt +6379 -0
  52. package/dist/licenses/emulation-notices.txt +717 -0
  53. package/dist/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  54. package/dist/licenses/memory-dependency-notices.txt +3319 -0
  55. package/dist/licenses/openarchflow-LICENSE.txt +21 -0
  56. package/dist/licenses/python-dependency-notices.txt +11951 -0
  57. package/docs/upstream-lock.json +26 -0
  58. package/knowledge/engineering-practices.json +536 -0
  59. package/package.json +30 -4
  60. package/packaging/uv-release-lock.json +54 -0
  61. package/public/licenses/aws-architect-LICENSE.txt +17 -0
  62. package/public/licenses/aws-mcp-LICENSE.txt +175 -0
  63. package/public/licenses/aws-mcp-NOTICE.txt +1 -0
  64. package/public/licenses/cloud-emulation-notices.txt +36619 -0
  65. package/public/licenses/dependency-notices.txt +6379 -0
  66. package/public/licenses/emulation-notices.txt +717 -0
  67. package/public/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  68. package/public/licenses/memory-dependency-notices.txt +3319 -0
  69. package/public/licenses/openarchflow-LICENSE.txt +21 -0
  70. package/public/licenses/python-dependency-notices.txt +11951 -0
  71. package/scripts/terraform-render.mjs +8 -0
  72. package/server/__init__.py +0 -0
  73. package/server/abstraction.py +28 -0
  74. package/server/agent_runtime.py +153 -0
  75. package/server/app.py +257 -0
  76. package/server/architect.py +112 -0
  77. package/server/architecture_activity.py +67 -0
  78. package/server/attachments.py +101 -0
  79. package/server/cloud-emulation-requirements.txt +29 -0
  80. package/server/codex_app_server.py +219 -0
  81. package/server/codex_engineering_backend.py +266 -0
  82. package/server/codex_worker_policy.py +170 -0
  83. package/server/contracts.py +315 -0
  84. package/server/copilot_worker.py +16 -0
  85. package/server/dependency_mirror.py +54 -0
  86. package/server/design_patch.py +43 -0
  87. package/server/emulation-requirements.txt +8 -0
  88. package/server/emulation_adapters.py +20 -0
  89. package/server/emulation_routes.py +98 -0
  90. package/server/emulation_worker.py +351 -0
  91. package/server/engineering_artifacts.py +215 -0
  92. package/server/engineering_assets/application-runtime.json +4372 -0
  93. package/server/engineering_assets/architecture_work.schema.json +79 -0
  94. package/server/engineering_assets/materialize_runtime.py +62 -0
  95. package/server/engineering_assets/prepare_tools.py +96 -0
  96. package/server/engineering_assets/sql_validation.py +59 -0
  97. package/server/engineering_contexts.py +120 -0
  98. package/server/engineering_delivery.py +210 -0
  99. package/server/engineering_finalization.py +210 -0
  100. package/server/engineering_handoff.py +163 -0
  101. package/server/engineering_jobs.py +206 -0
  102. package/server/engineering_knowledge.py +75 -0
  103. package/server/engineering_reconciliation.py +159 -0
  104. package/server/engineering_routes.py +469 -0
  105. package/server/engineering_sessions.py +273 -0
  106. package/server/engineering_tools.py +166 -0
  107. package/server/generation_lint.py +18 -0
  108. package/server/local_providers.py +181 -0
  109. package/server/local_validation.py +236 -0
  110. package/server/memory-requirements.lock +181 -0
  111. package/server/memory-requirements.txt +6 -0
  112. package/server/memory_routes.py +82 -0
  113. package/server/memory_service.py +273 -0
  114. package/server/memory_worker.py +66 -0
  115. package/server/openai_voice.py +80 -0
  116. package/server/packaged_app.py +9 -0
  117. package/server/pricing.py +84 -0
  118. package/server/process_control_observation.py +31 -0
  119. package/server/prompts.py +123 -0
  120. package/server/public_faq_emulation_worker.py +44 -0
  121. package/server/requirements.lock +1618 -0
  122. package/server/requirements.txt +68 -0
  123. package/server/runtime_dependency_locks.py +27 -0
  124. package/server/runtime_evidence.py +93 -0
  125. package/server/settings.py +14 -0
  126. package/server/skills.py +103 -0
  127. package/server/storage_emulation_worker.py +160 -0
  128. package/server/structured_emulation_worker.py +48 -0
  129. package/server/terraform_routes.py +118 -0
  130. package/server/upstream/__init__.py +0 -0
  131. package/server/upstream/agent/__init__.py +1 -0
  132. package/server/upstream/agent/aws_architect.py +600 -0
  133. package/server/upstream/agent/config/__init__.py +1 -0
  134. package/server/upstream/agent/config/agent_config.py +82 -0
  135. package/server/upstream/agent/config/prompts.py +138 -0
  136. package/server/upstream/agent/rate_limiter.py +62 -0
  137. package/server/upstream/utils/__init__.py +1 -0
  138. package/server/upstream/utils/file_writer.py +43 -0
  139. package/server/validation_applicability.py +85 -0
  140. package/server/validation_jobs.py +252 -0
  141. package/server/validation_workers.py +48 -0
  142. package/server/voice_routes.py +91 -0
  143. package/server/voice_worker.py +26 -0
  144. package/specs/028-convergence-evaluation/evidence/20261110-ibm-invoice-native/registry-structured/qualification.json +72 -0
  145. package/specs/028-convergence-evaluation/evidence/20261110-invoice-runtime/durable-current/receipt.json +61 -0
  146. package/specs/029-general-purpose-engine/evidence/20261111-runtime-dependencies/sdk/receipt.json +22 -0
  147. package/src/ArchitectureActivity.jsx +54 -0
  148. package/src/ArchitectureWorkPanel.jsx +46 -0
  149. package/src/ArtifactChecks.jsx +49 -0
  150. package/src/BlockRequirements.jsx +13 -0
  151. package/src/BrainActivity.jsx +11 -0
  152. package/src/ChatMessage.jsx +19 -0
  153. package/src/ChatResizeHandle.jsx +6 -0
  154. package/src/CodePreview.jsx +8 -0
  155. package/src/ComponentReview.jsx +31 -0
  156. package/src/DeliveryViews.jsx +59 -0
  157. package/src/EmulationPanel.jsx +44 -0
  158. package/src/EngagementViews.jsx +385 -0
  159. package/src/EngineeringPanel.jsx +151 -0
  160. package/src/ImplementationDelivery.jsx +55 -0
  161. package/src/JourneyViews.jsx +400 -0
  162. package/src/MemoryPanel.jsx +46 -0
  163. package/src/OpenAIVoicePanel.jsx +65 -0
  164. package/src/OutputsView.jsx +65 -0
  165. package/src/TerraformView.jsx +57 -0
  166. package/src/WorkbenchDialogs.jsx +532 -0
  167. package/src/activity-presentation.mjs +28 -0
  168. package/src/ai-qualification-scenarios.mjs +31 -0
  169. package/src/approval-application-files.mjs +32 -0
  170. package/src/approval-applications.mjs +82 -0
  171. package/src/approval-spec-kit.mjs +30 -0
  172. package/src/architecture-activity.css +28 -0
  173. package/src/architecture-activity.mjs +63 -0
  174. package/src/architecture-work.mjs +176 -0
  175. package/src/artifact-work.mjs +103 -0
  176. package/src/async-documents.mjs +191 -0
  177. package/src/atflow.css +1140 -0
  178. package/src/atforma.css +748 -0
  179. package/src/atmem-tokens.css +79 -0
  180. package/src/aws-preflight.mjs +60 -0
  181. package/src/aws-public-faq.mjs +102 -0
  182. package/src/aws-validation-scenarios.mjs +10 -0
  183. package/src/aws-validation.mjs +48 -0
  184. package/src/azure-plan-mocks.mjs +25 -0
  185. package/src/azure-structured.mjs +133 -0
  186. package/src/bedrock-summary.mjs +152 -0
  187. package/src/bounded-rag.mjs +60 -0
  188. package/src/brand.css +9 -0
  189. package/src/canvas-view.mjs +28 -0
  190. package/src/capability-composition.mjs +44 -0
  191. package/src/chat-attachments.mjs +13 -0
  192. package/src/cloud-provider-locks.mjs +9 -0
  193. package/src/cloud-storage-scenarios.mjs +2 -0
  194. package/src/component-review.mjs +55 -0
  195. package/src/composition-spec-kit.mjs +30 -0
  196. package/src/consultation-context.mjs +15 -0
  197. package/src/consultation.mjs +52 -0
  198. package/src/container-contracts.mjs +5 -0
  199. package/src/container-inspection.mjs +75 -0
  200. package/src/container-runtime.mjs +30 -0
  201. package/src/container-security-inspection.mjs +49 -0
  202. package/src/container-terraform.mjs +109 -0
  203. package/src/conversation.mjs +17 -0
  204. package/src/core.mjs +863 -0
  205. package/src/cross-cloud-storage.mjs +100 -0
  206. package/src/delivery-capabilities.mjs +25 -0
  207. package/src/delivery.mjs +58 -0
  208. package/src/deployment-ownership.mjs +10 -0
  209. package/src/deployment-region.mjs +11 -0
  210. package/src/derive.mjs +31 -0
  211. package/src/design-build-flow.mjs +10 -0
  212. package/src/diagram-attention.mjs +24 -0
  213. package/src/discovery.mjs +359 -0
  214. package/src/document.mjs +170 -0
  215. package/src/emulation-feedback.mjs +61 -0
  216. package/src/emulation-guidance.mjs +37 -0
  217. package/src/engagement.mjs +274 -0
  218. package/src/engineering-client.mjs +73 -0
  219. package/src/engineering-defaults.mjs +17 -0
  220. package/src/engineering.css +31 -0
  221. package/src/enterprise-application.mjs +76 -0
  222. package/src/enterprise-aws-host-infrastructure.mjs +62 -0
  223. package/src/enterprise-aws-host-tests.mjs +15 -0
  224. package/src/enterprise-aws-infrastructure-tests.mjs +38 -0
  225. package/src/enterprise-aws-infrastructure.mjs +138 -0
  226. package/src/enterprise-google-infrastructure.mjs +84 -0
  227. package/src/enterprise-google-tests.mjs +25 -0
  228. package/src/estimate.mjs +188 -0
  229. package/src/existing-references.mjs +18 -0
  230. package/src/external-identities.mjs +17 -0
  231. package/src/faq-lambda-build.mjs +28 -0
  232. package/src/gcp-structured.mjs +104 -0
  233. package/src/generation-provider.mjs +7 -0
  234. package/src/generation-quality.mjs +69 -0
  235. package/src/handoff.mjs +141 -0
  236. package/src/ibm-registry.mjs +12 -0
  237. package/src/ibm-structured.mjs +103 -0
  238. package/src/identifiers.mjs +15 -0
  239. package/src/implementation-map.mjs +14 -0
  240. package/src/implementation-outcome.mjs +8 -0
  241. package/src/incomplete-terraform.mjs +9 -0
  242. package/src/integrations.ts +137 -0
  243. package/src/interface-contracts.mjs +30 -0
  244. package/src/invoice-application.mjs +66 -0
  245. package/src/invoice-aws-operations.mjs +136 -0
  246. package/src/invoice-aws.mjs +143 -0
  247. package/src/invoice-azure-operations.mjs +147 -0
  248. package/src/invoice-azure.mjs +134 -0
  249. package/src/invoice-builder.mjs +35 -0
  250. package/src/invoice-dependencies.mjs +43 -0
  251. package/src/invoice-gcp-operations.mjs +178 -0
  252. package/src/invoice-gcp.mjs +104 -0
  253. package/src/invoice-ibm-operations.mjs +278 -0
  254. package/src/invoice-ibm.mjs +142 -0
  255. package/src/invoice-operations.mjs +143 -0
  256. package/src/invoice-plan.mjs +45 -0
  257. package/src/invoice-region.mjs +6 -0
  258. package/src/invoice-spec-kit.mjs +61 -0
  259. package/src/invoice-state.mjs +7 -0
  260. package/src/invoice-terraform.mjs +29 -0
  261. package/src/invoice-tests.mjs +122 -0
  262. package/src/journey-workspace.css +72 -0
  263. package/src/journey.mjs +87 -0
  264. package/src/levels.mjs +145 -0
  265. package/src/local-agent.mjs +184 -0
  266. package/src/main.jsx +2458 -0
  267. package/src/model-policy-preflight.mjs +34 -0
  268. package/src/openai-voice.mjs +102 -0
  269. package/src/output-bundle.mjs +11 -0
  270. package/src/ownership-inspection.mjs +28 -0
  271. package/src/pack.mjs +201 -0
  272. package/src/pricing.mjs +31 -0
  273. package/src/production-state.mjs +8 -0
  274. package/src/project-copy.mjs +10 -0
  275. package/src/project-file.mjs +160 -0
  276. package/src/project-links.mjs +3 -0
  277. package/src/protected-state.mjs +43 -0
  278. package/src/public-faq-builder.mjs +19 -0
  279. package/src/public-faq-clouds.mjs +251 -0
  280. package/src/public-faq.mjs +122 -0
  281. package/src/pulumi.mjs +123 -0
  282. package/src/python-container.mjs +25 -0
  283. package/src/python-runtime.mjs +50 -0
  284. package/src/rag-handler.mjs +43 -0
  285. package/src/rag-web-assets.mjs +52 -0
  286. package/src/rag-web.mjs +68 -0
  287. package/src/records.mjs +177 -0
  288. package/src/resources.mjs +163 -0
  289. package/src/review.mjs +252 -0
  290. package/src/runtime/EnterpriseDocument.java +89 -0
  291. package/src/runtime/approved_http_destination.py +72 -0
  292. package/src/runtime/aws-lambda-qualification-lock.json +7 -0
  293. package/src/runtime/aws-python-lock.json +82 -0
  294. package/src/runtime/aws-web-python-lock.json +887 -0
  295. package/src/runtime/aws_json_model.py +42 -0
  296. package/src/runtime/azure-python-lock.json +863 -0
  297. package/src/runtime/azure-sources.json +8 -0
  298. package/src/runtime/azure-test-lock.json +23 -0
  299. package/src/runtime/azure_acceptance.py +192 -0
  300. package/src/runtime/azure_structured.py +153 -0
  301. package/src/runtime/bounded_rag.py +126 -0
  302. package/src/runtime/catalog_admin.py +70 -0
  303. package/src/runtime/cloud-sql-proxy-lock.json +8 -0
  304. package/src/runtime/cloud-sql-proxy-security-review.json +30 -0
  305. package/src/runtime/connector_cli.py +50 -0
  306. package/src/runtime/connector_sync.py +70 -0
  307. package/src/runtime/durable-python-lock.json +247 -0
  308. package/src/runtime/durable_workflow.py +325 -0
  309. package/src/runtime/enterprise-application-manifest.json +110 -0
  310. package/src/runtime/enterprise-application-sources.json +266 -0
  311. package/src/runtime/enterprise-auth-python-lock.json +592 -0
  312. package/src/runtime/enterprise-aws-host-bootstrap-source.json +1 -0
  313. package/src/runtime/enterprise-aws-host-probe-source.json +1 -0
  314. package/src/runtime/enterprise-aws-task-template.json +235 -0
  315. package/src/runtime/enterprise-document-lock.json +18 -0
  316. package/src/runtime/enterprise-document.Dockerfile +15 -0
  317. package/src/runtime/enterprise-runtime-aws-python-lock.json +1115 -0
  318. package/src/runtime/enterprise-runtime-azure-python-lock.json +1106 -0
  319. package/src/runtime/enterprise-runtime-gcp-python-lock.json +1060 -0
  320. package/src/runtime/enterprise-runtime-ibm-python-lock.json +1096 -0
  321. package/src/runtime/enterprise-runtime-python-lock.json +1058 -0
  322. package/src/runtime/enterprise-runtime.Dockerfile +15 -0
  323. package/src/runtime/enterprise-web-app.js +1 -0
  324. package/src/runtime/enterprise-web-entry.js +105 -0
  325. package/src/runtime/enterprise-web-lock.json +29 -0
  326. package/src/runtime/enterprise-web-notices.txt +229 -0
  327. package/src/runtime/enterprise-web.css +4 -0
  328. package/src/runtime/enterprise-web.html +8 -0
  329. package/src/runtime/enterprise_audit.py +91 -0
  330. package/src/runtime/enterprise_audit_schema.sql +25 -0
  331. package/src/runtime/enterprise_auth_schema.sql +67 -0
  332. package/src/runtime/enterprise_auth_store.py +241 -0
  333. package/src/runtime/enterprise_aws_host_bootstrap.py +120 -0
  334. package/src/runtime/enterprise_aws_host_probe.py +105 -0
  335. package/src/runtime/enterprise_aws_hosts.py +136 -0
  336. package/src/runtime/enterprise_aws_jobs.py +300 -0
  337. package/src/runtime/enterprise_aws_objects.py +93 -0
  338. package/src/runtime/enterprise_aws_transfer.py +83 -0
  339. package/src/runtime/enterprise_azure_batch_api.py +128 -0
  340. package/src/runtime/enterprise_azure_host.py +599 -0
  341. package/src/runtime/enterprise_azure_jobs.py +294 -0
  342. package/src/runtime/enterprise_azure_objects.py +234 -0
  343. package/src/runtime/enterprise_azure_pool.py +161 -0
  344. package/src/runtime/enterprise_azure_transfer.py +122 -0
  345. package/src/runtime/enterprise_configuration.py +136 -0
  346. package/src/runtime/enterprise_document.py +116 -0
  347. package/src/runtime/enterprise_document_cli.py +32 -0
  348. package/src/runtime/enterprise_document_contract.py +54 -0
  349. package/src/runtime/enterprise_document_job.py +109 -0
  350. package/src/runtime/enterprise_document_jobs.py +122 -0
  351. package/src/runtime/enterprise_document_jobs_schema.sql +23 -0
  352. package/src/runtime/enterprise_extraction_cache.py +186 -0
  353. package/src/runtime/enterprise_extraction_schema.sql +17 -0
  354. package/src/runtime/enterprise_google_jobs.py +275 -0
  355. package/src/runtime/enterprise_google_transfer.py +85 -0
  356. package/src/runtime/enterprise_http.py +212 -0
  357. package/src/runtime/enterprise_identity.py +52 -0
  358. package/src/runtime/enterprise_knowledge.py +152 -0
  359. package/src/runtime/enterprise_models.py +94 -0
  360. package/src/runtime/enterprise_native.py +181 -0
  361. package/src/runtime/enterprise_oauth.py +132 -0
  362. package/src/runtime/enterprise_operator.py +160 -0
  363. package/src/runtime/enterprise_parser_service.py +136 -0
  364. package/src/runtime/enterprise_parser_transport.py +68 -0
  365. package/src/runtime/enterprise_queries.py +255 -0
  366. package/src/runtime/enterprise_queries_schema.sql +27 -0
  367. package/src/runtime/enterprise_query_service.py +111 -0
  368. package/src/runtime/enterprise_secrets.py +110 -0
  369. package/src/runtime/enterprise_sessions.py +95 -0
  370. package/src/runtime/enterprise_sources.py +260 -0
  371. package/src/runtime/enterprise_web.py +39 -0
  372. package/src/runtime/enterprise_worker.py +68 -0
  373. package/src/runtime/export_contract_acceptance.py +18 -0
  374. package/src/runtime/faq-output-contract.json +40 -0
  375. package/src/runtime/faq-web-app.js +41 -0
  376. package/src/runtime/faq-web.css +1 -0
  377. package/src/runtime/faq-web.html +6 -0
  378. package/src/runtime/gcp-sources.json +9 -0
  379. package/src/runtime/gcp_acceptance.py +110 -0
  380. package/src/runtime/gcp_structured.py +150 -0
  381. package/src/runtime/http_acceptance.py +39 -0
  382. package/src/runtime/iap-web-app.js +36 -0
  383. package/src/runtime/iap-web-source.json +1 -0
  384. package/src/runtime/ibm-python-lock.json +856 -0
  385. package/src/runtime/ibm-sources.json +9 -0
  386. package/src/runtime/ibm_acceptance.py +133 -0
  387. package/src/runtime/ibm_structured.py +113 -0
  388. package/src/runtime/invoice-azapi-provider-lock.json +7 -0
  389. package/src/runtime/invoice-ibm-provider-lock.json +7 -0
  390. package/src/runtime/invoice-parser-lock.json +1199 -0
  391. package/src/runtime/invoice-parser-python-lock.json +56 -0
  392. package/src/runtime/invoice-parser.Dockerfile +190 -0
  393. package/src/runtime/invoice-sources.json +58 -0
  394. package/src/runtime/invoice-test-sources.json +24 -0
  395. package/src/runtime/invoice-web.css +2 -0
  396. package/src/runtime/invoice-web.html +12 -0
  397. package/src/runtime/invoice-web.js +112 -0
  398. package/src/runtime/invoice_configuration.py +17 -0
  399. package/src/runtime/invoice_document.py +95 -0
  400. package/src/runtime/invoice_extraction.py +96 -0
  401. package/src/runtime/invoice_http.py +129 -0
  402. package/src/runtime/invoice_identity.py +5 -0
  403. package/src/runtime/invoice_maintenance.py +40 -0
  404. package/src/runtime/invoice_native.py +77 -0
  405. package/src/runtime/invoice_object_store.py +153 -0
  406. package/src/runtime/invoice_operations.py +108 -0
  407. package/src/runtime/invoice_operator.py +79 -0
  408. package/src/runtime/invoice_operator_core.py +39 -0
  409. package/src/runtime/invoice_operator_job.py +147 -0
  410. package/src/runtime/invoice_parser_cli.py +17 -0
  411. package/src/runtime/invoice_parser_identity.py +39 -0
  412. package/src/runtime/invoice_parser_service.py +89 -0
  413. package/src/runtime/invoice_parser_transport.py +89 -0
  414. package/src/runtime/invoice_processing.py +24 -0
  415. package/src/runtime/invoice_retention.py +16 -0
  416. package/src/runtime/invoice_runtime_secrets.py +43 -0
  417. package/src/runtime/invoice_stage.py +80 -0
  418. package/src/runtime/invoice_web.py +39 -0
  419. package/src/runtime/invoice_worker.py +50 -0
  420. package/src/runtime/lambda_faq.py +5 -0
  421. package/src/runtime/parser-requirements.lock +4 -0
  422. package/src/runtime/policy_review.py +18 -0
  423. package/src/runtime/postgres-qualification-lock.json +21 -0
  424. package/src/runtime/public-faq-sources.json +22 -0
  425. package/src/runtime/public_catalog_admin.py +122 -0
  426. package/src/runtime/public_catalog_store.py +125 -0
  427. package/src/runtime/public_faq.py +144 -0
  428. package/src/runtime/public_faq_app.py +49 -0
  429. package/src/runtime/python-container-lock.json +18 -0
  430. package/src/runtime/rag-sources.json +10 -0
  431. package/src/runtime/rag_acceptance.py +60 -0
  432. package/src/runtime/rag_aws_adapter.py +95 -0
  433. package/src/runtime/rds-ca-source.json +9 -0
  434. package/src/runtime/rds-global-ca.pem +2888 -0
  435. package/src/runtime/state-contract-check-source.json +4 -0
  436. package/src/runtime/state_contract_check.py +338 -0
  437. package/src/runtime/structured-source.json +1 -0
  438. package/src/runtime/structured-web-app.js +71 -0
  439. package/src/runtime/structured-web-source.json +1 -0
  440. package/src/runtime/structured_http.py +105 -0
  441. package/src/runtime/structured_output.py +48 -0
  442. package/src/runtime/web-python-lock.json +802 -0
  443. package/src/runtime/web_user_admin.py +44 -0
  444. package/src/runtime/workflow_application.py +62 -0
  445. package/src/runtime/workflow_bindings.py +128 -0
  446. package/src/runtime/workflow_configuration.py +40 -0
  447. package/src/runtime/workflow_connection.py +105 -0
  448. package/src/runtime/workflow_contract.py +54 -0
  449. package/src/runtime/workflow_database.py +94 -0
  450. package/src/runtime/workflow_errors.py +6 -0
  451. package/src/runtime/workflow_http.py +178 -0
  452. package/src/runtime/workflow_identity.py +69 -0
  453. package/src/runtime/workflow_payload.py +118 -0
  454. package/src/runtime/workflow_process_stage.py +88 -0
  455. package/src/runtime/workflow_profile.py +91 -0
  456. package/src/runtime/workflow_review.py +106 -0
  457. package/src/runtime/workflow_role_bootstrap.py +104 -0
  458. package/src/runtime/workflow_schema.sql +72 -0
  459. package/src/runtime/workflow_worker.py +142 -0
  460. package/src/runtime-settings.mjs +15 -0
  461. package/src/s3-access.mjs +14 -0
  462. package/src/source-contracts.mjs +17 -0
  463. package/src/spec-kit.mjs +186 -0
  464. package/src/state-custody.mjs +32 -0
  465. package/src/state-inspection.mjs +45 -0
  466. package/src/state-spec-kit.mjs +13 -0
  467. package/src/storage-tests.mjs +25 -0
  468. package/src/structured-api.mjs +133 -0
  469. package/src/structured-contract.mjs +19 -0
  470. package/src/structured-operation-policy.mjs +2 -0
  471. package/src/structured-web.mjs +17 -0
  472. package/src/style.css +1 -0
  473. package/src/system-description.mjs +26 -0
  474. package/src/terraform-admission.mjs +66 -0
  475. package/src/terraform-composition.mjs +83 -0
  476. package/src/terraform-expressions.mjs +46 -0
  477. package/src/terraform-validation-coverage.mjs +11 -0
  478. package/src/terraform.mjs +128 -0
  479. package/src/text-statistics.mjs +51 -0
  480. package/src/upstream/openarchflow/format-payload.ts +26 -0
  481. package/src/upstream/openarchflow/import/mermaid.ts +205 -0
  482. package/src/upstream/openarchflow/import/sql-ddl.ts +109 -0
  483. package/src/upstream/openarchflow/layout.ts +54 -0
  484. package/src/upstream/openarchflow/providers/aws.ts +196 -0
  485. package/src/upstream/openarchflow/providers/azure.ts +126 -0
  486. package/src/upstream/openarchflow/providers/cloudnative.ts +71 -0
  487. package/src/upstream/openarchflow/providers/generic.ts +227 -0
  488. package/src/upstream/openarchflow/providers/integration.ts +22 -0
  489. package/src/upstream/openarchflow/providers/types.ts +2 -0
  490. package/src/upstream/openarchflow/templates.ts +937 -0
  491. package/src/upstream-catalog.mjs +10 -0
  492. package/src/useChatScroll.jsx +36 -0
  493. package/src/useEmulation.jsx +36 -0
  494. package/src/validation-receipt.mjs +10 -0
  495. package/src/visual-export.mjs +92 -0
  496. package/src/voice-conversation.mjs +117 -0
  497. package/src/work-intake.mjs +24 -0
  498. package/src/workflow-runtime-inventory.mjs +3 -0
@@ -0,0 +1,294 @@
1
+ """Owned Azure Batch document request contract, not an admitted native executor.
2
+
3
+ Host preparation/release helpers, private pool/IAM provisioning, durable recovery
4
+ and actual node-agent checks must be composed before enabling this provider in
5
+ enterprise_worker. Schema-valid requests alone do not prove a running sandbox.
6
+ """
7
+ import hashlib
8
+ import copy
9
+ import json
10
+ import re
11
+ import shlex
12
+
13
+ API_VERSION = '2026-10-01'
14
+ FORMAT = 'enterprise-azure-document-job-v1'
15
+ _UUID = r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
16
+ _HASH = r'[0-9a-f]{64}'
17
+
18
+
19
+ def configuration(value):
20
+ fields = {'format', 'subscriptionId', 'region', 'resourceGroup', 'batchAccount',
21
+ 'poolId', 'storageAccount', 'container', 'workerClientId',
22
+ 'transferClientId', 'parserImage', 'transferImage', 'hostHelperSha256'}
23
+ if not isinstance(value, dict) or set(value) != fields or value['format'] != FORMAT:
24
+ raise ValueError('Use the exact owned Azure document-job configuration')
25
+ patterns = {'subscriptionId': _UUID, 'region': r'[a-z][a-z0-9]{1,30}',
26
+ 'resourceGroup': r'[A-Za-z0-9_()-](?:[A-Za-z0-9_().-]{0,88}[A-Za-z0-9_()-])?',
27
+ 'batchAccount': r'[a-z0-9]{3,24}', 'poolId': r'document-[a-z0-9-]{1,54}',
28
+ 'storageAccount': r'[a-z0-9]{3,24}',
29
+ 'container': r'[a-z0-9]+(?:-[a-z0-9]+)*',
30
+ 'workerClientId': _UUID, 'transferClientId': _UUID,
31
+ 'hostHelperSha256': _HASH}
32
+ for name, pattern in patterns.items():
33
+ if not isinstance(value[name], str) or not re.fullmatch(pattern, value[name]):
34
+ raise ValueError('Invalid owned Azure job destination or identity')
35
+ if not 3 <= len(value['container']) <= 63:
36
+ raise ValueError('Use the owned temporary container')
37
+ if value['workerClientId'] == value['transferClientId']:
38
+ raise ValueError('Transfer hosts must not inherit the question worker identity')
39
+ registry = None
40
+ for name in ['parserImage', 'transferImage']:
41
+ if not isinstance(value[name], str) or not re.fullmatch(
42
+ r'[a-z0-9]{5,50}\.azurecr\.io/[a-z0-9]+(?:[._/-][a-z0-9]+)*@sha256:' + _HASH, value[name]):
43
+ raise ValueError('Use immutable owned Azure Container Registry artifacts')
44
+ current = value[name].split('/', 1)[0]
45
+ if registry is not None and registry != current:
46
+ raise ValueError('Use the same owned artifact registry')
47
+ registry = current
48
+ if value['parserImage'] == value['transferImage']:
49
+ raise ValueError('Keep parser and trusted transfer artifacts separate')
50
+ # Registry/account names alone cannot prove subscription or regional ownership.
51
+ # The future native preflight must check ARM identity/location, not infer them.
52
+ return dict(value)
53
+
54
+
55
+ def identity(value):
56
+ if not isinstance(value, str) or not re.fullmatch(_HASH, value):
57
+ raise ValueError('Use the exact opaque per-file identity')
58
+ return value
59
+
60
+
61
+ def transfer_metadata(config, metadata, etag):
62
+ owned = configuration(config)
63
+ if (not isinstance(metadata, dict) or set(metadata) != {'format', 'requestId', 'sha256', 'mediaType'}
64
+ or metadata['format'] != 'enterprise-document-job-v1'
65
+ or not isinstance(metadata['sha256'], str) or not re.fullmatch(_HASH, metadata['sha256'])):
66
+ raise ValueError('Use the exact byte-bound document request')
67
+ identity(metadata['requestId'])
68
+ from enterprise_document_contract import MEDIA_TYPES, media_type
69
+ if (media_type(metadata['mediaType']) != metadata['mediaType'] or
70
+ metadata['mediaType'] not in MEDIA_TYPES | {'application/octet-stream'}):
71
+ raise ValueError('Use a supported canonical document MIME')
72
+ # Azure Blob ETags are conditional tokens; never use one as a content digest.
73
+ if not isinstance(etag, str) or not re.fullmatch(r'"0x[0-9A-Fa-f]{1,32}"', etag):
74
+ raise ValueError('Use the acknowledged native Azure input ETag')
75
+ return {'format': 'enterprise-azure-document-transfer-v1',
76
+ 'subscriptionId': owned['subscriptionId'], 'region': owned['region'],
77
+ 'resourceGroup': owned['resourceGroup'], 'storageAccount': owned['storageAccount'],
78
+ 'container': owned['container'], 'transferClientId': owned['transferClientId'],
79
+ 'request': dict(metadata), 'inputETag': etag}
80
+
81
+
82
+ def volume_names(identifier):
83
+ # IDs are 64 hex characters, also satisfying Batch's maximum job ID length.
84
+ token = identity(identifier)
85
+ return ('atmem-document-' + token + '-input', 'atmem-document-' + token + '-output')
86
+
87
+
88
+ def _host_command(owned, mode):
89
+ if mode not in {'prepare', 'release'}:
90
+ raise ValueError('Use the fixed trusted host operation')
91
+ digest = owned['hostHelperSha256']
92
+ path = '/opt/atmem/document-host/' + digest + '/enterprise_azure_host.py'
93
+ # No shell/path expansion, application-package default version, URL fetching,
94
+ # inherited PYTHONPATH or unverified host script. The root-owned helper tree
95
+ # and its parents are separately required by pool provisioning/preflight.
96
+ code = (
97
+ 'import os,stat,hashlib,sys;'
98
+ 'f=os.open(' + repr(path) + ',os.O_RDONLY|os.O_NOFOLLOW);'
99
+ 's=os.fstat(f);'
100
+ 'assert stat.S_ISREG(s.st_mode) and s.st_uid==0 and not s.st_mode&0o022 and s.st_size<=262144;'
101
+ 'b=os.read(f,262145);os.close(f);'
102
+ 'assert len(b)==s.st_size and hashlib.sha256(b).hexdigest()==' + repr(digest) + ';'
103
+ 'sys.argv=[' + repr(path) + ',' + repr(mode) + '];'
104
+ 'exec(compile(b,' + repr(path) + ',"exec"),{"__name__":"__main__","__file__":' + repr(path) + '})'
105
+ )
106
+ return '/usr/bin/python3 -I -c ' + shlex.quote(code)
107
+
108
+
109
+ def job_spec(config, metadata, etag):
110
+ owned = configuration(config)
111
+ transfer = transfer_metadata(owned, metadata, etag)
112
+ encoded = json.dumps(transfer, separators=(',', ':'), allow_nan=False)
113
+ # Never use commonEnvironmentSettings: those are inherited by the parser.
114
+ environment = [{'name': 'DOCUMENT_JOB', 'value': encoded},
115
+ {'name': 'DOCUMENT_TRANSFER_IMAGE', 'value': owned['transferImage']}]
116
+ admin = {'autoUser': {'scope': 'task', 'elevationLevel': 'admin'}}
117
+ return {'id': metadata['requestId'], 'poolInfo': {'poolId': owned['poolId']},
118
+ 'constraints': {'maxWallClockTime': 'PT8M', 'maxTaskRetryCount': 0},
119
+ # Keep active until its single task is observed. terminatejob here
120
+ # would complete an empty job before the task can be submitted.
121
+ 'onAllTasksComplete': 'noaction',
122
+ 'jobPreparationTask': {'id': 'prepare', 'commandLine': _host_command(owned, 'prepare'),
123
+ 'waitForSuccess': True, 'rerunOnNodeRebootAfterSuccess': True,
124
+ 'userIdentity': admin, 'environmentSettings': environment,
125
+ 'constraints': {'maxWallClockTime': 'PT1M', 'maxTaskRetryCount': 0, 'retentionTime': 'PT0S'}},
126
+ 'jobReleaseTask': {'id': 'release', 'commandLine': _host_command(owned, 'release'),
127
+ 'userIdentity': {'autoUser': dict(admin['autoUser'])},
128
+ 'environmentSettings': [dict(item) for item in environment],
129
+ 'maxWallClockTime': 'PT1M', 'retentionTime': 'PT0S'},
130
+ 'metadata': [{'name': 'atmem-owner', 'value': fingerprint(owned)},
131
+ {'name': 'atmem-parser-image', 'value': owned['parserImage']},
132
+ {'name': 'atmem-helper-sha256', 'value': owned['hostHelperSha256']},
133
+ {'name': 'atmem-worker-client', 'value': owned['workerClientId']}]}
134
+
135
+
136
+ def task_spec(config, identifier):
137
+ owned = configuration(config)
138
+ source, target = volume_names(identifier)
139
+ options = ['--rm', '--network=none', '--read-only', '--cap-drop=ALL',
140
+ '--security-opt=no-new-privileges:true', '--pids-limit=128',
141
+ '--memory=2g', '--memory-swap=2g', '--cpus=2', '--log-driver=none',
142
+ '--label=atmem.document.owner=' + cleanup_owner(owned),
143
+ '--label=atmem.document.request=' + identity(identifier),
144
+ '--label=atmem.document.policy=' + fingerprint(owned),
145
+ '--label=atmem.document.stage=parser',
146
+ '--tmpfs=/tmp:rw,noexec,nosuid,nodev,size=128m',
147
+ '--mount=type=volume,source=' + source + ',target=/input,readonly',
148
+ '--mount=type=volume,source=' + target + ',target=/output']
149
+ return {'id': 'document', 'commandLine': 'python -I /app/enterprise_document_cli.py --job',
150
+ 'containerSettings': {'imageName': owned['parserImage'],
151
+ 'containerRunOptions': ' '.join(options),
152
+ 'containerHostBatchBindMounts': [], 'workingDirectory': 'containerImageDefault'},
153
+ # Batch maps a native non-admin task UID. Do not assume the image's
154
+ # 10001 UID or give the untrusted parser admin to bypass permissions.
155
+ 'userIdentity': {'autoUser': {'scope': 'task', 'elevationLevel': 'nonadmin'}},
156
+ 'constraints': {'maxWallClockTime': 'PT2M', 'maxTaskRetryCount': 0, 'retentionTime': 'PT0S'}}
157
+
158
+
159
+ def fingerprint(config):
160
+ return hashlib.sha256(json.dumps(configuration(config), sort_keys=True, separators=(',', ':')).encode()).hexdigest()
161
+
162
+
163
+ def cleanup_owner(config):
164
+ checked = configuration(config)
165
+ keys = ['subscriptionId', 'region', 'resourceGroup', 'batchAccount', 'poolId',
166
+ 'storageAccount', 'container', 'transferClientId']
167
+ return hashlib.sha256(json.dumps({**{key: checked[key] for key in keys},
168
+ 'registry': checked['parserImage'].split('/')[0]}, sort_keys=True, separators=(',', ':')).encode()).hexdigest()
169
+
170
+
171
+ def verify_job(config, metadata, etag, reply):
172
+ """Reject unowned/replaced jobs before adopting them during recovery."""
173
+ expected = job_spec(config, metadata, etag)
174
+ if not isinstance(reply, dict) or not typed_equal(job_observation(expected), job_observation({key: reply.get(key) for key in expected})):
175
+ raise ValueError('Azure document job ownership or execution contract changed')
176
+ if any(key in reply for key in ['jobManagerTask', 'commonEnvironmentSettings', 'networkConfiguration']):
177
+ raise ValueError('Unexpected Azure document job authority')
178
+ return True
179
+
180
+
181
+ def verify_task(config, identifier, reply):
182
+ expected = task_spec(config, identifier)
183
+ if not isinstance(reply, dict) or any(not typed_equal(value, reply.get(key)) for key, value in expected.items()):
184
+ raise ValueError('Azure document parser isolation contract changed')
185
+ if any(key in reply for key in ['environmentSettings', 'resourceFiles', 'outputFiles',
186
+ 'applicationPackageReferences', 'multiInstanceSettings', 'dependsOn']):
187
+ raise ValueError('Unexpected Azure parser input or authority')
188
+ return True
189
+
190
+
191
+ def typed_equal(expected, observed):
192
+ if type(expected) is not type(observed): return False
193
+ if isinstance(expected, dict):
194
+ return set(expected) == set(observed) and all(typed_equal(value, observed[key]) for key, value in expected.items())
195
+ if isinstance(expected, list):
196
+ return len(expected) == len(observed) and all(typed_equal(left, right) for left, right in zip(expected, observed))
197
+ return expected == observed
198
+
199
+
200
+ def job_observation(value):
201
+ """Only documented name/value arrays are unordered; no fields are dropped."""
202
+ value = copy.deepcopy(value)
203
+ arrays = [value.get('metadata')]
204
+ for key in ['jobPreparationTask', 'jobReleaseTask']:
205
+ task = value.get(key)
206
+ if isinstance(task, dict): arrays.append(task.get('environmentSettings'))
207
+ for items in arrays:
208
+ if not isinstance(items, list): raise ValueError('Use exact native name/value settings')
209
+ names = set()
210
+ for item in items:
211
+ if (not isinstance(item, dict) or set(item) != {'name', 'value'} or
212
+ not isinstance(item['name'], str) or not isinstance(item['value'], str) or item['name'] in names):
213
+ raise ValueError('Ambiguous native name/value settings')
214
+ names.add(item['name'])
215
+ items.sort(key=lambda entry: entry['name'])
216
+ return value
217
+
218
+
219
+ def recover_job(config, identifier, reply):
220
+ """Recover only an exact owned historical job, without executing its text.
221
+
222
+ The SQL ledger stores opaque IDs, not source metadata or arbitrary cleanup
223
+ endpoints. The native job supplies its bounded original byte/ETag/artifact
224
+ binding; the stable destination and full immutable shape are checked here.
225
+ This result is for observation/cleanup, never permission to launch old code.
226
+ """
227
+ current = configuration(config)
228
+ identity(identifier)
229
+ if not isinstance(reply, dict) or reply.get('id') != identifier:
230
+ raise ValueError('Native job identity changed')
231
+ def settings(value, names):
232
+ if not isinstance(value, list) or len(value) != len(names):
233
+ raise ValueError('Use exact original job metadata')
234
+ result = {}
235
+ for entry in value:
236
+ if (not isinstance(entry, dict) or set(entry) != {'name', 'value'} or
237
+ not isinstance(entry['name'], str) or entry['name'] not in names or entry['name'] in result or not isinstance(entry['value'], str)):
238
+ raise ValueError('Ambiguous original job metadata')
239
+ result[entry['name']] = entry['value']
240
+ return result
241
+ metadata = settings(reply.get('metadata'), {'atmem-owner', 'atmem-parser-image',
242
+ 'atmem-helper-sha256', 'atmem-worker-client'})
243
+ prepare = reply.get('jobPreparationTask')
244
+ if not isinstance(prepare, dict): raise ValueError('Original preparation contract is required')
245
+ environment = settings(prepare.get('environmentSettings'), {'DOCUMENT_JOB', 'DOCUMENT_TRANSFER_IMAGE'})
246
+ historical = configuration({**current, 'parserImage': metadata['atmem-parser-image'],
247
+ 'transferImage': environment['DOCUMENT_TRANSFER_IMAGE'], 'hostHelperSha256': metadata['atmem-helper-sha256'],
248
+ 'workerClientId': metadata['atmem-worker-client']})
249
+ if cleanup_owner(historical) != cleanup_owner(current):
250
+ raise ValueError('Do not adopt a different cleanup destination or artifact registry')
251
+ if not 0 < len(environment['DOCUMENT_JOB'].encode()) <= 4096:
252
+ raise ValueError('Original transfer metadata exceeds the bound')
253
+ from enterprise_document_job import unique
254
+ value = json.loads(environment['DOCUMENT_JOB'], object_pairs_hook=unique)
255
+ if not isinstance(value, dict) or not isinstance(value.get('request'), dict) or value['request'].get('requestId') != identifier:
256
+ raise ValueError('Original file identity changed')
257
+ expected = transfer_metadata(historical, value['request'], value.get('inputETag'))
258
+ if value != expected:
259
+ raise ValueError('Original transfer destination changed')
260
+ verify_job(historical, expected['request'], expected['inputETag'], reply)
261
+ return historical, expected
262
+
263
+
264
+ def terminal_writer_nodes(config, identifier, job, task, status):
265
+ """Seal the complete native preparation-node set only after job completion.
266
+
267
+ A preflight snapshot or one task's current node omits earlier retry nodes.
268
+ This function observes provider metadata; it does not certify local cleanup
269
+ or erase storage. Every returned node still requires its own acknowledgement.
270
+ """
271
+ historical, transfer = recover_job(config, identifier, job)
272
+ if job.get('state') != 'completed': raise ValueError('Native work has not completed termination/release')
273
+ if (not isinstance(status, dict) or status.get('odata.nextLink') or status.get('@odata.nextLink') or
274
+ not isinstance(status.get('value'), list) or len(status['value']) > 100):
275
+ raise ValueError('Observe the complete native preparation/release history')
276
+ nodes = set()
277
+ for row in status['value']:
278
+ if (not isinstance(row, dict) or row.get('poolId') != historical['poolId'] or
279
+ not isinstance(row.get('nodeId'), str) or not re.fullmatch(r'[A-Za-z0-9_-]{1,128}', row['nodeId']) or
280
+ row['nodeId'] in nodes or not isinstance(row.get('jobPreparationTaskExecutionInfo'), dict) or
281
+ row['jobPreparationTaskExecutionInfo'].get('state') != 'completed'):
282
+ raise ValueError('Native preparation history is incomplete or foreign')
283
+ release = row.get('jobReleaseTaskExecutionInfo')
284
+ if release is not None and (not isinstance(release, dict) or release.get('state') != 'completed'):
285
+ raise ValueError('Native release is still active or unknown')
286
+ nodes.add(row['nodeId'])
287
+ if task is not None:
288
+ verify_task(historical, identifier, task)
289
+ if task.get('state') != 'completed': raise ValueError('Native parser task is not terminal')
290
+ assigned = task.get('nodeInfo')
291
+ if assigned is not None and (not isinstance(assigned, dict) or assigned.get('poolId') != historical['poolId'] or
292
+ assigned.get('nodeId') not in nodes):
293
+ raise ValueError('Native task assignment is missing from complete preparation history')
294
+ return historical, transfer, frozenset(nodes)
@@ -0,0 +1,234 @@
1
+ """Bounded, native-MI Azure temporary blobs; never a source URL connector.
2
+
3
+ ARM preflight is observational, not an administrative lock. Provisioning must
4
+ restrict changes to this dedicated no-history account/container. Lost nodes and
5
+ cloud erasure require separate controller/recovery evidence.
6
+ """
7
+ import base64
8
+ from datetime import datetime, timezone
9
+ from email.utils import format_datetime
10
+ import hashlib
11
+ import json
12
+ import re
13
+ import time
14
+ from urllib.parse import quote
15
+ import requests
16
+ from enterprise_document_contract import MAX_INPUT
17
+ from enterprise_document_job import MAX_ENVELOPE, unique
18
+ from enterprise_sources import SourceUnavailable
19
+
20
+ ARM_VERSION = '2025-06-01'
21
+ BLOB_VERSION = '2025-05-05'
22
+ _UUID = r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
23
+
24
+
25
+ def object_key(identifier, side, node_id=None):
26
+ if not isinstance(identifier, str) or not re.fullmatch('[0-9a-f]{64}', identifier) or side not in {'input', 'result', 'cleanup'}:
27
+ raise ValueError('Use the owned per-file object identity')
28
+ if side == 'cleanup':
29
+ if not isinstance(node_id, str) or not re.fullmatch(r'[A-Za-z0-9_-]{1,128}', node_id):
30
+ raise ValueError('Use the exact native cleanup node identity')
31
+ return 'document-jobs/' + identifier + '/cleanup/' + node_id
32
+ if node_id is not None: raise ValueError('Only cleanup receipts have a node identity')
33
+ return 'document-jobs/' + identifier + '/' + side
34
+
35
+
36
+ def etag(value):
37
+ if not isinstance(value, str) or not re.fullmatch(r'"0x[0-9A-Fa-f]{1,32}"', value):
38
+ raise SourceUnavailable('document_object_unavailable')
39
+ return value
40
+
41
+
42
+ class AzureDocumentObjects:
43
+ def __init__(self, *, subscription, resource_group, region, account, container,
44
+ client_id, session=None, credential=None):
45
+ for value, pattern in [(subscription, _UUID), (client_id, _UUID),
46
+ (resource_group, r'[A-Za-z0-9_()-](?:[A-Za-z0-9_().-]{0,88}[A-Za-z0-9_()-])?'),
47
+ (region, r'[a-z][a-z0-9]{1,30}'), (account, r'[a-z0-9]{3,24}'),
48
+ (container, r'[a-z0-9]+(?:-[a-z0-9]+)*')]:
49
+ if not isinstance(value, str) or not re.fullmatch(pattern, value):
50
+ raise ValueError('Use the owned Azure temporary object destination')
51
+ if not 3 <= len(container) <= 63:
52
+ raise ValueError('Use the owned Azure temporary container')
53
+ self.region, self.account, self.container, self.client_id = region, account, container, client_id
54
+ self.account_id = ('/subscriptions/' + subscription + '/resourceGroups/' + resource_group +
55
+ '/providers/Microsoft.Storage/storageAccounts/' + account)
56
+ self.service_id = self.account_id + '/blobServices/default'
57
+ self.container_id = self.service_id + '/containers/' + container
58
+ self.base = 'https://' + account + '.blob.core.windows.net/' + container + '/'
59
+ self.session = session or requests.Session()
60
+ self.session.trust_env = False
61
+ self.credential = credential
62
+
63
+ def _credential(self):
64
+ if self.credential is None:
65
+ from azure.identity import ManagedIdentityCredential
66
+ self.credential = ManagedIdentityCredential(client_id=self.client_id, retry_total=0,
67
+ connection_timeout=1, read_timeout=2)
68
+ return self.credential
69
+
70
+ def _send(self, method, url, *, arm=False, headers=None, raw=None, limit=32768, missing=False):
71
+ # The private entry point still enforces destination ownership. Never
72
+ # follow redirects or use developer proxy/netrc credentials.
73
+ if method not in {'GET', 'PUT', 'DELETE'}:
74
+ raise ValueError('Use the fixed owned object operations')
75
+ if arm:
76
+ if method != 'GET' or url not in {'https://management.azure.com' + value for value in
77
+ [self.account_id, self.service_id, self.container_id]}:
78
+ raise ValueError('Use only the fixed owned ARM observations')
79
+ elif not url.startswith(self.base + 'document-jobs/') or not re.fullmatch(
80
+ re.escape(self.base) + r'document-jobs/[0-9a-f]{64}/(?:input|result|cleanup/[A-Za-z0-9_-]{1,128})', url):
81
+ raise ValueError('Use only the fixed owned blob object')
82
+ try:
83
+ scope = 'https://management.azure.com/.default' if arm else 'https://storage.azure.com/.default'
84
+ token = self._credential().get_token(scope).token
85
+ if not isinstance(token, str) or not 8 <= len(token) <= 20000 or any(ord(c) < 33 or ord(c) > 126 for c in token):
86
+ raise ValueError()
87
+ request_headers = {'Authorization': 'Bearer ' + token, 'Accept-Encoding': 'identity'}
88
+ if not arm:
89
+ request_headers.update({'x-ms-version': BLOB_VERSION,
90
+ 'x-ms-date': format_datetime(datetime.now(timezone.utc), usegmt=True)})
91
+ request_headers.update(headers or {})
92
+ started = time.monotonic()
93
+ with self.session.request(method, url, headers=request_headers, data=raw,
94
+ params={'api-version': ARM_VERSION} if arm else None,
95
+ stream=True, allow_redirects=False, timeout=(2, 8)) as response:
96
+ if missing and response.status_code == 404:
97
+ return None
98
+ expected = {'GET': {200}, 'PUT': {201}, 'DELETE': {202}}
99
+ if response.status_code not in expected[method]:
100
+ raise ValueError()
101
+ received = {key.lower(): value for key, value in response.headers.items()}
102
+ if received.get('content-encoding') not in {None, 'identity'}:
103
+ raise ValueError()
104
+ data = bytearray()
105
+ for chunk in response.iter_content(16384):
106
+ data.extend(chunk)
107
+ if len(data) > limit or time.monotonic() - started > 15:
108
+ raise ValueError()
109
+ return bytes(data), received
110
+ except Exception:
111
+ raise SourceUnavailable('document_object_unavailable') from None
112
+
113
+ def _arm(self, identifier):
114
+ raw, _ = self._send('GET', 'https://management.azure.com' + identifier, arm=True, limit=262144)
115
+ try:
116
+ value = json.loads(raw, object_pairs_hook=unique)
117
+ if not isinstance(value, dict) or not isinstance(value.get('id'), str) or value['id'].lower() != identifier.lower():
118
+ raise ValueError()
119
+ return value
120
+ except Exception:
121
+ raise SourceUnavailable('document_object_policy_unavailable') from None
122
+
123
+ def preflight(self):
124
+ """Before any data operation, observe placement and no retained history."""
125
+ try:
126
+ account = self._arm(self.account_id)
127
+ properties = account['properties']
128
+ if (account.get('location') != self.region or account.get('kind') != 'StorageV2' or
129
+ account.get('sku', {}).get('name') not in {'Standard_LRS', 'Standard_ZRS'} or
130
+ properties.get('primaryLocation') != self.region or properties.get('secondaryLocation') is not None or
131
+ properties.get('supportsHttpsTrafficOnly') is not True or properties.get('minimumTlsVersion') != 'TLS1_2' or
132
+ properties.get('allowSharedKeyAccess') is not False or properties.get('allowBlobPublicAccess') is not False or
133
+ properties.get('publicNetworkAccess') != 'Disabled' or
134
+ properties.get('encryption', {}).get('services', {}).get('blob', {}).get('enabled') is not True or
135
+ properties.get('immutableStorageWithVersioning', {}).get('enabled') is True):
136
+ raise ValueError()
137
+ service = self._arm(self.service_id)['properties']
138
+ if service.get('isVersioningEnabled') is not False:
139
+ raise ValueError()
140
+ for name in ['deleteRetentionPolicy', 'containerDeleteRetentionPolicy', 'restorePolicy', 'changeFeed']:
141
+ if service.get(name, {}).get('enabled') is not False:
142
+ raise ValueError()
143
+ container = self._arm(self.container_id)['properties']
144
+ if (container.get('publicAccess') != 'None' or container.get('hasImmutabilityPolicy') is not False or
145
+ container.get('hasLegalHold') is not False or
146
+ container.get('immutableStorageWithVersioning', {}).get('enabled') is True):
147
+ raise ValueError()
148
+ except Exception:
149
+ raise SourceUnavailable('document_object_policy_unavailable') from None
150
+
151
+ def url(self, identifier, side, node_id=None):
152
+ return self.base + quote(object_key(identifier, side, node_id), safe='/')
153
+
154
+ def put(self, identifier, side, raw):
155
+ if side == 'cleanup': raise ValueError('Only the root publisher creates cleanup acknowledgements')
156
+ url = self.url(identifier, side)
157
+ maximum = MAX_INPUT if side == 'input' else MAX_ENVELOPE
158
+ if not isinstance(raw, bytes) or not 0 < len(raw) <= maximum:
159
+ raise ValueError('Use bounded owned document bytes')
160
+ self.preflight()
161
+ _, headers = self._send('PUT', url, raw=raw, headers={'If-None-Match': '*',
162
+ 'Content-Type': 'application/octet-stream', 'Content-Length': str(len(raw)),
163
+ 'Content-MD5': base64.b64encode(hashlib.md5(raw, usedforsecurity=False).digest()).decode(),
164
+ 'x-ms-blob-type': 'BlockBlob'}, limit=4096)
165
+ if headers.get('x-ms-request-server-encrypted') != 'true' or headers.get('x-ms-version-id') is not None:
166
+ raise SourceUnavailable('document_object_unavailable')
167
+ return etag(headers.get('etag'))
168
+
169
+ def load(self, identifier, side, condition=None, *, allow_missing=False, node_id=None):
170
+ if allow_missing and side != 'cleanup': raise ValueError('Only cleanup receipt lookup is optional')
171
+ url = self.url(identifier, side, node_id)
172
+ headers = {} if condition is None else {'If-Match': etag(condition)}
173
+ self.preflight()
174
+ maximum = MAX_INPUT if side == 'input' else 1024 if side == 'cleanup' else MAX_ENVELOPE
175
+ value = self._send('GET', url, headers=headers, limit=maximum, missing=allow_missing)
176
+ if value is None: return None
177
+ raw, observed = value
178
+ try:
179
+ if (observed.get('x-ms-server-encrypted') != 'true' or observed.get('x-ms-version-id') is not None or
180
+ observed.get('x-ms-blob-type') != 'BlockBlob' or
181
+ not isinstance(observed.get('content-length'), str) or
182
+ not re.fullmatch('[1-9][0-9]{0,9}', observed['content-length']) or
183
+ int(observed['content-length']) != len(raw) or (condition is not None and observed.get('etag') != condition)):
184
+ raise ValueError()
185
+ etag(observed.get('etag'))
186
+ return raw
187
+ except Exception:
188
+ raise SourceUnavailable('document_object_unavailable') from None
189
+
190
+ def delete(self, identifier, side):
191
+ if side == 'cleanup': raise ValueError('Cleanup metadata is retired by its dedicated lifecycle policy')
192
+ url = self.url(identifier, side)
193
+ self.preflight()
194
+ try:
195
+ value = self._send('DELETE', url, headers={'x-ms-delete-snapshots': 'include'}, missing=True, limit=4096)
196
+ if value is not None and value[1].get('x-ms-version-id') is not None:
197
+ raise ValueError()
198
+ except Exception:
199
+ raise SourceUnavailable('document_cleanup_unavailable') from None
200
+
201
+ def cleanup_acknowledged(self, identifier, owner, node_id):
202
+ if not isinstance(owner, str) or not re.fullmatch('[0-9a-f]{64}', owner):
203
+ raise ValueError('Use the exact owned cleanup destination fingerprint')
204
+ raw = self.load(identifier, 'cleanup', allow_missing=True, node_id=node_id)
205
+ if raw is None: return False
206
+ try:
207
+ value = json.loads(raw, object_pairs_hook=unique)
208
+ if (not isinstance(value, dict) or value.get('cleaned') is not True or value != {
209
+ 'format': 'enterprise-azure-document-cleanup-v2', 'requestId': identifier,
210
+ 'owner': owner, 'nodeId': node_id, 'cleaned': True}):
211
+ raise ValueError()
212
+ return True
213
+ except Exception:
214
+ raise SourceUnavailable('document_cleanup_receipt_unavailable') from None
215
+
216
+ def cleanup_acknowledged_all(self, identifier, owner, node_ids):
217
+ """Caller must obtain this complete closed set from terminal native work.
218
+
219
+ A partial/preflight node list is not a settlement proof. Empty/duplicate
220
+ sets cannot accidentally certify cleanup; unscheduled jobs require a
221
+ separate verified no-writer lifecycle in the controller.
222
+ """
223
+ if (not isinstance(node_ids, (set, frozenset, list, tuple)) or not 1 <= len(node_ids) <= 100 or
224
+ any(not isinstance(node, str) or not re.fullmatch(r'[A-Za-z0-9_-]{1,128}', node) for node in node_ids) or
225
+ len(set(node_ids)) != len(node_ids)):
226
+ raise ValueError('Use the complete unique native writer-node set')
227
+ return all(self.cleanup_acknowledged(identifier, owner, node) for node in sorted(node_ids))
228
+
229
+ def close(self):
230
+ try:
231
+ self.session.close()
232
+ finally:
233
+ if self.credential is not None:
234
+ self.credential.close()