fde-framework 0.1.1__tar.gz → 0.1.2__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.1 → fde_framework-0.1.2}/CHANGELOG.md +20 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/PKG-INFO +86 -17
- {fde_framework-0.1.1 → fde_framework-0.1.2}/README.md +85 -16
- {fde_framework-0.1.1 → fde_framework-0.1.2}/pyproject.toml +1 -1
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/cli.py +113 -54
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/emit.py +34 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/gates.py +17 -10
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/implement.py +15 -4
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/registry.py +22 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_emit.py +9 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_sanitisation.py +11 -3
- {fde_framework-0.1.1 → fde_framework-0.1.2}/.gitignore +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/LICENSE +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/examples/invoice-extraction/README.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/ansible-playbook.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/assisted-deterministic.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/audit-only.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/boundary-and-audit.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/cascade.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/classical-ml.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/compose.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/decision-log.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/deterministic-masking.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/deterministic.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/direct-call.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/episodic-store.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/explainability-record.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/field-match.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/finetune.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/fixed-sequence.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/gitops.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/governed-tools.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/graph-retrieval.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/judged.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/keyword-search.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/kubernetes-manifests.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/labelled-metrics.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/llm-extraction.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/llm-scrubbing.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/llm.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/local-embedding.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/managed-api.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/managed-embedding.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/manual-runbook.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/model-planner.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/ocr-pipeline.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/optimisation-reasoning.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/optimisation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/passthrough.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/role-scoped-authority.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/segmentation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/self-hosted.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/serverless-gpu.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/speech-transcription.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/structured-logs.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/systemd-unit.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/terraform-module.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/text-extraction.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/traced.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/vector-search.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/video-ingestion.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/windowed-ingestion.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/approaches/working-state.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/cases/churn-scoring.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/cases/route-planning.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/cases/structured-extraction.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/cases/studio-style.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/accountability.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/deployment.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/embedding.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/evaluation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/governance.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/integration.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/memory.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/observability.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/perception.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/planning.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/provisioning.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/reasoning.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/redaction.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/representation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/retrieval.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/components/serving.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/accelerator.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/access_model.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/arrival_rate.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/availability_target.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/cheap_path_coverage.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/confidence_calibrated.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/container_competence.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/corpus_size.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/data_residency.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/environment_lifetime.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/existing_cluster.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/existing_iac_tool.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/external_systems.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/hosting.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/human_waiting.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/input_format.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/interpretability_required.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/labelled_count.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/latency_budget_ms.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/licence_posture.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/operates_after_handover.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/output_shape.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/provisioning_api.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/query_pattern.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/recall_span.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/dimensions/sensitivity_present.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Generator.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Guard.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Mapper.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/ModelServer.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Parser.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Planner.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Retriever.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Scorer.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Store.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/ToolBoundary.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/interfaces/Tracer.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/locales/eu-gdpr.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/locales/in-dpdp.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/ansible-playbook.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/assisted-deterministic.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/audit-only.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/boundary-and-audit.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/cascade-reasoning.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/cascade-representation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/cascade-retrieval.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/classical-ml-reasoning.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/classical-ml.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/compose.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/decision-log.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/deterministic-masking.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/deterministic.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/direct-call.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/episodic-store.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/explainability-record.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/field-match.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/finetune-representation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/finetune.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/fixed-sequence.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/gitops.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/governed-tools.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/graph-retrieval.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/judged.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/keyword-search.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/kubernetes-manifests.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/labelled-metrics.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/llm-representation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/llm-scrubbing.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/llm.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/local-embedding.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/managed-api.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/managed-embedding.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/manual-runbook.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/model-planner.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/ocr-pipeline.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/optimisation-reasoning.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/optimisation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/passthrough.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/role-scoped-authority.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/segmentation.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/self-hosted.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/serverless-gpu.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/speech-transcription.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/structured-logs.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/systemd-unit.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/terraform-module.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/text-extraction.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/traced.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/vector-search.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/video-ingestion.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/windowed-ingestion.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/patterns/working-state.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/langgraph.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/local-judge.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/mcp.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/ollama.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/openai-judge.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/opentelemetry.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/ortools.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/pgvector.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/plain-python.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/qdrant.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/tesseract.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/vllm.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/whisper.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/stacks/xgboost.md +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/accountability/decision-log.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/accountability/explainability-record.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/deployment/compose.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/deployment/kubernetes-manifests.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/deployment/systemd-unit.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/embedding/local-embedding.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/embedding/managed-embedding.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/evaluation/field-match.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/evaluation/judged.local-judge.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/evaluation/judged.openai-judge.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/evaluation/judged.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/evaluation/labelled-metrics.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/evaluation/labelled-metrics.xgboost.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/governance/audit-only.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/governance/boundary-and-audit.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/governance/role-scoped-authority.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/integration/direct-call.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/integration/governed-tools.mcp.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/integration/governed-tools.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/memory/episodic-store.pgvector.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/memory/episodic-store.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/memory/working-state.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/observability/structured-logs.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/observability/traced.opentelemetry.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/observability/traced.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/perception/ocr-pipeline.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/perception/ocr-pipeline.tesseract.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/perception/passthrough.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/perception/speech-transcription.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/perception/speech-transcription.whisper.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/perception/text-extraction.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/perception/video-ingestion.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/perception/windowed-ingestion.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/planning/fixed-sequence.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/planning/model-planner.langgraph.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/planning/model-planner.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/planning/optimisation.ortools.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/planning/optimisation.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/provisioning/ansible-playbook.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/provisioning/gitops.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/provisioning/manual-runbook.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/provisioning/terraform-module.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/reasoning/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/reasoning/classical-ml.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/reasoning/classical-ml.xgboost.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/reasoning/finetune.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/reasoning/llm.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/reasoning/optimisation.ortools.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/reasoning/optimisation.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/redaction/deterministic-masking.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/redaction/llm-scrubbing.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/representation/assisted.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/representation/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/representation/classical-ml.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/representation/classical-ml.xgboost.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/representation/deterministic.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/representation/finetune.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/representation/llm.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/representation/segmentation.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/retrieval/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/retrieval/graph-retrieval.pgvector.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/retrieval/graph-retrieval.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/retrieval/graph-retrieval.qdrant.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/retrieval/keyword-search.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/retrieval/vector-search.pgvector.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/retrieval/vector-search.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/retrieval/vector-search.qdrant.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/serving/managed-api.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/serving/self-hosted.ollama.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/serving/self-hosted.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/serving/self-hosted.vllm.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/framework/templates/serving/serverless-gpu.plain.py.j2 +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/__init__.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/architect.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/costing.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/decide.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/decompose.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/deploy.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/evolution.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/factlog.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/graph.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/intake/__init__.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/intake/answers.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/intake/documents.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/intake/interview.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/intake/llm_reader.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/intake/prose.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/intake/samples.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/models/__init__.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/models/base.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/models/fact.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/models/profile.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/models/respondent.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/models/schema.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/moves.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/ops.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/predicate.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/realization.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/scan.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/space.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/src/fde/workflow.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/__init__.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/conftest.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/models/__init__.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/models/test_profile.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_access_and_sensitivity.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_answers.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_cascade.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_cli_engagement.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_cli_evolution.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_cli_gates.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_cli_intake.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_cli_kb.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_cli_robustness.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_cli_samples.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_costing.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_coverage.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_dead_zones.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_decide.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_decompose.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_deploy.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_documents.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_embedding.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_evolution.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_factlog.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_gates.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_implement.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_interview.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_llm_reader.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_locales.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_memory_retrieval.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_more_templates.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_moves.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_multimodal.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_ops.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_package.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_prose.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_realization.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_registry.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_remaining.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_reversibility.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_samples.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_scan.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_schema.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_serving.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_shipped_registry.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_space.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_stack_swap.py +0 -0
- {fde_framework-0.1.1 → fde_framework-0.1.2}/tests/test_templates.py +0 -0
|
@@ -5,6 +5,26 @@ 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.2] — 2026-09-07
|
|
9
|
+
|
|
10
|
+
A fresh-eyes usability round, every finding verified in a clean venv:
|
|
11
|
+
|
|
12
|
+
- The hero quickstart runs verbatim: bare engagement names resolve
|
|
13
|
+
(`fde ask acme` finds `engagements/acme`), and `fde start` reads its
|
|
14
|
+
own statement through the prose reader, so the first minute shows
|
|
15
|
+
typed facts instead of "no facts recorded yet".
|
|
16
|
+
- `fde --version`; interview questions show their legal values with the
|
|
17
|
+
question; gate remedies name their clearing commands; the baseline
|
|
18
|
+
refusal names the exact keys that satisfy it.
|
|
19
|
+
- `fde implement` without an agent on PATH is a sentence with the fix,
|
|
20
|
+
never a traceback; `fde cost --price-per-seat` works without fleet
|
|
21
|
+
flags.
|
|
22
|
+
- kb subcommands take `--registry` (with `--root` kept as an alias).
|
|
23
|
+
- Every emitted project gets a front-door README; images are PNG so
|
|
24
|
+
they render on PyPI and mobile; the README gains a copy-paste full
|
|
25
|
+
lifecycle with the baseline YAML inline, and a Python API section
|
|
26
|
+
backed by the public `fde.registry.default_root()`.
|
|
27
|
+
|
|
8
28
|
## [0.1.1] — 2026-09-06
|
|
9
29
|
|
|
10
30
|
- README images render everywhere: absolute URLs, because PyPI does not
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fde-framework
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: A framework for Forward Deployed Engineers: from problem statement to a deployable AI project, every decision traced to a fact.
|
|
5
5
|
Project-URL: Homepage, https://github.com/atulkapoor/fde-framework
|
|
6
6
|
Project-URL: Repository, https://github.com/atulkapoor/fde-framework
|
|
@@ -36,8 +36,8 @@ Requires-Dist: anthropic>=0.40; extra == 'llm'
|
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
37
37
|
|
|
38
38
|
<picture>
|
|
39
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/banner-dark.
|
|
40
|
-
<img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/banner-light.
|
|
39
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/banner-dark.png">
|
|
40
|
+
<img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/banner-light.png" alt="fde — a framework for Forward Deployed Engineers" width="760">
|
|
41
41
|
</picture>
|
|
42
42
|
|
|
43
43
|
# fde — a framework for Forward Deployed Engineers
|
|
@@ -49,6 +49,15 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|

