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.
- {fde_framework-0.1.43 → fde_framework-0.1.45}/CHANGELOG.md +69 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/PKG-INFO +29 -14
- {fde_framework-0.1.43 → fde_framework-0.1.45}/README.md +28 -13
- {fde_framework-0.1.43 → fde_framework-0.1.45}/pyproject.toml +1 -1
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/cli.py +70 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/mcp_server.py +76 -14
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_mcp.py +132 -0
- fde_framework-0.1.45/tests/test_mcp_stdio.py +119 -0
- fde_framework-0.1.45/tests/test_release_consistency.py +38 -0
- fde_framework-0.1.43/tests/test_release_consistency.py +0 -18
- {fde_framework-0.1.43 → fde_framework-0.1.45}/.gitignore +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/LICENSE +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/examples/invoice-extraction/README.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/examples/policy-qa/README.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/examples/support-triage/README.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/ansible-playbook.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/assisted-deterministic.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/audit-only.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/boundary-and-audit.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/cascade.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/classical-ml.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/compose.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/decision-log.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/deterministic-masking.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/deterministic.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/direct-call.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/episodic-store.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/explainability-record.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/field-match.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/finetune.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/fixed-sequence.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/gitops.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/governed-tools.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/graph-expanded-retrieval.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/graph-retrieval.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/hybrid-search.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/judged.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/keyword-search.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/kubernetes-manifests.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/labelled-decision.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/labelled-metrics.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/llm-extraction.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/llm-scrubbing.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/llm.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/local-embedding.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/managed-api.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/managed-embedding.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/manual-runbook.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/model-planner.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/ocr-pipeline.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/optimisation-reasoning.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/optimisation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/passthrough.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/reranked-retrieval.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/role-scoped-authority.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/segmentation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/self-hosted.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/serverless-gpu.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/speech-transcription.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/structured-logs.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/systemd-unit.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/terraform-module.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/text-extraction.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/traced.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/vector-search.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/video-ingestion.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/windowed-ingestion.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/approaches/working-state.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/cases/churn-scoring.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/cases/route-planning.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/cases/structured-extraction.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/cases/studio-style.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/accountability.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/deployment.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/embedding.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/evaluation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/governance.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/integration.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/memory.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/observability.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/perception.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/planning.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/provisioning.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/reasoning.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/redaction.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/representation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/retrieval.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/components/serving.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/accelerator.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/access_model.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/arrival_rate.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/availability_target.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/cheap_path_coverage.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/confidence_calibrated.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/container_competence.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/corpus_churn.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/corpus_size.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/data_residency.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/environment_lifetime.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/existing_cluster.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/existing_iac_tool.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/external_systems.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/hosting.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/human_waiting.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/input_format.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/interpretability_required.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/labelled_count.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/latency_budget_ms.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/licence_posture.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/operates_after_handover.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/output_shape.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/provisioning_api.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/query_pattern.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/recall_span.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/dimensions/sensitivity_present.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Generator.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Guard.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Mapper.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/ModelServer.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Parser.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Planner.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Retriever.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Scorer.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Store.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/ToolBoundary.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/interfaces/Tracer.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/locales/eu-gdpr.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/locales/in-dpdp.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/ansible-playbook.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/assisted-deterministic.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/audit-only.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/boundary-and-audit.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/cascade-reasoning.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/cascade-representation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/cascade-retrieval.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/classical-ml-reasoning.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/classical-ml.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/compose.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/decision-log.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/deterministic-masking.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/deterministic.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/direct-call.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/episodic-store.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/explainability-record.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/field-match.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/finetune-representation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/finetune.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/fixed-sequence.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/gitops.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/governed-tools.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/graph-expanded-retrieval.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/graph-retrieval.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/hybrid-search.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/judged.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/keyword-search.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/kubernetes-manifests.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/labelled-decision.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/labelled-metrics.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/llm-representation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/llm-scrubbing.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/llm.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/local-embedding.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/managed-api.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/managed-embedding.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/manual-runbook.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/model-planner.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/ocr-pipeline.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/optimisation-reasoning.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/optimisation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/passthrough.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/reranked-retrieval.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/role-scoped-authority.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/segmentation.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/self-hosted.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/serverless-gpu.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/speech-transcription.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/structured-logs.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/systemd-unit.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/terraform-module.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/text-extraction.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/traced.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/vector-search.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/video-ingestion.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/windowed-ingestion.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/patterns/working-state.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/langgraph.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/llamaindex.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/local-judge.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/mcp.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/ollama.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/openai-judge.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/opentelemetry.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/ortools.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/peft.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/pgvector.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/plain-python.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/qdrant.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/supermemory.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/tesseract.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/vllm.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/whisper.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/stacks/xgboost.md +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/accountability/decision-log.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/accountability/explainability-record.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/deployment/compose.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/deployment/kubernetes-manifests.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/deployment/systemd-unit.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/embedding/local-embedding.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/embedding/managed-embedding.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/field-match.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/judged.local-judge.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/judged.openai-judge.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/judged.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/labelled-metrics.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/evaluation/labelled-metrics.xgboost.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/governance/audit-only.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/governance/boundary-and-audit.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/governance/role-scoped-authority.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/integration/direct-call.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/integration/governed-tools.mcp.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/integration/governed-tools.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/memory/episodic-store.pgvector.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/memory/episodic-store.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/memory/episodic-store.supermemory.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/memory/working-state.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/observability/structured-logs.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/observability/traced.opentelemetry.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/observability/traced.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/ocr-pipeline.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/ocr-pipeline.tesseract.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/passthrough.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/speech-transcription.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/speech-transcription.whisper.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/text-extraction.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/video-ingestion.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/perception/windowed-ingestion.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/fixed-sequence.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/model-planner.langgraph.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/model-planner.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/optimisation.ortools.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/planning/optimisation.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/provisioning/ansible-playbook.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/provisioning/gitops.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/provisioning/manual-runbook.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/provisioning/terraform-module.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/classical-ml.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/classical-ml.xgboost.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/finetune.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/labelled-decision.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/llm.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/optimisation.ortools.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/reasoning/optimisation.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/redaction/deterministic-masking.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/redaction/llm-scrubbing.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/assisted.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/classical-ml.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/classical-ml.xgboost.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/deterministic.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/finetune.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/llm.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/representation/segmentation.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-expanded-retrieval.pgvector.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-expanded-retrieval.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-expanded-retrieval.qdrant.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-retrieval.pgvector.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-retrieval.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/graph-retrieval.qdrant.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/hybrid-search.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/keyword-search.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/reranked-retrieval.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/vector-search.llamaindex.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/vector-search.pgvector.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/vector-search.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/retrieval/vector-search.qdrant.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/managed-api.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/self-hosted.ollama.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/self-hosted.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/self-hosted.vllm.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/framework/templates/serving/serverless-gpu.plain.py.j2 +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/__init__.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/architect.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/bench.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/costing.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/debt.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/decide.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/decompose.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/deploy.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/drift.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/emit.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/evolution.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/experiment.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/factlog.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/forecast.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/gates.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/graph.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/history.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/impact.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/implement.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/importer.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/__init__.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/answers.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/documents.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/interview.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/llm_reader.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/prose.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/intake/samples.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/lifecycle.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/__init__.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/base.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/fact.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/profile.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/respondent.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/models/schema.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/moves.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/ops.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/predicate.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/realization.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/registry.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/sandbox.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/scan.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/scorecard.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/space.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/stakeholders.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/stop.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/training.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/value.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/src/fde/workflow.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/__init__.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/conftest.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/models/__init__.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/models/test_profile.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_acceptance.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_access_and_sensitivity.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_answers.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_audit_fixes.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_bench.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_build_receipt.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cascade.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_engagement.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_evolution.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_gates.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_intake.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_kb.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_robustness.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_cli_samples.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_costing.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_coverage.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_dead_zones.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_debt.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_decide.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_decompose.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_deploy.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_documents.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_drift.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_embedding.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_emit.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_evolution.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_example_transcript.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_experiment.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_factlog.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_finetune.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_finetune_real.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_forecast.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_gates.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_history.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_impact.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_implement.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_importer.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_interview.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_lifecycle.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_llm_reader.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_locales.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_memory_retrieval.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_more_templates.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_moves.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_multimodal.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_next.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_ops.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_outcome_contract.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_package.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_prose.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_realization.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_registry.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_remaining.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_retrieval_eval.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_reversibility.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_samples.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_sandbox.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_sanitisation.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_scan.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_schema.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_scorecard.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_serving.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_shipped_registry.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_space.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_stack_swap.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_stakeholders.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_stop.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_supermemory.py +0 -0
- {fde_framework-0.1.43 → fde_framework-0.1.45}/tests/test_templates.py +0 -0
- {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.
|
|
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),
|
|
158
|
-
workspace. The envelope follows the command's exit,
|
|
159
|
-
its text.
|
|
160
|
-
|
|
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.
|
|
165
|
-
|
|
166
|
-
|
|
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,
|
|
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.
|
|
296
|
-
|
|
297
|
-
|
|
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 #
|
|
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),
|
|
114
|
-
workspace. The envelope follows the command's exit,
|
|
115
|
-
its text.
|
|
116
|
-
|
|
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.
|
|
121
|
-
|
|
122
|
-
|
|
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,
|
|
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.
|
|
252
|
-
|
|
253
|
-
|
|
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 #
|
|
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.
|
|
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
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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={"
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|