fde-framework 0.1.43__tar.gz → 0.1.45__tar.gz

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 (404) hide show
  1. {fde_framework-0.1.43 → fde_framework-0.1.45}/CHANGELOG.md +69 -0
  2. {fde_framework-0.1.43 → fde_framework-0.1.45}/PKG-INFO +29 -14
  3. {fde_framework-0.1.43 → fde_framework-0.1.45}/README.md +28 -13
  4. {fde_framework-0.1.43 → fde_framework-0.1.45}/pyproject.toml +1 -1
  5. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/cli.py +70 -0
  6. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/mcp_server.py +76 -14
  7. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_mcp.py +132 -0
  8. fde_framework-0.1.45/tests/test_mcp_stdio.py +119 -0
  9. fde_framework-0.1.45/tests/test_release_consistency.py +38 -0
  10. fde_framework-0.1.43/tests/test_release_consistency.py +0 -18
  11. {fde_framework-0.1.43 → fde_framework-0.1.45}/.gitignore +0 -0
  12. {fde_framework-0.1.43 → fde_framework-0.1.45}/LICENSE +0 -0
  13. {fde_framework-0.1.43 → fde_framework-0.1.45}/examples/invoice-extraction/README.md +0 -0
  14. {fde_framework-0.1.43 → fde_framework-0.1.45}/examples/policy-qa/README.md +0 -0
  15. {fde_framework-0.1.43 → fde_framework-0.1.45}/examples/support-triage/README.md +0 -0
  16. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/ansible-playbook.md +0 -0
  17. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/assisted-deterministic.md +0 -0
  18. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/audit-only.md +0 -0
  19. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/boundary-and-audit.md +0 -0
  20. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/cascade.md +0 -0
  21. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/classical-ml.md +0 -0
  22. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/compose.md +0 -0
  23. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/decision-log.md +0 -0
  24. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/deterministic-masking.md +0 -0
  25. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/deterministic.md +0 -0
  26. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/direct-call.md +0 -0
  27. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/episodic-store.md +0 -0
  28. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/explainability-record.md +0 -0
  29. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/field-match.md +0 -0
  30. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/finetune.md +0 -0
  31. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/fixed-sequence.md +0 -0
  32. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/gitops.md +0 -0
  33. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/governed-tools.md +0 -0
  34. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/graph-expanded-retrieval.md +0 -0
  35. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/graph-retrieval.md +0 -0
  36. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/hybrid-search.md +0 -0
  37. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/judged.md +0 -0
  38. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/keyword-search.md +0 -0
  39. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/kubernetes-manifests.md +0 -0
  40. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/labelled-decision.md +0 -0
  41. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/labelled-metrics.md +0 -0
  42. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/llm-extraction.md +0 -0
  43. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/llm-scrubbing.md +0 -0
  44. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/llm.md +0 -0
  45. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/local-embedding.md +0 -0
  46. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/managed-api.md +0 -0
  47. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/managed-embedding.md +0 -0
  48. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/manual-runbook.md +0 -0
  49. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/model-planner.md +0 -0
  50. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/ocr-pipeline.md +0 -0
  51. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/optimisation-reasoning.md +0 -0
  52. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/optimisation.md +0 -0
  53. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/passthrough.md +0 -0
  54. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/reranked-retrieval.md +0 -0
  55. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/role-scoped-authority.md +0 -0
  56. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/segmentation.md +0 -0
  57. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/self-hosted.md +0 -0
  58. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/serverless-gpu.md +0 -0
  59. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/speech-transcription.md +0 -0
  60. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/structured-logs.md +0 -0
  61. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/systemd-unit.md +0 -0
  62. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/terraform-module.md +0 -0
  63. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/text-extraction.md +0 -0
  64. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/traced.md +0 -0
  65. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/vector-search.md +0 -0
  66. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/video-ingestion.md +0 -0
  67. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/windowed-ingestion.md +0 -0
  68. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/working-state.md +0 -0
  69. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/cases/churn-scoring.md +0 -0
  70. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/cases/route-planning.md +0 -0
  71. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/cases/structured-extraction.md +0 -0
  72. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/cases/studio-style.md +0 -0
  73. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/accountability.md +0 -0
  74. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/deployment.md +0 -0
  75. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/embedding.md +0 -0
  76. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/evaluation.md +0 -0
  77. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/governance.md +0 -0
  78. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/integration.md +0 -0
  79. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/memory.md +0 -0
  80. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/observability.md +0 -0
  81. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/perception.md +0 -0
  82. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/planning.md +0 -0
  83. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/provisioning.md +0 -0
  84. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/reasoning.md +0 -0
  85. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/redaction.md +0 -0
  86. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/representation.md +0 -0
  87. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/retrieval.md +0 -0
  88. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/serving.md +0 -0
  89. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/accelerator.md +0 -0
  90. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/access_model.md +0 -0
  91. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/arrival_rate.md +0 -0
  92. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/availability_target.md +0 -0
  93. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/cheap_path_coverage.md +0 -0
  94. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/confidence_calibrated.md +0 -0
  95. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/container_competence.md +0 -0
  96. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/corpus_churn.md +0 -0
  97. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/corpus_size.md +0 -0
  98. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/data_residency.md +0 -0
  99. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/environment_lifetime.md +0 -0
  100. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/existing_cluster.md +0 -0
  101. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/existing_iac_tool.md +0 -0
  102. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/external_systems.md +0 -0
  103. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/hosting.md +0 -0
  104. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/human_waiting.md +0 -0
  105. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/input_format.md +0 -0
  106. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/interpretability_required.md +0 -0
  107. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/labelled_count.md +0 -0
  108. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/latency_budget_ms.md +0 -0
  109. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/licence_posture.md +0 -0
  110. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/operates_after_handover.md +0 -0
  111. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/output_shape.md +0 -0
  112. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/provisioning_api.md +0 -0
  113. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/query_pattern.md +0 -0
  114. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/recall_span.md +0 -0
  115. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/sensitivity_present.md +0 -0
  116. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Generator.md +0 -0
  117. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Guard.md +0 -0
  118. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Mapper.md +0 -0
  119. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/ModelServer.md +0 -0
  120. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Parser.md +0 -0
  121. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Planner.md +0 -0
  122. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Retriever.md +0 -0
  123. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Scorer.md +0 -0
  124. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Store.md +0 -0
  125. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/ToolBoundary.md +0 -0
  126. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Tracer.md +0 -0
  127. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/locales/eu-gdpr.md +0 -0
  128. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/locales/in-dpdp.md +0 -0
  129. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/ansible-playbook.md +0 -0
  130. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/assisted-deterministic.md +0 -0
  131. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/audit-only.md +0 -0
  132. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/boundary-and-audit.md +0 -0
  133. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/cascade-reasoning.md +0 -0
  134. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/cascade-representation.md +0 -0
  135. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/cascade-retrieval.md +0 -0
  136. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/classical-ml-reasoning.md +0 -0
  137. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/classical-ml.md +0 -0
  138. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/compose.md +0 -0
  139. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/decision-log.md +0 -0
  140. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/deterministic-masking.md +0 -0
  141. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/deterministic.md +0 -0
  142. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/direct-call.md +0 -0
  143. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/episodic-store.md +0 -0
  144. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/explainability-record.md +0 -0
  145. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/field-match.md +0 -0
  146. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/finetune-representation.md +0 -0
  147. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/finetune.md +0 -0
  148. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/fixed-sequence.md +0 -0
  149. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/gitops.md +0 -0
  150. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/governed-tools.md +0 -0
  151. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/graph-expanded-retrieval.md +0 -0
  152. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/graph-retrieval.md +0 -0
  153. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/hybrid-search.md +0 -0
  154. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/judged.md +0 -0
  155. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/keyword-search.md +0 -0
  156. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/kubernetes-manifests.md +0 -0
  157. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/labelled-decision.md +0 -0
  158. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/labelled-metrics.md +0 -0
  159. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/llm-representation.md +0 -0
  160. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/llm-scrubbing.md +0 -0
  161. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/llm.md +0 -0
  162. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/local-embedding.md +0 -0
  163. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/managed-api.md +0 -0
  164. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/managed-embedding.md +0 -0
  165. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/manual-runbook.md +0 -0
  166. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/model-planner.md +0 -0
  167. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/ocr-pipeline.md +0 -0
  168. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/optimisation-reasoning.md +0 -0
  169. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/optimisation.md +0 -0
  170. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/passthrough.md +0 -0
  171. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/reranked-retrieval.md +0 -0
  172. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/role-scoped-authority.md +0 -0
  173. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/segmentation.md +0 -0
  174. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/self-hosted.md +0 -0
  175. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/serverless-gpu.md +0 -0
  176. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/speech-transcription.md +0 -0
  177. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/structured-logs.md +0 -0
  178. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/systemd-unit.md +0 -0
  179. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/terraform-module.md +0 -0
  180. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/text-extraction.md +0 -0
  181. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/traced.md +0 -0
  182. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/vector-search.md +0 -0
  183. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/video-ingestion.md +0 -0
  184. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/windowed-ingestion.md +0 -0
  185. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/working-state.md +0 -0
  186. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/langgraph.md +0 -0
  187. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/llamaindex.md +0 -0
  188. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/local-judge.md +0 -0
  189. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/mcp.md +0 -0
  190. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/ollama.md +0 -0
  191. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/openai-judge.md +0 -0
  192. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/opentelemetry.md +0 -0
  193. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/ortools.md +0 -0
  194. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/peft.md +0 -0
  195. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/pgvector.md +0 -0
  196. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/plain-python.md +0 -0
  197. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/qdrant.md +0 -0
  198. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/supermemory.md +0 -0
  199. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/tesseract.md +0 -0
  200. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/vllm.md +0 -0
  201. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/whisper.md +0 -0
  202. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/xgboost.md +0 -0
  203. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/accountability/decision-log.plain.py.j2 +0 -0
  204. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/accountability/explainability-record.plain.py.j2 +0 -0
  205. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/deployment/compose.plain.py.j2 +0 -0
  206. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/deployment/kubernetes-manifests.plain.py.j2 +0 -0
  207. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/deployment/systemd-unit.plain.py.j2 +0 -0
  208. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/embedding/local-embedding.plain.py.j2 +0 -0
  209. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/embedding/managed-embedding.plain.py.j2 +0 -0
  210. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/field-match.plain.py.j2 +0 -0
  211. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/judged.local-judge.py.j2 +0 -0
  212. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/judged.openai-judge.py.j2 +0 -0
  213. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/judged.plain.py.j2 +0 -0
  214. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/labelled-metrics.plain.py.j2 +0 -0
  215. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/labelled-metrics.xgboost.py.j2 +0 -0
  216. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/governance/audit-only.plain.py.j2 +0 -0
  217. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/governance/boundary-and-audit.plain.py.j2 +0 -0
  218. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/governance/role-scoped-authority.plain.py.j2 +0 -0
  219. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/integration/direct-call.plain.py.j2 +0 -0
  220. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/integration/governed-tools.mcp.py.j2 +0 -0
  221. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/integration/governed-tools.plain.py.j2 +0 -0
  222. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/memory/episodic-store.pgvector.py.j2 +0 -0
  223. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/memory/episodic-store.plain.py.j2 +0 -0
  224. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/memory/episodic-store.supermemory.py.j2 +0 -0
  225. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/memory/working-state.plain.py.j2 +0 -0
  226. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/observability/structured-logs.plain.py.j2 +0 -0
  227. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/observability/traced.opentelemetry.py.j2 +0 -0
  228. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/observability/traced.plain.py.j2 +0 -0
  229. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/ocr-pipeline.plain.py.j2 +0 -0
  230. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/ocr-pipeline.tesseract.py.j2 +0 -0
  231. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/passthrough.plain.py.j2 +0 -0
  232. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/speech-transcription.plain.py.j2 +0 -0
  233. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/speech-transcription.whisper.py.j2 +0 -0
  234. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/text-extraction.plain.py.j2 +0 -0
  235. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/video-ingestion.plain.py.j2 +0 -0
  236. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/windowed-ingestion.plain.py.j2 +0 -0
  237. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/fixed-sequence.plain.py.j2 +0 -0
  238. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/model-planner.langgraph.py.j2 +0 -0
  239. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/model-planner.plain.py.j2 +0 -0
  240. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/optimisation.ortools.py.j2 +0 -0
  241. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/optimisation.plain.py.j2 +0 -0
  242. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/provisioning/ansible-playbook.plain.py.j2 +0 -0
  243. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/provisioning/gitops.plain.py.j2 +0 -0
  244. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/provisioning/manual-runbook.plain.py.j2 +0 -0
  245. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/provisioning/terraform-module.plain.py.j2 +0 -0
  246. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/cascade.plain.py.j2 +0 -0
  247. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/classical-ml.plain.py.j2 +0 -0
  248. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/classical-ml.xgboost.py.j2 +0 -0
  249. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/finetune.plain.py.j2 +0 -0
  250. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/labelled-decision.plain.py.j2 +0 -0
  251. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/llm.plain.py.j2 +0 -0
  252. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/optimisation.ortools.py.j2 +0 -0
  253. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/optimisation.plain.py.j2 +0 -0
  254. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/redaction/deterministic-masking.plain.py.j2 +0 -0
  255. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/redaction/llm-scrubbing.plain.py.j2 +0 -0
  256. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/assisted.plain.py.j2 +0 -0
  257. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/cascade.plain.py.j2 +0 -0
  258. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/classical-ml.plain.py.j2 +0 -0
  259. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/classical-ml.xgboost.py.j2 +0 -0
  260. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/deterministic.plain.py.j2 +0 -0
  261. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/finetune.plain.py.j2 +0 -0
  262. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/llm.plain.py.j2 +0 -0
  263. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/segmentation.plain.py.j2 +0 -0
  264. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/cascade.plain.py.j2 +0 -0
  265. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-expanded-retrieval.pgvector.py.j2 +0 -0
  266. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-expanded-retrieval.plain.py.j2 +0 -0
  267. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-expanded-retrieval.qdrant.py.j2 +0 -0
  268. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-retrieval.pgvector.py.j2 +0 -0
  269. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-retrieval.plain.py.j2 +0 -0
  270. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-retrieval.qdrant.py.j2 +0 -0
  271. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/hybrid-search.plain.py.j2 +0 -0
  272. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/keyword-search.plain.py.j2 +0 -0
  273. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/reranked-retrieval.plain.py.j2 +0 -0
  274. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/vector-search.llamaindex.py.j2 +0 -0
  275. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/vector-search.pgvector.py.j2 +0 -0
  276. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/vector-search.plain.py.j2 +0 -0
  277. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/vector-search.qdrant.py.j2 +0 -0
  278. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/managed-api.plain.py.j2 +0 -0
  279. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/self-hosted.ollama.py.j2 +0 -0
  280. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/self-hosted.plain.py.j2 +0 -0
  281. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/self-hosted.vllm.py.j2 +0 -0
  282. {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/serverless-gpu.plain.py.j2 +0 -0
  283. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/__init__.py +0 -0
  284. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/architect.py +0 -0
  285. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/bench.py +0 -0
  286. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/costing.py +0 -0
  287. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/debt.py +0 -0
  288. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/decide.py +0 -0
  289. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/decompose.py +0 -0
  290. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/deploy.py +0 -0
  291. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/drift.py +0 -0
  292. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/emit.py +0 -0
  293. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/evolution.py +0 -0
  294. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/experiment.py +0 -0
  295. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/factlog.py +0 -0
  296. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/forecast.py +0 -0
  297. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/gates.py +0 -0
  298. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/graph.py +0 -0
  299. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/history.py +0 -0
  300. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/impact.py +0 -0
  301. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/implement.py +0 -0
  302. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/importer.py +0 -0
  303. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/__init__.py +0 -0
  304. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/answers.py +0 -0
  305. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/documents.py +0 -0
  306. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/interview.py +0 -0
  307. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/llm_reader.py +0 -0
  308. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/prose.py +0 -0
  309. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/samples.py +0 -0
  310. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/lifecycle.py +0 -0
  311. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/__init__.py +0 -0
  312. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/base.py +0 -0
  313. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/fact.py +0 -0
  314. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/profile.py +0 -0
  315. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/respondent.py +0 -0
  316. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/schema.py +0 -0
  317. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/moves.py +0 -0
  318. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/ops.py +0 -0
  319. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/predicate.py +0 -0
  320. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/realization.py +0 -0
  321. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/registry.py +0 -0
  322. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/sandbox.py +0 -0
  323. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/scan.py +0 -0
  324. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/scorecard.py +0 -0
  325. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/space.py +0 -0
  326. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/stakeholders.py +0 -0
  327. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/stop.py +0 -0
  328. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/training.py +0 -0
  329. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/value.py +0 -0
  330. {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/workflow.py +0 -0
  331. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/__init__.py +0 -0
  332. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/conftest.py +0 -0
  333. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/models/__init__.py +0 -0
  334. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/models/test_profile.py +0 -0
  335. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_acceptance.py +0 -0
  336. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_access_and_sensitivity.py +0 -0
  337. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_answers.py +0 -0
  338. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_audit_fixes.py +0 -0
  339. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_bench.py +0 -0
  340. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_build_receipt.py +0 -0
  341. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cascade.py +0 -0
  342. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_engagement.py +0 -0
  343. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_evolution.py +0 -0
  344. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_gates.py +0 -0
  345. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_intake.py +0 -0
  346. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_kb.py +0 -0
  347. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_robustness.py +0 -0
  348. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_samples.py +0 -0
  349. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_costing.py +0 -0
  350. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_coverage.py +0 -0
  351. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_dead_zones.py +0 -0
  352. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_debt.py +0 -0
  353. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_decide.py +0 -0
  354. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_decompose.py +0 -0
  355. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_deploy.py +0 -0
  356. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_documents.py +0 -0
  357. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_drift.py +0 -0
  358. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_embedding.py +0 -0
  359. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_emit.py +0 -0
  360. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_evolution.py +0 -0
  361. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_example_transcript.py +0 -0
  362. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_experiment.py +0 -0
  363. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_factlog.py +0 -0
  364. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_finetune.py +0 -0
  365. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_finetune_real.py +0 -0
  366. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_forecast.py +0 -0
  367. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_gates.py +0 -0
  368. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_history.py +0 -0
  369. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_impact.py +0 -0
  370. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_implement.py +0 -0
  371. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_importer.py +0 -0
  372. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_interview.py +0 -0
  373. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_lifecycle.py +0 -0
  374. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_llm_reader.py +0 -0
  375. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_locales.py +0 -0
  376. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_memory_retrieval.py +0 -0
  377. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_more_templates.py +0 -0
  378. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_moves.py +0 -0
  379. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_multimodal.py +0 -0
  380. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_next.py +0 -0
  381. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_ops.py +0 -0
  382. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_outcome_contract.py +0 -0
  383. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_package.py +0 -0
  384. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_prose.py +0 -0
  385. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_realization.py +0 -0
  386. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_registry.py +0 -0
  387. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_remaining.py +0 -0
  388. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_retrieval_eval.py +0 -0
  389. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_reversibility.py +0 -0
  390. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_samples.py +0 -0
  391. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_sandbox.py +0 -0
  392. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_sanitisation.py +0 -0
  393. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_scan.py +0 -0
  394. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_schema.py +0 -0
  395. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_scorecard.py +0 -0
  396. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_serving.py +0 -0
  397. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_shipped_registry.py +0 -0
  398. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_space.py +0 -0
  399. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_stack_swap.py +0 -0
  400. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_stakeholders.py +0 -0
  401. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_stop.py +0 -0
  402. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_supermemory.py +0 -0
  403. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_templates.py +0 -0
  404. {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_value.py +0 -0
@@ -5,6 +5,75 @@ the project is pre-release, so everything sits under 0.1.0 until the first tag.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.1.45] — 2026-10-11
9
+
10
+ A twelfth outside reading, and a coding agent driving the server for the
11
+ first time, found three things between them: a release page that said
12
+ nothing about what it was, a date a day ahead, and an MCP server that lost
13
+ the agent's second call two different ways.
14
+
15
+ - **The agent's second call finds the first call's work.**
16
+ `start(name="acme")` puts the record at engagements/acme;
17
+ `status(engagement="acme")` a moment later looked at `<workspace>/acme`
18
+ and said there was no engagement. The in-process test had passed the
19
+ path `start` returned, so it never asked by name. A bare name is now
20
+ looked for where `start` puts it, as `fde status acme` does at the
21
+ terminal; a path is taken as given; a name that climbs out is refused
22
+ before anything is looked for.
23
+ - **Calls issued at once all come back.** Claude Code asked for `status`
24
+ and `next` in the same breath and the second came back "Connection
25
+ closed". The server runs each tool on its own thread, and the capture
26
+ that collects a command's output does so by swapping the process's
27
+ stdout; two swaps interleaved left one command printing straight onto
28
+ the JSON-RPC channel. Commands now run one at a time inside the server.
29
+ A new suite drives the server as a separate process over stdio with the
30
+ official client: six calls at once for three rounds, a call abandoned
31
+ midway, a record changed on disk between calls, and every tool's input
32
+ schema published. The hang reproduced before the fix and fails within
33
+ ninety seconds instead of hanging CI.
34
+ - **The envelope's contract is a JSON Schema.** `fde mcp --schema` prints
35
+ it; every reply is validated against it; the schema's list of tools is
36
+ the server's; a field it does not name or a status it does not name
37
+ fails. The README says when the version moves: a field changing meaning
38
+ or going away, never one being added.
39
+ - **A release page means verified.** It is written only after the wheel
40
+ PyPI serves has been installed on Python 3.11, 3.12 and 3.13, hashed
41
+ against the wheel the tests ran on, and walked through the first five
42
+ minutes. It carries the wheel and the sdist, their SHA-256, the commit,
43
+ the run and the install line.
44
+ - **One date.** 0.1.44 shipped on October 11 dated October 12 in both
45
+ CHANGELOG and CITATION. Corrected; the release workflow now holds the
46
+ CHANGELOG date, CITATION's date-released and the tagged commit's own day
47
+ in the author's time zone to one value, and a test holds the two files
48
+ to each other.
49
+ - A corrupt record is a sentence naming the file, not a traceback, through
50
+ the server as at the terminal; now pinned by a test.
51
+
52
+ ## [0.1.44] — 2026-10-11
53
+
54
+ An eleventh outside reading found the README contradicting itself on its
55
+ own test count, and asked for the published wheel to be the thing that
56
+ is tested. Both done, with the two small things a stranger's first five
57
+ minutes need.
58
+
59
+ - **One count, in one place.** The README stated the test count three
60
+ times and two of them were six releases stale. It is now stated once,
61
+ in the claim ladder, and the other two places say where to look.
62
+ - **A stranger's install, every release.** The release workflow now
63
+ installs the published wheel, not the checkout, into a fresh
64
+ interpreter and walks the documented first five minutes: triage,
65
+ start, next, status, the MCP check, the doctor. The index lags a tag
66
+ by a minute, so it retries before concluding anything.
67
+ - **`fde doctor`.** The interpreter, the package, the registry, the
68
+ working directory, each optional extra, and whether docker, the agent
69
+ and the linter are on the path -- each line a measurement, exit 1 only
70
+ when something the core needs is missing.
71
+ - **The envelope is versioned.** `schema_version` on every reply, the
72
+ contract written in the server module, and a test that reaches all
73
+ four statuses, including failed.
74
+ - The first screen links the banking run as the one case where the
75
+ framework told itself to stop.
76
+
8
77
  ## [0.1.43] — 2026-10-11
9
78
 
10
79
  A tenth outside reading, of 0.1.42, found the one place the server's
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fde-framework
3
- Version: 0.1.43
3
+ Version: 0.1.45
4
4
  Summary: The decision before the AI build: evidence in, an architecture with receipts out, a scored build or a stop. For Forward Deployed Engineers and anyone deciding whether an AI system should exist.
5
5
  Project-URL: Homepage, https://github.com/atulkapoor/fde-framework
6
6
  Project-URL: Documentation, https://atulkapoor.github.io/fde-framework/
@@ -111,12 +111,15 @@ it can say stop.
111
111
 
112
112
  ```bash
113
113
  pip install fde-framework
114
+ fde doctor # the interpreter, the package, the registry, the extras: measured, not guessed
114
115
  fde start acme --statement "Route each support message to one of 77 queues."
115
116
  fde next acme # the one thing to learn or do next, and what turns on it
116
117
  ```
117
118
 
118
119
  One minute, nothing to bring: paste two problems and see which one is
119
- decidable yet.
120
+ decidable yet. For what the whole loop looks like on a real dataset,
121
+ including the one time the framework told itself to stop, read the
122
+ [banking run](https://github.com/atulkapoor/fde-demo-banking#the-operating-loop-closed).
120
123
 
121
124
  ```
122
125
  $ fde triage --statement "Route each inbound support message to one of 77 queues; customer data may not leave our cloud account." \
@@ -154,16 +157,26 @@ the argument `mcp`:
154
157
  It serves `triage`, `start`, `frame`, `status`, `next`, `stage`, `debt`,
155
158
  `stop_when` and `history`, each returning exactly what the terminal prints,
156
159
  refusals included, beside a small envelope an agent can branch on without
157
- parsing prose: `status` (ok, blocked, refused, failed), `mutated`, and the
158
- workspace. The envelope follows the command's exit, never a reading of
159
- its text. An agent is not a person, so every path it supplies must
160
- stay inside one workspace, the directory the server started in or
160
+ parsing prose: `schema_version`, `status` (ok, blocked, refused, failed),
161
+ `mutated`, and the workspace. The envelope follows the command's exit,
162
+ never a reading of its text. Its contract is a JSON Schema, `fde mcp
163
+ --schema`, that every reply is tested against; a client pins
164
+ `schema_version`, which moves only when a field changes meaning or goes
165
+ away, never when one is added. An engagement is named as `start` named
166
+ it (`start(name="acme")`, then `status(engagement="acme")`), or by its
167
+ path. An agent is not a person, so every path it supplies must stay
168
+ inside one workspace, the directory the server started in or
161
169
  `FDE_WORKSPACE`; a path outside it, a traversal, or a symlink that points
162
170
  out is refused in words, and engagement names are plain names. The two
163
171
  tools that write to the record, `start` and `frame`, say so and carry the
164
- protocol's hints so a client can ask first. Building, implementing and
165
- deploying are not served; they stay on the command line, where the person
166
- who runs them is accountable for them.
172
+ protocol's hints so a client can ask first. Calls run one at a time
173
+ inside the server, so an agent that asks for `status` and `next` in the
174
+ same breath gets both. Exercised against two real clients: the official
175
+ MCP Python client over stdio, in the test suite, with concurrent calls,
176
+ an abandoned call and a record changed on disk between calls; and Claude
177
+ Code 2.1, by hand, from the configuration above. Building, implementing
178
+ and deploying are not served; they stay on the command line, where the
179
+ person who runs them is accountable for them.
167
180
 
168
181
  > ### Five founding engagements
169
182
  > One real workflow, your real examples, a measured baseline, and someone
@@ -228,7 +241,7 @@ ladder, so nothing here can be over-read:
228
241
 
229
242
  | Claim | Status | The evidence, and where it stops |
230
243
  |---|---|---|
231
- | The framework is implemented | verified | 1,306 tests on three Pythons in CI; lint clean; every audit finding pinned as a check before it was fixed |
244
+ | The framework is implemented | verified | 1,316 tests on three Pythons in CI; lint clean; every audit finding pinned as a check before it was fixed; every release installed from the published wheel into a fresh interpreter and walked through the first five minutes |
232
245
  | A build is reproducible | verified | byte-identical rebuild pinned by test; each public demo carries a Reproduce section and its digests |
233
246
  | The decisions generalise | partial | four shapes (extraction, freeform QA, a labelled decision, seventy-seven-way routing) on real data; one industry set; `fde kb sweep` names the shapes nothing serves |
234
247
  | The deliverable is production-reliable | unverified | no production engagement; the banking operating loop ran on a laptop and its record says so |
@@ -292,9 +305,10 @@ pairs, role-scoped interview, hardware scan) → fact log with provenance →
292
305
  permutation space → eight gates → decide → architect → build (code, evals,
293
306
  deploy and ops assets, `RISKS.md`, `COMPLIANCE.md`) → retro and case
294
307
  capture. Overrides are honoured on the next run, trigger observations feed
295
- calibration, and a reviewed case can enter the corpus. 1160+ tests; nine
296
- fresh-eyes audit passes, every finding resolved and the fix pinned as a
297
- regression test; CI gates on the suite, lint, and a sanitisation scan of
308
+ calibration, and a reviewed case can enter the corpus. The test count is
309
+ stated once, in the claim ladder above, and updated at every release;
310
+ eleven fresh-eyes audit passes, every finding resolved and the fix pinned
311
+ as a regression test; CI gates on the suite, lint, and a sanitisation scan of
298
312
  the tree *and its history*; the evidence corpus is anchored to publicly
299
313
  documented production deployments; every decision is reproducible from its
300
314
  inputs.
@@ -712,6 +726,7 @@ the same receipts the emitted `ARCHITECTURE.md` prints.
712
726
  | `fde bench <eng>=<project> ...` | the same figures off every record, side by side, in `BENCH.md` |
713
727
  | `fde history <eng>` | every dated entry on the record in order, one line each |
714
728
  | `fde mcp` / `fde mcp --check` | serve triage, start, frame, status, next, stage, debt, stop-when and history to a coding agent over stdio, confined to `FDE_WORKSPACE`; `--check` lists what it serves |
729
+ | `fde doctor` | whether this install can do what the README says: interpreter, package, registry, working directory, extras, docker, the agent, the linter; exit 1 only on what the core needs |
715
730
  | `fde outcome-contract <eng> --owner --metric --baseline --unit --target --method --window` | the eighth gate: which number this system exists to move, from what to what, measured how, by when |
716
731
  | `fde debt <eng>` | decision debt: what nobody has settled, with an owner, what it blocks, and its age |
717
732
  | `fde stop-when <eng> --when "answered_accuracy < 0.88"` | what evidence would stop the engagement; judged against the record, exit 1 and a STOP stage when triggered |
@@ -898,7 +913,7 @@ Set up as in [Install → from source](#install) (python3.11+), then:
898
913
 
899
914
  ```bash
900
915
  .venv/bin/pip install -e ".[dev,documents]"
901
- .venv/bin/pytest -q # 1160+ tests, ~ 100s
916
+ .venv/bin/pytest -q # the full suite, about two minutes
902
917
  .venv/bin/ruff check src tests
903
918
  ```
904
919
 
@@ -67,12 +67,15 @@ it can say stop.
67
67
 
68
68
  ```bash
69
69
  pip install fde-framework
70
+ fde doctor # the interpreter, the package, the registry, the extras: measured, not guessed
70
71
  fde start acme --statement "Route each support message to one of 77 queues."
71
72
  fde next acme # the one thing to learn or do next, and what turns on it
72
73
  ```
73
74
 
74
75
  One minute, nothing to bring: paste two problems and see which one is
75
- decidable yet.
76
+ decidable yet. For what the whole loop looks like on a real dataset,
77
+ including the one time the framework told itself to stop, read the
78
+ [banking run](https://github.com/atulkapoor/fde-demo-banking#the-operating-loop-closed).
76
79
 
77
80
  ```
78
81
  $ fde triage --statement "Route each inbound support message to one of 77 queues; customer data may not leave our cloud account." \
@@ -110,16 +113,26 @@ the argument `mcp`:
110
113
  It serves `triage`, `start`, `frame`, `status`, `next`, `stage`, `debt`,
111
114
  `stop_when` and `history`, each returning exactly what the terminal prints,
112
115
  refusals included, beside a small envelope an agent can branch on without
113
- parsing prose: `status` (ok, blocked, refused, failed), `mutated`, and the
114
- workspace. The envelope follows the command's exit, never a reading of
115
- its text. An agent is not a person, so every path it supplies must
116
- stay inside one workspace, the directory the server started in or
116
+ parsing prose: `schema_version`, `status` (ok, blocked, refused, failed),
117
+ `mutated`, and the workspace. The envelope follows the command's exit,
118
+ never a reading of its text. Its contract is a JSON Schema, `fde mcp
119
+ --schema`, that every reply is tested against; a client pins
120
+ `schema_version`, which moves only when a field changes meaning or goes
121
+ away, never when one is added. An engagement is named as `start` named
122
+ it (`start(name="acme")`, then `status(engagement="acme")`), or by its
123
+ path. An agent is not a person, so every path it supplies must stay
124
+ inside one workspace, the directory the server started in or
117
125
  `FDE_WORKSPACE`; a path outside it, a traversal, or a symlink that points
118
126
  out is refused in words, and engagement names are plain names. The two
119
127
  tools that write to the record, `start` and `frame`, say so and carry the
120
- protocol's hints so a client can ask first. Building, implementing and
121
- deploying are not served; they stay on the command line, where the person
122
- who runs them is accountable for them.
128
+ protocol's hints so a client can ask first. Calls run one at a time
129
+ inside the server, so an agent that asks for `status` and `next` in the
130
+ same breath gets both. Exercised against two real clients: the official
131
+ MCP Python client over stdio, in the test suite, with concurrent calls,
132
+ an abandoned call and a record changed on disk between calls; and Claude
133
+ Code 2.1, by hand, from the configuration above. Building, implementing
134
+ and deploying are not served; they stay on the command line, where the
135
+ person who runs them is accountable for them.
123
136
 
124
137
  > ### Five founding engagements
125
138
  > One real workflow, your real examples, a measured baseline, and someone
@@ -184,7 +197,7 @@ ladder, so nothing here can be over-read:
184
197
 
185
198
  | Claim | Status | The evidence, and where it stops |
186
199
  |---|---|---|
187
- | The framework is implemented | verified | 1,306 tests on three Pythons in CI; lint clean; every audit finding pinned as a check before it was fixed |
200
+ | The framework is implemented | verified | 1,316 tests on three Pythons in CI; lint clean; every audit finding pinned as a check before it was fixed; every release installed from the published wheel into a fresh interpreter and walked through the first five minutes |
188
201
  | A build is reproducible | verified | byte-identical rebuild pinned by test; each public demo carries a Reproduce section and its digests |
189
202
  | The decisions generalise | partial | four shapes (extraction, freeform QA, a labelled decision, seventy-seven-way routing) on real data; one industry set; `fde kb sweep` names the shapes nothing serves |
190
203
  | The deliverable is production-reliable | unverified | no production engagement; the banking operating loop ran on a laptop and its record says so |
@@ -248,9 +261,10 @@ pairs, role-scoped interview, hardware scan) → fact log with provenance →
248
261
  permutation space → eight gates → decide → architect → build (code, evals,
249
262
  deploy and ops assets, `RISKS.md`, `COMPLIANCE.md`) → retro and case
250
263
  capture. Overrides are honoured on the next run, trigger observations feed
251
- calibration, and a reviewed case can enter the corpus. 1160+ tests; nine
252
- fresh-eyes audit passes, every finding resolved and the fix pinned as a
253
- regression test; CI gates on the suite, lint, and a sanitisation scan of
264
+ calibration, and a reviewed case can enter the corpus. The test count is
265
+ stated once, in the claim ladder above, and updated at every release;
266
+ eleven fresh-eyes audit passes, every finding resolved and the fix pinned
267
+ as a regression test; CI gates on the suite, lint, and a sanitisation scan of
254
268
  the tree *and its history*; the evidence corpus is anchored to publicly
255
269
  documented production deployments; every decision is reproducible from its
256
270
  inputs.
@@ -668,6 +682,7 @@ the same receipts the emitted `ARCHITECTURE.md` prints.
668
682
  | `fde bench <eng>=<project> ...` | the same figures off every record, side by side, in `BENCH.md` |
669
683
  | `fde history <eng>` | every dated entry on the record in order, one line each |
670
684
  | `fde mcp` / `fde mcp --check` | serve triage, start, frame, status, next, stage, debt, stop-when and history to a coding agent over stdio, confined to `FDE_WORKSPACE`; `--check` lists what it serves |
685
+ | `fde doctor` | whether this install can do what the README says: interpreter, package, registry, working directory, extras, docker, the agent, the linter; exit 1 only on what the core needs |
671
686
  | `fde outcome-contract <eng> --owner --metric --baseline --unit --target --method --window` | the eighth gate: which number this system exists to move, from what to what, measured how, by when |
672
687
  | `fde debt <eng>` | decision debt: what nobody has settled, with an owner, what it blocks, and its age |
673
688
  | `fde stop-when <eng> --when "answered_accuracy < 0.88"` | what evidence would stop the engagement; judged against the record, exit 1 and a STOP stage when triggered |
@@ -854,7 +869,7 @@ Set up as in [Install → from source](#install) (python3.11+), then:
854
869
 
855
870
  ```bash
856
871
  .venv/bin/pip install -e ".[dev,documents]"
857
- .venv/bin/pytest -q # 1160+ tests, ~ 100s
872
+ .venv/bin/pytest -q # the full suite, about two minutes
858
873
  .venv/bin/ruff check src tests
859
874
  ```
860
875
 
@@ -2,7 +2,7 @@
2
2
  # Distribution name matches the repository and stays unique on any index;
3
3
  # the import package and the CLI command remain the short `fde`.
4
4
  name = "fde-framework"
5
- version = "0.1.43"
5
+ version = "0.1.45"
6
6
  description = "The decision before the AI build: evidence in, an architecture with receipts out, a scored build or a stop. For Forward Deployed Engineers and anyone deciding whether an AI system should exist."
7
7
  readme = "README.md"
8
8
  license = {text = "Apache-2.0"}
@@ -2339,17 +2339,87 @@ def experiment_cmd(
2339
2339
  raise typer.Exit(1) from exc
2340
2340
 
2341
2341
 
2342
+ @app.command("doctor")
2343
+ def doctor_cmd(
2344
+ registry_root: Annotated[Path, typer.Option("--registry")] = DEFAULT_ROOT,
2345
+ ) -> None:
2346
+ """Whether this install can do what the README says: the interpreter,
2347
+ the package, the registry, the working directory, the optional extras,
2348
+ and the tools the implement loop and the sandbox would call. Each line
2349
+ is a measurement, never a guess; exit 1 when something the core needs
2350
+ is missing."""
2351
+ import importlib.metadata
2352
+ import shutil
2353
+ import sys
2354
+
2355
+ from fde.registry import is_empty, load_registry
2356
+
2357
+ rows: list[tuple[str, bool | None, str]] = []
2358
+ rows.append(("python", sys.version_info >= (3, 11),
2359
+ f"{sys.version.split()[0]} at {sys.executable}"))
2360
+ try:
2361
+ rows.append(("fde-framework", True, importlib.metadata.version("fde-framework")))
2362
+ except importlib.metadata.PackageNotFoundError:
2363
+ rows.append(("fde-framework", False, "not installed as a package (a checkout?)"))
2364
+ try:
2365
+ registry = load_registry(registry_root)
2366
+ ok = not is_empty(registry)
2367
+ rows.append(("registry", ok, f"{registry_root} -- {len(registry.dimensions)} dimensions, "
2368
+ f"{len(registry.approaches)} approaches, "
2369
+ f"{len(registry.patterns)} patterns, "
2370
+ f"{len(registry.stacks)} stacks" if ok
2371
+ else f"{registry_root} loads empty"))
2372
+ except RegistryError as exc:
2373
+ rows.append(("registry", False, str(exc).splitlines()[0]))
2374
+ cwd = Path.cwd()
2375
+ rows.append(("working directory", os.access(cwd, os.W_OK), f"{cwd} is "
2376
+ + ("writable" if os.access(cwd, os.W_OK) else "not writable")))
2377
+ for extra, module, why in (("documents", "pypdf", "fde frame --file brief.pdf"),
2378
+ ("mcp", "mcp", "fde mcp"),
2379
+ ("llm", "anthropic", "fde frame --reader llm (hosted)")):
2380
+ try:
2381
+ importlib.import_module(module)
2382
+ rows.append((f"extra [{extra}]", None, f"installed -- {why}"))
2383
+ except ImportError:
2384
+ rows.append((f"extra [{extra}]", None,
2385
+ f"not installed -- pip install 'fde-framework[{extra}]' for {why}"))
2386
+ for tool, why in (("docker", "fde implement --sandbox docker"),
2387
+ ("claude", "the default --agent-cmd of fde implement"),
2388
+ ("ruff", "the lint row of fde scorecard and the emitted project's "
2389
+ "own lint test")):
2390
+ found = shutil.which(tool)
2391
+ rows.append((tool, None, f"{found} -- {why}" if found
2392
+ else f"not on PATH -- {why} will say so when asked"))
2393
+ width = max(len(name) for name, _, _ in rows)
2394
+ failed = False
2395
+ for name, ok, detail in rows:
2396
+ mark = " --" if ok is None else (" ok" if ok else " NO")
2397
+ failed = failed or ok is False
2398
+ typer.echo(f"{mark} {name:{width}} {detail}")
2399
+ typer.echo("\nsomething the core needs is missing" if failed
2400
+ else "\nthe core can run; the -- lines are optional")
2401
+ if failed:
2402
+ raise typer.Exit(1)
2403
+
2404
+
2342
2405
  @app.command("mcp")
2343
2406
  def mcp_cmd(
2344
2407
  check: Annotated[bool, typer.Option(
2345
2408
  "--check", help="Build the server and list what it serves, without serving it."
2346
2409
  )] = False,
2410
+ schema: Annotated[bool, typer.Option(
2411
+ "--schema", help="Print the JSON Schema of the envelope beside every reply."
2412
+ )] = False,
2347
2413
  ) -> None:
2348
2414
  """Serve the framework's conversational commands to a coding agent over
2349
2415
  stdio (Model Context Protocol): triage, start, frame, status, next,
2350
2416
  stage, debt, stop_when, history. Every path the agent supplies must be
2351
2417
  inside the workspace (FDE_WORKSPACE, else the current directory). Add
2352
2418
  it with, for example, `claude mcp add fde -- fde mcp`."""
2419
+ if schema:
2420
+ from fde.mcp_server import ENVELOPE_SCHEMA
2421
+ typer.echo(json.dumps(ENVELOPE_SCHEMA, indent=2))
2422
+ return
2353
2423
  try:
2354
2424
  from fde.mcp_server import describe, serve
2355
2425
  if check:
@@ -30,10 +30,43 @@ from __future__ import annotations
30
30
 
31
31
  import os
32
32
  import re
33
+ import threading
33
34
  from pathlib import Path
34
35
 
35
36
  NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
36
37
 
38
+ # The envelope beside every reply, as the JSON Schema a client can validate
39
+ # against (`fde mcp --schema` prints it). Bump the version when a field
40
+ # changes meaning or goes away; adding a field keeps the version.
41
+ SCHEMA_VERSION = 1
42
+ STATUSES = ("ok", "blocked", "refused", "failed")
43
+ TOOLS = ("triage", "start", "frame", "status", "next", "stage", "debt", "stop_when", "history")
44
+ ENVELOPE_SCHEMA = {
45
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
46
+ "$id": f"https://github.com/atulkapoor/fde-framework/schemas/mcp-envelope/v{SCHEMA_VERSION}",
47
+ "title": f"fde MCP reply envelope, version {SCHEMA_VERSION}",
48
+ "description": "The structured content beside the text of every tool reply. The text "
49
+ "is what the command printed; the envelope is what an agent branches on "
50
+ "without parsing prose. It follows the command's exit, not its wording.",
51
+ "type": "object",
52
+ "properties": {
53
+ "schema_version": {"const": SCHEMA_VERSION,
54
+ "description": "Bumped when a field changes meaning or goes away."},
55
+ "tool": {"enum": list(TOOLS), "description": "The tool that answered."},
56
+ "status": {"enum": list(STATUSES),
57
+ "description": "ok: the command exited 0. blocked: it refused or a "
58
+ "condition triggered (exit 1); the text says which. "
59
+ "refused: a path or name was outside what the agent may "
60
+ "touch; nothing ran. failed: an exception; a bug to report."},
61
+ "mutated": {"type": "boolean",
62
+ "description": "True only when a writing tool ran and exited 0."},
63
+ "workspace": {"type": "string",
64
+ "description": "The directory every path was confined to."},
65
+ },
66
+ "required": ["schema_version", "tool", "status", "mutated", "workspace"],
67
+ "additionalProperties": False,
68
+ }
69
+
37
70
  THIRTY_SECONDS = """\
38
71
  You give it the problem as the client put it, the client's own examples,
39
72
  the constraints, a baseline someone measured, and where it has to run. It
@@ -54,10 +87,12 @@ brief into typed facts; `status` for the gates and what is still unknown;
54
87
  for where the engagement stands on its record; `debt` for what nobody has
55
88
  settled; `stop_when` for whether the evidence says stop; `history` for the
56
89
  whole record in order. Every tool returns the command's own output,
57
- refusals included. `start` and `frame` write to the record; the rest only
58
- read it. Every path must be inside the server's workspace. Nothing here
59
- builds, implements or deploys; those are run by a person on the command
60
- line.
90
+ refusals included, with a status beside it (ok, blocked, refused, failed)
91
+ to branch on instead of the prose. `start` and `frame` write to the
92
+ record; the rest only read it. An engagement is named as `start` named
93
+ it, or by its path; every path must be inside the server's workspace.
94
+ Nothing here builds, implements or deploys; those are run by a person on
95
+ the command line.
61
96
  """
62
97
 
63
98
 
@@ -86,6 +121,21 @@ def confine(path: str | None, what: str, root: Path | None = None) -> Path | Non
86
121
  return resolved
87
122
 
88
123
 
124
+ def locate(engagement: str) -> Path:
125
+ """The engagement as the command will see it. `start(name="acme")` puts
126
+ the record at engagements/acme; `status(engagement="acme")` a moment
127
+ later must find it, as `fde status acme` does on the command line. So a
128
+ bare name is looked for at <workspace>/<name> and then where `start`
129
+ puts it; both candidates are confined, so a name that climbs out is
130
+ refused before anything is looked for. A path is taken as given."""
131
+ path = confine(engagement, "engagement")
132
+ if path is not None and not (path / "facts").is_dir() and not Path(engagement).is_absolute():
133
+ under = confine(str(Path("engagements") / engagement), "engagement")
134
+ if under is not None and (under / "facts").is_dir():
135
+ return under
136
+ return path
137
+
138
+
89
139
  def plain_name(name: str) -> str:
90
140
  if not NAME.match(name or ""):
91
141
  raise Outside(f"an engagement name is a plain name (letters, digits, '.', '_', '-'), "
@@ -99,6 +149,16 @@ def _registry() -> str:
99
149
  return os.environ.get("FDE_REGISTRY") or str(default_root())
100
150
 
101
151
 
152
+ # One command at a time. The server runs each tool on a worker thread, and
153
+ # a coding agent will issue `status` and `next` in the same breath; the
154
+ # runner that captures a command's output does so by swapping the
155
+ # process's stdout, and two swaps interleaved left one command printing
156
+ # straight onto the JSON-RPC channel. The protocol broke, the client saw
157
+ # "Connection closed", and the record was not at fault. Commands take
158
+ # milliseconds; the record has one writer at a time, as it should.
159
+ _ONE_AT_A_TIME = threading.Lock()
160
+
161
+
102
162
  def run_command(args: list[str], registry: bool = True) -> tuple[str, str]:
103
163
  """One command, in-process, as the terminal would run it: the text it
104
164
  printed, and a status an agent can branch on without parsing prose --
@@ -109,7 +169,8 @@ def run_command(args: list[str], registry: bool = True) -> tuple[str, str]:
109
169
  from fde.cli import app
110
170
 
111
171
  argv = [*args, "--registry", _registry()] if registry else list(args)
112
- result = CliRunner().invoke(app, argv)
172
+ with _ONE_AT_A_TIME:
173
+ result = CliRunner().invoke(app, argv)
113
174
  text = (result.output or "").strip()
114
175
  if result.exception and not isinstance(result.exception, SystemExit):
115
176
  return (f"{text}\n\nfde: {type(result.exception).__name__}: {result.exception}".strip(),
@@ -128,8 +189,8 @@ def reply(tool: str, text: str, status: str, mutated: bool):
128
189
 
129
190
  return types.CallToolResult(
130
191
  content=[types.TextContent(type="text", text=text)],
131
- structured_content={"tool": tool, "status": status, "mutated": mutated,
132
- "workspace": str(workspace())},
192
+ structured_content={"schema_version": SCHEMA_VERSION, "tool": tool, "status": status,
193
+ "mutated": mutated, "workspace": str(workspace())},
133
194
  is_error=status == "failed",
134
195
  )
135
196
 
@@ -197,14 +258,14 @@ def build_server():
197
258
  """WRITES: reads a brief, pasted as text, into typed facts appended to
198
259
  the engagement's record, played back for correction with the
199
260
  follow-up questions. The engagement must be inside the workspace."""
200
- return run_command(["frame", str(confine(engagement, "engagement")), "--text", text])
261
+ return run_command(["frame", str(locate(engagement)), "--text", text])
201
262
 
202
263
  @server.tool(annotations=reads)
203
264
  @guarded
204
265
  def status(engagement: str):
205
266
  """The engagement's gates, what is known by scope and who said it, what
206
267
  is still missing, and which roles have not been heard."""
207
- return run_command(["status", str(confine(engagement, "engagement"))])
268
+ return run_command(["status", str(locate(engagement))])
208
269
 
209
270
  @server.tool(annotations=reads)
210
271
  @guarded
@@ -213,7 +274,7 @@ def build_server():
213
274
  and -- when it is a question -- the evidence already on record and
214
275
  the decisions that turn on each candidate answer. Reads only; the
215
276
  action it names is for a person to run."""
216
- return run_command(["next", str(confine(engagement, "engagement"))])
277
+ return run_command(["next", str(locate(engagement))])
217
278
 
218
279
  @server.tool(annotations=reads)
219
280
  @guarded
@@ -222,7 +283,7 @@ def build_server():
222
283
  validation, prototype, pilot, production, adoption, retrospective,
223
284
  or stopped -- with what would move it to the next stage. Reads only:
224
285
  a person runs `fde stage` to append the transition to the record."""
225
- args = ["stage", str(confine(engagement, "engagement")), "--no-record"]
286
+ args = ["stage", str(locate(engagement)), "--no-record"]
226
287
  target = confine(project, "project")
227
288
  return run_command(args + (["--project", str(target)] if target else []))
228
289
 
@@ -233,7 +294,7 @@ def build_server():
233
294
  settled -- failing gates, standing waivers, guessed or merely stated
234
295
  facts, disagreements, unsigned attestations, unheard roles, open
235
296
  incidents, undecided components -- each with an owner and an age."""
236
- return run_command(["debt", str(confine(engagement, "engagement"))])
297
+ return run_command(["debt", str(locate(engagement))])
237
298
 
238
299
  @server.tool(annotations=reads)
239
300
  @guarded
@@ -242,7 +303,7 @@ def build_server():
242
303
  """Judge the engagement's stop conditions against what the record has
243
304
  measured: the scorecard, the field journal, the recorded outcomes.
244
305
  Reads only; recording a condition is for a person."""
245
- args = ["stop-when", str(confine(engagement, "engagement"))]
306
+ args = ["stop-when", str(locate(engagement))]
246
307
  target = confine(project, "project")
247
308
  if target:
248
309
  args += ["--project", str(target)]
@@ -256,7 +317,7 @@ def build_server():
256
317
  def history(engagement: str):
257
318
  """Every dated entry on the engagement's record, in order, the undated
258
319
  ones above it, one line each."""
259
- return run_command(["history", str(confine(engagement, "engagement"))],
320
+ return run_command(["history", str(locate(engagement))],
260
321
  registry=False)
261
322
 
262
323
  @server.resource("fde://thirty-seconds", name="fde in thirty seconds",
@@ -279,6 +340,7 @@ def describe() -> str:
279
340
  else "reads"
280
341
  lines.append(f" {tool.name:10} {hint}")
281
342
  lines.append(" resources: fde://thirty-seconds")
343
+ lines.append(f" envelope: schema_version {SCHEMA_VERSION} (`fde mcp --schema`)")
282
344
  return "\n".join(lines)
283
345
 
284
346