|
|
50
50
|
[](LICENSE)
|
|
51
51
|
|
|
52
|
+
<p>
|
|
53
|
+
<a href="#install">Install</a> ·
|
|
54
|
+
<a href="#try-it-in-two-minutes">Quickstart</a> ·
|
|
55
|
+
<a href="ARCHITECTURE.md">Architecture</a> ·
|
|
56
|
+
<a href="examples/invoice-extraction/">Worked example</a> ·
|
|
57
|
+
<a href="CONTRIBUTING.md">Contributing</a> ·
|
|
58
|
+
<a href="https://pypi.org/project/fde-framework/">PyPI</a>
|
|
59
|
+
</p>
|
|
60
|
+
|
|
52
61
|
**fde** is an open-source framework for Forward Deployed Engineers: it takes a
|
|
53
62
|
client engagement from a problem statement to a runnable, deployable AI
|
|
54
63
|
project — with every decision traced to a fact, and every fact traced to a
|
|
@@ -68,7 +77,7 @@ fde architect acme # the design, with cited rationale
|
|
|
68
77
|
fde build acme --out project # code + evals + deploy assets + runbook
|
|
69
78
|
```
|
|
70
79
|
|
|
71
|
-
<img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/demo.
|
|
80
|
+
<img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/demo.png" alt="fde in a terminal: a paragraph becomes typed facts, an architecture with a fingerprint, and a build that refuses until the hard gate passes" width="780">
|
|
72
81
|
|
|
73
82
|
*That refusal at the end is the product working: no baseline, no verified data
|
|
74
83
|
access — no build. The remedies ship with every gate.*
|
|
@@ -232,18 +241,18 @@ refuses by name and says exactly what to install.
|
|
|
232
241
|
## Try it
|
|
233
242
|
|
|
234
243
|
```bash
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
fde start acme --statement "Extract fields from supplier invoices."
|
|
245
|
+
fde ask engagements/acme --role admin # role-scoped interview
|
|
246
|
+
fde status engagements/acme # gates, gaps, disagreements
|
|
247
|
+
fde architect engagements/acme # the design, with rationale
|
|
248
|
+
fde build engagements/acme --out project # refuses until gates clear
|
|
249
|
+
|
|
250
|
+
fde scan engagements/acme # what this hardware runs
|
|
251
|
+
fde cost --requests-per-day 500000 --model-b 70 # dated fleet sizing
|
|
252
|
+
|
|
253
|
+
fde kb validate # parse and cross-link the registry
|
|
254
|
+
fde kb gaps --root framework # what the corpus is missing
|
|
255
|
+
fde kb sweep --root framework # profiles no approach can serve
|
|
247
256
|
.venv/bin/pytest -q
|
|
248
257
|
```
|
|
249
258
|
|
|
@@ -276,6 +285,66 @@ project/
|
|
|
276
285
|
└── COMPLIANCE.md # jurisdiction obligations, when a locale pack was applied
|
|
277
286
|
```
|
|
278
287
|
|
|
288
|
+
## The full lifecycle, copy-paste
|
|
289
|
+
|
|
290
|
+
Everything below runs from an empty directory after `pip install fde-framework`:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
fde start acme --statement "Extract fields from scanned supplier invoices; \
|
|
294
|
+
data cannot leave; 200,000 documents, 8,000 verified; a person is waiting."
|
|
295
|
+
# plays back the typed facts it read, and the three questions worth asking next
|
|
296
|
+
|
|
297
|
+
fde ask acme --role eval_owner # answer what discovery still needs
|
|
298
|
+
fde status acme # facts by scope, gates, % settled
|
|
299
|
+
|
|
300
|
+
cat > baseline.yaml <<'YAML'
|
|
301
|
+
volume: {value: 20000, unit: docs/month, definition: invoices received by AP}
|
|
302
|
+
cycle_time_per_unit_seconds: {value: 300, unit: s, definition: arrival to posted}
|
|
303
|
+
labour_hours_per_week: {value: 35, unit: h/week, definition: AP team keying time}
|
|
304
|
+
rework_rate: {value: 0.1, unit: ratio, definition: entries corrected after post}
|
|
305
|
+
exception_rate: {value: 0.07, unit: ratio, definition: routed to a human queue}
|
|
306
|
+
error_rate: {value: 0.04, unit: ratio, definition: wrong amount or vendor posted}
|
|
307
|
+
business_metric: {value: 9, unit: days, definition: mean days payable outstanding}
|
|
308
|
+
sampled: {n: 40, method: random invoices across two months}
|
|
309
|
+
YAML
|
|
310
|
+
fde baseline acme --file baseline.yaml
|
|
311
|
+
fde data-access acme --note "read replica returned 14 real rows"
|
|
312
|
+
fde security-review acme --note "client infosec walked the data paths"
|
|
313
|
+
fde ask acme --role eval_owner # or: fde waive acme client_readiness --reason "..."
|
|
314
|
+
|
|
315
|
+
fde build acme --out project # refuses until the gates truly pass
|
|
316
|
+
python project/evals/harness.py # red until implemented -- that's the exam
|
|
317
|
+
fde implement project # drive a coding agent until it's green
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Python API
|
|
321
|
+
|
|
322
|
+
The CLI is a thin layer; everything is importable. The registry loads from the
|
|
323
|
+
installed wheel, so this runs anywhere:
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
from fde.architect import architect
|
|
327
|
+
from fde.intake.prose import parse_prose
|
|
328
|
+
from fde.models.profile import Profile
|
|
329
|
+
from fde.registry import default_root, load_registry
|
|
330
|
+
|
|
331
|
+
registry = load_registry(default_root())
|
|
332
|
+
|
|
333
|
+
profile = Profile()
|
|
334
|
+
profile.ingest(parse_prose(
|
|
335
|
+
"500,000 scanned invoices; data cannot leave; 10,000 verified; "
|
|
336
|
+
"a person is waiting; structured records out.", registry))
|
|
337
|
+
|
|
338
|
+
design = architect(profile, registry)
|
|
339
|
+
print(design.topology) # customer-vpc
|
|
340
|
+
for component, decision in sorted(design.decisions.items()):
|
|
341
|
+
if decision.approach:
|
|
342
|
+
print(component, decision.approach, decision.rationale)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Every decision object carries its rationale and its rejected alternatives —
|
|
346
|
+
the same receipts the emitted `ARCHITECTURE.md` prints.
|
|
347
|
+
|
|
279
348
|
## Common commands
|
|
280
349
|
|
|
281
350
|
| | |
|
|
@@ -429,7 +498,7 @@ The registry is the shared asset; engagements are private working state.
|
|
|
429
498
|
deliberately waits for a corpus of measured retrospectives rather than
|
|
430
499
|
pretending a handful is evidence.
|
|
431
500
|
- **More locale packs and stacks** — both are data; contributions enter
|
|
432
|
-
against [CONTRIBUTING.md](CONTRIBUTING.md)'s contract.
|
|
501
|
+
against [CONTRIBUTING.md](CONTRIBUTING.md)'s contract (and the [code of conduct](CODE_OF_CONDUCT.md)).
|
|
433
502
|
- **Language, channel, and device axes** — six of twenty industry test
|
|
434
503
|
statements named regional languages, low bandwidth, or basic devices;
|
|
435
504
|
the honest wiring (per-language evaluation, SMS/IVR serving approaches,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<picture>
|
|
2
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/banner-dark.
|
|
3
|
-
<img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/banner-light.
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/banner-dark.png">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/banner-light.png" alt="fde — a framework for Forward Deployed Engineers" width="760">
|
|
4
4
|
</picture>
|
|
5
5
|
|
|
6
6
|
# fde — a framework for Forward Deployed Engineers
|
|
@@ -12,6 +12,15 @@
|
|
|
12
12
|

|
|
13
13
|
[](LICENSE)
|
|
14
14
|
|
|
15
|
+
<p>
|
|
16
|
+
<a href="#install">Install</a> ·
|
|
17
|
+
<a href="#try-it-in-two-minutes">Quickstart</a> ·
|
|
18
|
+
<a href="ARCHITECTURE.md">Architecture</a> ·
|
|
19
|
+
<a href="examples/invoice-extraction/">Worked example</a> ·
|
|
20
|
+
<a href="CONTRIBUTING.md">Contributing</a> ·
|
|
21
|
+
<a href="https://pypi.org/project/fde-framework/">PyPI</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
15
24
|
**fde** is an open-source framework for Forward Deployed Engineers: it takes a
|
|
16
25
|
client engagement from a problem statement to a runnable, deployable AI
|
|
17
26
|
project — with every decision traced to a fact, and every fact traced to a
|
|
@@ -31,7 +40,7 @@ fde architect acme # the design, with cited rationale
|
|
|
31
40
|
fde build acme --out project # code + evals + deploy assets + runbook
|
|
32
41
|
```
|
|
33
42
|
|
|
34
|
-
<img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/demo.
|
|
43
|
+
<img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/demo.png" alt="fde in a terminal: a paragraph becomes typed facts, an architecture with a fingerprint, and a build that refuses until the hard gate passes" width="780">
|
|
35
44
|
|
|
36
45
|
*That refusal at the end is the product working: no baseline, no verified data
|
|
37
46
|
access — no build. The remedies ship with every gate.*
|
|
@@ -195,18 +204,18 @@ refuses by name and says exactly what to install.
|
|
|
195
204
|
## Try it
|
|
196
205
|
|
|
197
206
|
```bash
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
fde start acme --statement "Extract fields from supplier invoices."
|
|
208
|
+
fde ask engagements/acme --role admin # role-scoped interview
|
|
209
|
+
fde status engagements/acme # gates, gaps, disagreements
|
|
210
|
+
fde architect engagements/acme # the design, with rationale
|
|
211
|
+
fde build engagements/acme --out project # refuses until gates clear
|
|
212
|
+
|
|
213
|
+
fde scan engagements/acme # what this hardware runs
|
|
214
|
+
fde cost --requests-per-day 500000 --model-b 70 # dated fleet sizing
|
|
215
|
+
|
|
216
|
+
fde kb validate # parse and cross-link the registry
|
|
217
|
+
fde kb gaps --root framework # what the corpus is missing
|
|
218
|
+
fde kb sweep --root framework # profiles no approach can serve
|
|
210
219
|
.venv/bin/pytest -q
|
|
211
220
|
```
|
|
212
221
|
|
|
@@ -239,6 +248,66 @@ project/
|
|
|
239
248
|
└── COMPLIANCE.md # jurisdiction obligations, when a locale pack was applied
|
|
240
249
|
```
|
|
241
250
|
|
|
251
|
+
## The full lifecycle, copy-paste
|
|
252
|
+
|
|
253
|
+
Everything below runs from an empty directory after `pip install fde-framework`:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
fde start acme --statement "Extract fields from scanned supplier invoices; \
|
|
257
|
+
data cannot leave; 200,000 documents, 8,000 verified; a person is waiting."
|
|
258
|
+
# plays back the typed facts it read, and the three questions worth asking next
|
|
259
|
+
|
|
260
|
+
fde ask acme --role eval_owner # answer what discovery still needs
|
|
261
|
+
fde status acme # facts by scope, gates, % settled
|
|
262
|
+
|
|
263
|
+
cat > baseline.yaml <<'YAML'
|
|
264
|
+
volume: {value: 20000, unit: docs/month, definition: invoices received by AP}
|
|
265
|
+
cycle_time_per_unit_seconds: {value: 300, unit: s, definition: arrival to posted}
|
|
266
|
+
labour_hours_per_week: {value: 35, unit: h/week, definition: AP team keying time}
|
|
267
|
+
rework_rate: {value: 0.1, unit: ratio, definition: entries corrected after post}
|
|
268
|
+
exception_rate: {value: 0.07, unit: ratio, definition: routed to a human queue}
|
|
269
|
+
error_rate: {value: 0.04, unit: ratio, definition: wrong amount or vendor posted}
|
|
270
|
+
business_metric: {value: 9, unit: days, definition: mean days payable outstanding}
|
|
271
|
+
sampled: {n: 40, method: random invoices across two months}
|
|
272
|
+
YAML
|
|
273
|
+
fde baseline acme --file baseline.yaml
|
|
274
|
+
fde data-access acme --note "read replica returned 14 real rows"
|
|
275
|
+
fde security-review acme --note "client infosec walked the data paths"
|
|
276
|
+
fde ask acme --role eval_owner # or: fde waive acme client_readiness --reason "..."
|
|
277
|
+
|
|
278
|
+
fde build acme --out project # refuses until the gates truly pass
|
|
279
|
+
python project/evals/harness.py # red until implemented -- that's the exam
|
|
280
|
+
fde implement project # drive a coding agent until it's green
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Python API
|
|
284
|
+
|
|
285
|
+
The CLI is a thin layer; everything is importable. The registry loads from the
|
|
286
|
+
installed wheel, so this runs anywhere:
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from fde.architect import architect
|
|
290
|
+
from fde.intake.prose import parse_prose
|
|
291
|
+
from fde.models.profile import Profile
|
|
292
|
+
from fde.registry import default_root, load_registry
|
|
293
|
+
|
|
294
|
+
registry = load_registry(default_root())
|
|
295
|
+
|
|
296
|
+
profile = Profile()
|
|
297
|
+
profile.ingest(parse_prose(
|
|
298
|
+
"500,000 scanned invoices; data cannot leave; 10,000 verified; "
|
|
299
|
+
"a person is waiting; structured records out.", registry))
|
|
300
|
+
|
|
301
|
+
design = architect(profile, registry)
|
|
302
|
+
print(design.topology) # customer-vpc
|
|
303
|
+
for component, decision in sorted(design.decisions.items()):
|
|
304
|
+
if decision.approach:
|
|
305
|
+
print(component, decision.approach, decision.rationale)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Every decision object carries its rationale and its rejected alternatives —
|
|
309
|
+
the same receipts the emitted `ARCHITECTURE.md` prints.
|
|
310
|
+
|
|
242
311
|
## Common commands
|
|
243
312
|
|
|
244
313
|
| | |
|
|
@@ -392,7 +461,7 @@ The registry is the shared asset; engagements are private working state.
|
|
|
392
461
|
deliberately waits for a corpus of measured retrospectives rather than
|
|
393
462
|
pretending a handful is evidence.
|
|
394
463
|
- **More locale packs and stacks** — both are data; contributions enter
|
|
395
|
-
against [CONTRIBUTING.md](CONTRIBUTING.md)'s contract.
|
|
464
|
+
against [CONTRIBUTING.md](CONTRIBUTING.md)'s contract (and the [code of conduct](CODE_OF_CONDUCT.md)).
|
|
396
465
|
- **Language, channel, and device axes** — six of twenty industry test
|
|
397
466
|
statements named regional languages, low bandwidth, or basic devices;
|
|
398
467
|
the honest wiring (per-language evaluation, SMS/IVR serving approaches,
|
|
@@ -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.2"
|
|
6
6
|
description = "A framework for Forward Deployed Engineers: from problem statement to a deployable AI project, every decision traced to a fact."
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
license = {text = "Apache-2.0"}
|
|
@@ -47,7 +47,7 @@ from fde.models.fact import Fact
|
|
|
47
47
|
from fde.models.profile import Profile
|
|
48
48
|
from fde.models.respondent import Respondent, Role
|
|
49
49
|
from fde.predicate import PredicateError, holds
|
|
50
|
-
from fde.registry import KINDS, RegistryError, is_empty, load_registry
|
|
50
|
+
from fde.registry import KINDS, RegistryError, default_root, is_empty, load_registry
|
|
51
51
|
from fde.scan import (
|
|
52
52
|
GPU,
|
|
53
53
|
Hardware,
|
|
@@ -60,32 +60,30 @@ from fde.scan import (
|
|
|
60
60
|
from fde.space import Contradiction, Space
|
|
61
61
|
|
|
62
62
|
app = typer.Typer(help="Take an engagement from problem statement to a runnable project.")
|
|
63
|
-
kb = typer.Typer(help="Inspect the knowledge base in framework/.")
|
|
64
|
-
app.add_typer(kb, name="kb")
|
|
65
|
-
|
|
66
|
-
def _default_registry_root() -> Path:
|
|
67
|
-
"""A checkout's ./framework when present, else the copy in the wheel.
|
|
68
63
|
|
|
69
|
-
Local first, always: a contributor editing the corpus must see their
|
|
70
|
-
edits, not the packaged snapshot. The packaged copy is what makes
|
|
71
|
-
`pip install fde-framework` a working tool rather than a tool with no
|
|
72
|
-
knowledge base.
|
|
73
|
-
"""
|
|
74
|
-
local = Path("framework")
|
|
75
|
-
if local.is_dir():
|
|
76
|
-
return local
|
|
77
|
-
try:
|
|
78
|
-
from importlib.resources import files
|
|
79
64
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
65
|
+
@app.callback(invoke_without_command=True)
|
|
66
|
+
def _main(
|
|
67
|
+
ctx: typer.Context,
|
|
68
|
+
version: Annotated[bool, typer.Option(
|
|
69
|
+
"--version", help="Print the version and exit."
|
|
70
|
+
)] = False,
|
|
71
|
+
) -> None:
|
|
72
|
+
if version:
|
|
73
|
+
from importlib.metadata import version as pkg_version
|
|
86
74
|
|
|
75
|
+
try:
|
|
76
|
+
typer.echo(f"fde-framework {pkg_version('fde-framework')}")
|
|
77
|
+
except Exception: # noqa: BLE001 - source checkout without install metadata
|
|
78
|
+
typer.echo("fde-framework (source checkout)")
|
|
79
|
+
raise typer.Exit(0)
|
|
80
|
+
if ctx.invoked_subcommand is None:
|
|
81
|
+
typer.echo(ctx.get_help())
|
|
82
|
+
raise typer.Exit(0)
|
|
83
|
+
kb = typer.Typer(help="Inspect the knowledge base in framework/.")
|
|
84
|
+
app.add_typer(kb, name="kb")
|
|
87
85
|
|
|
88
|
-
DEFAULT_ROOT =
|
|
86
|
+
DEFAULT_ROOT = default_root()
|
|
89
87
|
|
|
90
88
|
# What `fde retro` writes, and the only shape ingest will treat as a filename.
|
|
91
89
|
CASE_ID = re.compile(r"case-[0-9a-f]{6,32}")
|
|
@@ -93,7 +91,10 @@ CASE_ID = re.compile(r"case-[0-9a-f]{6,32}")
|
|
|
93
91
|
|
|
94
92
|
@kb.command("validate")
|
|
95
93
|
def kb_validate(
|
|
96
|
-
root: Annotated[Path, typer.Option(
|
|
94
|
+
root: Annotated[Path, typer.Option(
|
|
95
|
+
"--registry", "--root",
|
|
96
|
+
help="Registry directory (--root kept as an alias).",
|
|
97
|
+
)] = DEFAULT_ROOT,
|
|
97
98
|
lenient: Annotated[
|
|
98
99
|
bool, typer.Option(help="Report dangling links without failing.")
|
|
99
100
|
] = False,
|
|
@@ -145,6 +146,30 @@ def start(
|
|
|
145
146
|
typer.echo(" artifacts/ drop specs, schemas and sample pairs here")
|
|
146
147
|
if not statement:
|
|
147
148
|
typer.echo("\nNo statement yet. Add prose later, or start answering questions.")
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
# The statement is prose; read it like prose. Handing the tool a
|
|
152
|
+
# sentence and being told "no facts recorded yet" one command later
|
|
153
|
+
# made the first minute feel broken -- the reader was just never asked.
|
|
154
|
+
try:
|
|
155
|
+
registry = load_registry(DEFAULT_ROOT)
|
|
156
|
+
except RegistryError:
|
|
157
|
+
return
|
|
158
|
+
facts = parse_prose(statement, registry, source="statement")
|
|
159
|
+
if facts:
|
|
160
|
+
typer.echo("")
|
|
161
|
+
typer.echo(restate(facts, registry))
|
|
162
|
+
session_id = _next_session_id(engagement, "frame")
|
|
163
|
+
briefs = engagement.artifacts_dir / "briefs"
|
|
164
|
+
briefs.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
(briefs / f"{session_id}.txt").write_text(statement)
|
|
166
|
+
engagement.append(
|
|
167
|
+
Session(
|
|
168
|
+
session_id=session_id,
|
|
169
|
+
respondent=Respondent(role=Role.SYSTEM),
|
|
170
|
+
facts=facts,
|
|
171
|
+
)
|
|
172
|
+
)
|
|
148
173
|
|
|
149
174
|
|
|
150
175
|
@app.command("status")
|
|
@@ -642,9 +667,13 @@ def _put(dimension, question):
|
|
|
642
667
|
End of input ends the interview rather than aborting it: whatever was
|
|
643
668
|
gathered up to that point is still worth recording.
|
|
644
669
|
"""
|
|
670
|
+
hint = ""
|
|
671
|
+
if getattr(dimension, "values", None):
|
|
672
|
+
hint = f" [{' / '.join(dimension.values)}]"
|
|
645
673
|
while True:
|
|
646
674
|
try:
|
|
647
|
-
reply = typer.prompt(f"\n{question.asks}", default="",
|
|
675
|
+
reply = typer.prompt(f"\n{question.asks}{hint}", default="",
|
|
676
|
+
show_default=False)
|
|
648
677
|
except (EOFError, typer.Abort):
|
|
649
678
|
return None
|
|
650
679
|
answer = parse_answer(dimension, reply)
|
|
@@ -715,6 +744,12 @@ def _engagement(root: Path):
|
|
|
715
744
|
file is a one-line explanation, never a traceback -- a stack trace at a
|
|
716
745
|
client site reads as the tool being broken rather than the input.
|
|
717
746
|
"""
|
|
747
|
+
root = Path(root)
|
|
748
|
+
# `fde ask acme` after `fde start acme` should just work: start put it
|
|
749
|
+
# in engagements/acme, and making every later command spell that out
|
|
750
|
+
# cost every new user their first two minutes.
|
|
751
|
+
if not (root / "facts").is_dir() and (Path("engagements") / root).is_dir():
|
|
752
|
+
root = Path("engagements") / root
|
|
718
753
|
try:
|
|
719
754
|
engagement = load_engagement(root)
|
|
720
755
|
# Read once here so a hand-edited gates.yaml fails as a sentence
|
|
@@ -1670,7 +1705,7 @@ def implement_cmd(
|
|
|
1670
1705
|
reported if the agent touches them. Every round lands in
|
|
1671
1706
|
ops/implement-log.md.
|
|
1672
1707
|
"""
|
|
1673
|
-
from fde.implement import run_loop
|
|
1708
|
+
from fde.implement import AgentMissing, run_loop
|
|
1674
1709
|
|
|
1675
1710
|
project = Path(project)
|
|
1676
1711
|
if not (project / "evals").is_dir() or not (project / "app").is_dir():
|
|
@@ -1680,8 +1715,12 @@ def implement_cmd(
|
|
|
1680
1715
|
)
|
|
1681
1716
|
raise typer.Exit(1)
|
|
1682
1717
|
|
|
1683
|
-
|
|
1684
|
-
|
|
1718
|
+
try:
|
|
1719
|
+
report = run_loop(project, agent_cmd=agent_cmd, max_rounds=max_rounds,
|
|
1720
|
+
check=check, holdout=holdout)
|
|
1721
|
+
except AgentMissing as exc:
|
|
1722
|
+
typer.echo(str(exc), err=True)
|
|
1723
|
+
raise typer.Exit(1) from exc
|
|
1685
1724
|
(project / "ops").mkdir(exist_ok=True)
|
|
1686
1725
|
(project / "ops" / "implement-log.md").write_text(report.log())
|
|
1687
1726
|
|
|
@@ -1811,6 +1850,13 @@ def cost_cmd(
|
|
|
1811
1850
|
typer.echo(f"arrival_rate from the engagement: {requests_per_day:,}/day")
|
|
1812
1851
|
if human_waiting is None and values.get("human_waiting") is not None:
|
|
1813
1852
|
human_waiting = values["human_waiting"] != "no"
|
|
1853
|
+
if requests_per_day is None and price_per_seat is not None:
|
|
1854
|
+
# Seat economics needs workflows, not fleet volume: the README's own
|
|
1855
|
+
# one-liner failed for demanding a number the check never uses.
|
|
1856
|
+
requests_per_day = int(workflows_per_day)
|
|
1857
|
+
skip_fleet = True
|
|
1858
|
+
else:
|
|
1859
|
+
skip_fleet = False
|
|
1814
1860
|
if requests_per_day is None:
|
|
1815
1861
|
typer.echo(
|
|
1816
1862
|
"no volume to size for -- pass --requests-per-day, or --root an "
|
|
@@ -1820,31 +1866,35 @@ def cost_cmd(
|
|
|
1820
1866
|
if human_waiting is None:
|
|
1821
1867
|
human_waiting = True
|
|
1822
1868
|
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1869
|
+
if skip_fleet:
|
|
1870
|
+
plan = None
|
|
1871
|
+
else:
|
|
1872
|
+
plan = size_for(requests_per_day, params_b, today=stamp)
|
|
1873
|
+
if plan is not None:
|
|
1874
|
+
comparison = compare_hosting(
|
|
1875
|
+
requests_per_day, params_b, human_waiting=human_waiting, today=stamp
|
|
1876
|
+
)
|
|
1827
1877
|
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1878
|
+
typer.echo(
|
|
1879
|
+
f"{params_b:g}B at {requests_per_day:,}/day"
|
|
1880
|
+
f"{' (interactive)' if human_waiting else ' (batch, nobody waiting)'}\n"
|
|
1881
|
+
)
|
|
1882
|
+
typer.echo(f" naive: {plan['naive_replicas']} replica(s)")
|
|
1883
|
+
typer.echo(
|
|
1884
|
+
f" real: {plan['replicas']} replica(s) x {plan['gpus_per_replica']} "
|
|
1885
|
+
f"card(s) = {plan['gpus']} cards"
|
|
1886
|
+
)
|
|
1887
|
+
for name, why in plan["factors"].items():
|
|
1888
|
+
typer.echo(f" {name}: {why}")
|
|
1839
1889
|
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1890
|
+
typer.echo(
|
|
1891
|
+
f"\n self-hosted ${comparison['self_hosted_monthly']:,.0f}/mo\n"
|
|
1892
|
+
f" managed ${comparison['managed_monthly']:,.0f}/mo\n"
|
|
1893
|
+
f" -> {comparison['recommendation']}: {comparison['why']}"
|
|
1894
|
+
)
|
|
1895
|
+
typer.echo(
|
|
1896
|
+
f"\n as of {plan['as_of']} -- {plan['rederive']}"
|
|
1897
|
+
)
|
|
1848
1898
|
|
|
1849
1899
|
if price_per_seat is not None:
|
|
1850
1900
|
from fde.costing import unit_economics
|
|
@@ -1872,7 +1922,10 @@ def cost_cmd(
|
|
|
1872
1922
|
@kb.command("ingest-case")
|
|
1873
1923
|
def kb_ingest_case(
|
|
1874
1924
|
case_file: Annotated[Path, typer.Argument(help="A case.json from `fde retro`.")],
|
|
1875
|
-
root: Annotated[Path, typer.Option(
|
|
1925
|
+
root: Annotated[Path, typer.Option(
|
|
1926
|
+
"--registry", "--root",
|
|
1927
|
+
help="Registry directory (--root kept as an alias).",
|
|
1928
|
+
)] = DEFAULT_ROOT,
|
|
1876
1929
|
) -> None:
|
|
1877
1930
|
"""Bring a captured case into the corpus -- as pending, never as reviewed.
|
|
1878
1931
|
|
|
@@ -2048,7 +2101,10 @@ def kb_suggest(
|
|
|
2048
2101
|
|
|
2049
2102
|
@kb.command("sweep")
|
|
2050
2103
|
def kb_sweep(
|
|
2051
|
-
root: Annotated[Path, typer.Option(
|
|
2104
|
+
root: Annotated[Path, typer.Option(
|
|
2105
|
+
"--registry", "--root",
|
|
2106
|
+
help="Registry directory (--root kept as an alias).",
|
|
2107
|
+
)] = DEFAULT_ROOT,
|
|
2052
2108
|
samples: Annotated[int, typer.Option(help="Fully specified profiles to try.")] = 300,
|
|
2053
2109
|
seed: Annotated[int, typer.Option(help="Deterministic sampling seed.")] = 0,
|
|
2054
2110
|
) -> None:
|
|
@@ -2086,7 +2142,10 @@ def kb_sweep(
|
|
|
2086
2142
|
|
|
2087
2143
|
@kb.command("gaps")
|
|
2088
2144
|
def kb_gaps(
|
|
2089
|
-
root: Annotated[Path, typer.Option(
|
|
2145
|
+
root: Annotated[Path, typer.Option(
|
|
2146
|
+
"--registry", "--root",
|
|
2147
|
+
help="Registry directory (--root kept as an alias).",
|
|
2148
|
+
)] = DEFAULT_ROOT,
|
|
2090
2149
|
) -> None:
|
|
2091
2150
|
"""Report what the corpus is missing. Work items, not errors -- always exits 0."""
|
|
2092
2151
|
try:
|
|
@@ -216,6 +216,40 @@ def _write_package(architecture: Architecture, out: Path) -> None:
|
|
|
216
216
|
f"Regenerating from the same profile produces the same files, so a diff\n"
|
|
217
217
|
f'between two builds means a decision changed.\n"""\n'
|
|
218
218
|
)
|
|
219
|
+
(out / "README.md").write_text(
|
|
220
|
+
f"# {architecture.topology} deployment -- generated by fde-framework\n"
|
|
221
|
+
"\n"
|
|
222
|
+
"This project was emitted from an engagement profile. Every decision\n"
|
|
223
|
+
"in it is traced in [ARCHITECTURE.md](ARCHITECTURE.md); every risk\n"
|
|
224
|
+
"accepted along the way is in [RISKS.md](RISKS.md).\n"
|
|
225
|
+
"\n"
|
|
226
|
+
"## Run the evaluation\n"
|
|
227
|
+
"\n"
|
|
228
|
+
"```bash\n"
|
|
229
|
+
"python evals/harness.py --min-score 0.9\n"
|
|
230
|
+
"```\n"
|
|
231
|
+
"\n"
|
|
232
|
+
"It fails until the pipeline is implemented end to end -- that is the\n"
|
|
233
|
+
"point. The scaffolded components under `app/components/` carry their\n"
|
|
234
|
+
"contracts; implement them by hand, or drive a coding agent against\n"
|
|
235
|
+
"the harness with `fde implement .` (the evals, the boundary and the\n"
|
|
236
|
+
"decision documents are fenced -- an agent that edits them is caught\n"
|
|
237
|
+
"and reverted).\n"
|
|
238
|
+
"\n"
|
|
239
|
+
"## The pieces\n"
|
|
240
|
+
"\n"
|
|
241
|
+
"| Path | What it is |\n"
|
|
242
|
+
"|---|---|\n"
|
|
243
|
+
"| `app/pipeline.py` | The run order: payload steps, approval gates, critics |\n"
|
|
244
|
+
"| `app/contract.py` | RefusedInput -- forbidden input is refused, never guessed at |\n"
|
|
245
|
+
"| `evals/` | Golden / edge / adversarial sets from the client's "
|
|
246
|
+
"own pairs, plus the acceptance protocol |\n"
|
|
247
|
+
"| `deploy/` | The substrate this profile earned, and how to tear it down |\n"
|
|
248
|
+
"| `ops/` | Runbook keyed to the failure taxonomy, SLOs, rollback |\n"
|
|
249
|
+
"\n"
|
|
250
|
+
"Regenerating from the same facts reproduces this project byte for\n"
|
|
251
|
+
"byte; a diff between two builds means a decision changed.\n"
|
|
252
|
+
)
|
|
219
253
|
(out / "app" / "components" / "__init__.py").write_text("")
|
|
220
254
|
(out / "app" / "contract.py").write_text(
|
|
221
255
|
'"""The one contract every step shares: forbidden input is refused.\n'
|