project-loop-harness 0.2.0__tar.gz → 0.2.1__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.
- {project_loop_harness-0.2.0/src/project_loop_harness.egg-info → project_loop_harness-0.2.1}/PKG-INFO +1 -1
- project_loop_harness-0.2.1/agent-tasks/0089-dogfood-fixture-propose.md +107 -0
- project_loop_harness-0.2.1/agent-tasks/0090-eval-baseline-record-compare.md +134 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/code-context.md +104 -11
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/distribution.md +1 -1
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/pyproject.toml +1 -1
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/scripts/run_advisory_retrieval_eval.py +74 -23
- project_loop_harness-0.2.1/src/pcl/__init__.py +1 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/cli.py +57 -1
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/__init__.py +3 -0
- project_loop_harness-0.2.1/src/pcl/code_context/eval.py +1043 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/store.py +63 -5
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/context.py +1 -40
- project_loop_harness-0.2.1/src/pcl/token_estimation.py +44 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1/src/project_loop_harness.egg-info}/PKG-INFO +1 -1
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/project_loop_harness.egg-info/SOURCES.txt +4 -0
- project_loop_harness-0.2.1/tests/fixtures/retrieval_v0.json +41 -0
- project_loop_harness-0.2.1/tests/test_advisory_retrieval_eval_script.py +146 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_code_index.py +685 -0
- project_loop_harness-0.2.0/src/pcl/__init__.py +0 -1
- project_loop_harness-0.2.0/src/pcl/code_context/eval.py +0 -234
- project_loop_harness-0.2.0/tests/fixtures/retrieval_v0.json +0 -19
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/LICENSE +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/MANIFEST.in +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/Makefile +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/README.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0001-hardening-cli.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0002-db-migrations.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0003-workflow-runner.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0004-dashboard-renderer.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0005-agent-integration.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0006-codex-plugin.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0007-mcp-server.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0008-loop-lifecycle.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0009-defect-lifecycle.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0010-reporting-evidence.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0011-validation-invariants.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0012-audit-log-integrity.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0013-validation-diagnostics.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0014-escalation-lifecycle.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0015-decision-lifecycle.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0016-escalation-decision-linkage.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0017-next-action-guided-loop.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0018-readme-golden-path.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0019-recovery-playbook.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0020-example-project-refresh.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0021-agent-adapter-contract.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0022-agent-output-validation.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0023-codex-exec-adapter-hardening.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0024-claude-manual-adapter-hardening.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0025-generic-shell-adapter.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0026-agent-job-evidence-ingestion.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0027-dashboard-data-contract.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0028-dashboard-evidence-navigation.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0029-dashboard-risk-and-blockers.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0030-distribution-readiness.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0031-workflow-proposal-mode.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0032-workflow-proposal-review.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0033-workflow-verifier.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0034-limited-execution-sandbox.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0035-automatic-workflow-executor.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0036-executor-dogfood-workflow.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0037-executor-retry-resume.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0038-story-test-lifecycle.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0039-workflow-yaml-rule-expressions.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0040-test-case-evidence-validation.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0041-feature-inspection-commands.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0042-report-coverage-context.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0043-feature-report.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0044-complete-csv-export.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0045-filtered-job-inspection.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0046-feature-status-lifecycle.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0047-feature-coverage-next-action.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0048-migration-status-command.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0049-render-json-artifact-paths.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0050-codex-plugin-package-inventory.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0051-mcp-render-artifact-paths.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0052-lifecycle-failure-job-cleanup.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0053-prompt-job-json-handoff.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0054-human-queue-linkage-cli.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0055-workflow-proposal-list-filter.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0056-sandbox-noop-execution-guard.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0057-executor-no-executable-step-guard.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0058-dogfood-usability-hardening.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0059-checkpoint-review-guidance.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0060-pypi-trusted-publishing.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0061-context-pack-v1.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0062-task-backlog-entity.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0063-structured-verification-rubric.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0064-task-loop-integration.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0065-dashboard-human-decisions.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0066-agent-registry-lease.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0067-context-pack-improvements.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0068-trust-hardening.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0069-explainable-code-context-v0.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0070-human-decision-cockpit.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0071-dogfood-impact-precision.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0072-sensitive-omission.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0073-code-context-module-split.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0074-search-snapshot-consistency.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0075-diff-source-modes.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0076-schema-version-integrity.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0077-index-output-budget-and-impact-noise.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0078-context-pack-code-context-bridge.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0079-receipt-human-summary.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0080-retrieval-eval-gate.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0081-diff-modes-completion.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0082-receipt-relevance-and-age.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0083-required-section-invariant.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0084-source-commands-honesty.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0085-distribution-source-completeness.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0086-command-surface-alignment.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0087-verification-suggestion-ids.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0088-verification-feedback.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/agent-tasks/0091-refresh-command-scope-fidelity.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/adoption-guide.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/adr/0001-hybrid-state.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/adr/0002-cli-is-the-runtime.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/adr/0003-static-workflows-before-dynamic-workflows.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/agent-adapter-contract.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/agent-output-template.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/agent-roles.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/architecture.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/context-pack.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/dashboard-data-contract.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/dashboard-design.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/data-model.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/golden-path.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/implementation-plan.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/plh_v0_1_11_review_agenda.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/plh_v0_1_12_review_agenda.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/pypi-publishing.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/recovery-playbook.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/safety-permissions.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/v0.2.0-plan.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/verification-feedback-design.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/verification-feedback.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/verification-rubric.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/docs/workflow-contract.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/scripts/verify_sdist_contracts.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/setup.cfg +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/__main__.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/agents.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/checkpoints.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/diff.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/impact.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/receipts.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/scan.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/search.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/summary.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/symbols.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_context/test_hints.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/code_index.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/commands.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/db/migrations/001_initial.sql +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/db/migrations/002_tasks.sql +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/db/migrations/003_agent_registry.sql +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/db/migrations/004_code_index.sql +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/db/migrations/005_verification_feedback.sql +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/db/schema.sql +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/db.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/decisions.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/dispatch.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/errors.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/escalations.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/events.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/evidence.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/exporters.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/guards.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/ids.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/init_project.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/lifecycle.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/links.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/locales.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/mcp_server.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/migrations.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/paths.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/receipt_show.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/registry.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/renderer.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/reports.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/resources.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/rubric.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/stories.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/tasks.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/dashboard/dashboard.html +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/project/AGENTS.block.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/project/CLAUDE.block.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/project/gitignore.fragment +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/project/pcl.yaml +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/skills/project-control-loop/SKILL.md +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/workflows/defect_repair.yaml +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/workflows/executor_smoke.yaml +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/workflows/feature_coverage.yaml +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/templates/workflows/regression_loop.yaml +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/timeutil.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/update_check.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/validators.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/verification_feedback.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/verifications.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/workflow_executor.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/workflow_proposal_validation.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/workflow_proposals.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/workflow_sandbox.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/workflow_verifier.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/workflow_yaml.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/pcl/workflows.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/project_loop_harness.egg-info/dependency_links.txt +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/project_loop_harness.egg-info/entry_points.txt +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/project_loop_harness.egg-info/requires.txt +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/src/project_loop_harness.egg-info/top_level.txt +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/fixtures/code_context_summary_v0.json +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/fixtures/context_receipt_v0.json +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/fixtures/context_receipt_v0_legacy_string_suggestions.json +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/fixtures/retrieval_adversarial_v0.json +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/fixtures/retrieval_real_history_v0.json +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_agent_adapter_contract.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_agent_output_validation.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_agents.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_audit_log_integrity.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_checkpoints.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_claude_manual_adapter.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_cli_init.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_code_context_summary.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_codex_exec_adapter.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_codex_plugin.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_context.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_dashboard.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_dashboard_data_contract.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_decisions.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_defects.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_dispatch.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_distribution.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_escalations.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_examples.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_features.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_generic_shell_adapter.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_golden_path.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_lifecycle.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_mcp_server.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_migrations.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_next_actions.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_pypi_publishing.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_receipt_show.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_recovery_playbook.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_registry.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_reports.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_rubric.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_stories.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_tasks.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_update_check.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_validation.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_validation_diagnostics.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_verification_feedback.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_workflow_executor.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_workflow_proposals.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_workflow_sandbox.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_workflow_verifier.py +0 -0
- {project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/tests/test_workflows.py +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Task 0089: Dogfood-to-Fixture Propose (v0.2.1)
|
|
2
|
+
|
|
3
|
+
Design source: `docs/verification-feedback-design.md` Part 4 plus the
|
|
4
|
+
Approval addendum item 4 (staging location). Where this spec and the
|
|
5
|
+
design doc disagree, the design doc wins. No migration, no new
|
|
6
|
+
runtime dependency.
|
|
7
|
+
|
|
8
|
+
## Goal
|
|
9
|
+
|
|
10
|
+
An eval suite whose fixtures do not grow from real usage is not
|
|
11
|
+
credible. Add `pcl eval fixture propose --from-receipt <evidence-id>`
|
|
12
|
+
so real context receipts become UNLABELED fixture candidates that a
|
|
13
|
+
human labels before adoption. PLH never fabricates ground-truth
|
|
14
|
+
labels.
|
|
15
|
+
|
|
16
|
+
## Design constraints (approved, do not relitigate)
|
|
17
|
+
|
|
18
|
+
- Staging area is repo-root `fixtures/proposed/` — a reviewable,
|
|
19
|
+
Git-tracked source asset, NOT `.project-loop/` state.
|
|
20
|
+
- Proposed candidates carry `labels_status: "unlabeled"` and EMPTY
|
|
21
|
+
`expected_files` / `expected_tests` / `critical_context` arrays.
|
|
22
|
+
Filling them is a human review step by definition.
|
|
23
|
+
- Adoption into `tests/fixtures/` is a manual `git mv`-style step
|
|
24
|
+
after human labeling. NO auto-adoption path exists, and none may
|
|
25
|
+
be added.
|
|
26
|
+
|
|
27
|
+
## Scope
|
|
28
|
+
|
|
29
|
+
### 1. `pcl eval fixture propose --from-receipt <evidence-id> [--json]`
|
|
30
|
+
|
|
31
|
+
- Loads the referenced evidence row; it must be `type =
|
|
32
|
+
'context_receipt'` with a readable `context-receipt/v0` artifact —
|
|
33
|
+
unknown evidence, wrong type, or unreadable artifact are distinct
|
|
34
|
+
typed errors (reuse the receipt-loading discipline from
|
|
35
|
+
`verification_feedback.py` where sensible).
|
|
36
|
+
- Emits `fixtures/proposed/<receipt-evidence-id>-retrieval.json`
|
|
37
|
+
(lowercased evidence id, deterministic name; refuse to overwrite an
|
|
38
|
+
existing file unless `--force` — overwriting a half-labeled
|
|
39
|
+
candidate silently would destroy human work).
|
|
40
|
+
- Candidate shape: `retrieval-fixture/v0` with ONE task:
|
|
41
|
+
- `id`: derived from the receipt evidence id (deterministic).
|
|
42
|
+
- `diff`: a minimal synthetic unified diff touching exactly the
|
|
43
|
+
receipt's `changed_files` paths (same mechanical style as
|
|
44
|
+
existing fixtures). Receipts do not store original diff text, so
|
|
45
|
+
the replay diff is synthesized; record that honestly via
|
|
46
|
+
`diff_synthesized_from_receipt: true` inside the task.
|
|
47
|
+
- `expected_files: []`, `expected_tests: []`,
|
|
48
|
+
`critical_context: []`, `labels_status: "unlabeled"`.
|
|
49
|
+
- `source_receipt` provenance block: `evidence_id`, `created_at`,
|
|
50
|
+
`diff_source`, `base_ref` (when present), and
|
|
51
|
+
`retrieved_candidate_paths` (the receipt's
|
|
52
|
+
`included_candidate_context` paths) — this is the raw material
|
|
53
|
+
the human labeler starts from, clearly provenance, not labels.
|
|
54
|
+
- Record one standard `eval_fixture_proposed` event via the shared
|
|
55
|
+
`append_event` path (SQLite events table + JSONL mirror, sequential
|
|
56
|
+
event id), recording the receipt evidence id and output path. The
|
|
57
|
+
event row is the ONLY permitted SQLite write; no other tables may
|
|
58
|
+
be touched.
|
|
59
|
+
**Clarified 2026-07-06 after first acceptance pass:** the original
|
|
60
|
+
wording "JSONL event ... No SQLite mutation" was over-constrained
|
|
61
|
+
and led to a JSONL-only event with a uuid id, which violates the
|
|
62
|
+
task-0012 audit integrity invariant (`validate --strict` errors on
|
|
63
|
+
JSONL event ids missing from the DB events table). Events always go
|
|
64
|
+
through `append_event` to both stores; do not invent side-channel
|
|
65
|
+
event writers.
|
|
66
|
+
|
|
67
|
+
### 2. Guard: eval refuses unlabeled fixtures
|
|
68
|
+
|
|
69
|
+
- `pcl eval retrieval` on a fixture whose tasks carry
|
|
70
|
+
`labels_status: "unlabeled"` (or empty expected+critical blocks
|
|
71
|
+
alongside that marker) fails with a typed error telling the
|
|
72
|
+
operator to label the candidate and move it into `tests/fixtures/`.
|
|
73
|
+
Labeled/legacy fixtures (no `labels_status` field) evaluate as
|
|
74
|
+
today.
|
|
75
|
+
|
|
76
|
+
### 3. Documentation
|
|
77
|
+
|
|
78
|
+
- `docs/code-context.md` (eval section): propose workflow, staging
|
|
79
|
+
directory, human labeling step, manual adoption, and the
|
|
80
|
+
no-fabricated-labels rule.
|
|
81
|
+
- Add `fixtures/proposed/.gitkeep` (or a one-line README.md in that
|
|
82
|
+
directory) so the staging area exists in Git.
|
|
83
|
+
|
|
84
|
+
## Acceptance Criteria
|
|
85
|
+
|
|
86
|
+
- Proposing from a real receipt fixture yields a candidate that is
|
|
87
|
+
byte-deterministic given the same receipt (no timestamps of its
|
|
88
|
+
own — reuse the receipt's `created_at`).
|
|
89
|
+
- Candidate visibly unlabeled: `labels_status: "unlabeled"`, all
|
|
90
|
+
three expected/critical arrays empty (contract test).
|
|
91
|
+
- `pcl eval retrieval` against the proposed candidate → typed error;
|
|
92
|
+
after a test labels it (filling expected blocks, removing/flipping
|
|
93
|
+
`labels_status`), evaluation runs and produces metrics.
|
|
94
|
+
- Unknown/wrong-type/unreadable receipt → distinct typed errors,
|
|
95
|
+
nothing written.
|
|
96
|
+
- Existing candidate without `--force` → typed error, file untouched.
|
|
97
|
+
- `ruff check .` passes; full `python3 -m pytest` passes; `pcl init`
|
|
98
|
+
smoke against a temp dir passes.
|
|
99
|
+
|
|
100
|
+
## Do Not
|
|
101
|
+
|
|
102
|
+
- Do not fill or guess expected/critical labels from receipt data.
|
|
103
|
+
- Do not add an auto-adoption or auto-labeling path.
|
|
104
|
+
- Do not write candidates anywhere under `.project-loop/`.
|
|
105
|
+
- Do not mutate SQLite; the only side effects are the candidate file
|
|
106
|
+
and one JSONL event.
|
|
107
|
+
- Do not use hit/miss/usefulness vocabulary.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Task 0090: Eval Baseline Record / Compare (v0.2.1)
|
|
2
|
+
|
|
3
|
+
Design source: `docs/verification-feedback-design.md` Part 3 plus the
|
|
4
|
+
Approval addendum. Where this spec and the design doc disagree, the
|
|
5
|
+
design doc wins. Depends on task 0089 (unlabeled-fixture guard) being
|
|
6
|
+
merged. No migration, no new runtime dependency.
|
|
7
|
+
|
|
8
|
+
## Goal
|
|
9
|
+
|
|
10
|
+
Make retrieval eval results durable, comparable evidence with full
|
|
11
|
+
provenance, so the semantic promotion gate can eventually be decided
|
|
12
|
+
by evidence instead of enthusiasm. Metric thresholds stay ADVISORY;
|
|
13
|
+
only broken measurement infrastructure blocks.
|
|
14
|
+
|
|
15
|
+
## Scope
|
|
16
|
+
|
|
17
|
+
### 1. Metrics completion (additive to `retrieval-eval/v0` output)
|
|
18
|
+
|
|
19
|
+
- Add `false_positive_rate` = (retrieved_total − true_positive_total)
|
|
20
|
+
/ retrieved_total (aggregate and per task; `null` on empty
|
|
21
|
+
denominator — same rule as everywhere else).
|
|
22
|
+
- Add `token_cost_estimate` = deterministic `charclass/v1` estimate
|
|
23
|
+
for the retrieved candidate files (per task and aggregate). Reuse
|
|
24
|
+
the existing estimator; it is an estimate label, never a billing
|
|
25
|
+
claim. Files missing from the index contribute 0 and are listed in
|
|
26
|
+
a `token_cost_unestimated_paths` array rather than silently
|
|
27
|
+
skipped.
|
|
28
|
+
**Clarified 2026-07-06 after first acceptance pass:** the estimate
|
|
29
|
+
basis is a per-file `token_estimate` INTEGER computed at index
|
|
30
|
+
build time (file text is already in memory during scan) and stored
|
|
31
|
+
in the index detail payload. The detail artifact must NEVER store
|
|
32
|
+
file content — the index stays hashes + symbol-lite metadata
|
|
33
|
+
(architecture boundary; 0077 budget discipline; 0072 sensitive
|
|
34
|
+
surface). Old details lacking `token_estimate` route those paths
|
|
35
|
+
into `token_cost_unestimated_paths`. The first implementation
|
|
36
|
+
embedded full `indexed_content` text into the detail; do not
|
|
37
|
+
repeat that.
|
|
38
|
+
- Existing precision / recall / missing_critical_context stay
|
|
39
|
+
unchanged.
|
|
40
|
+
|
|
41
|
+
### 2. Fixture kind coverage (5 kinds)
|
|
42
|
+
|
|
43
|
+
The approved kinds: code change, docs-only, config-only, rename/move,
|
|
44
|
+
secret omission. Code change, rename/move, and secret omission
|
|
45
|
+
already exist (`retrieval_v0.json`, `retrieval_adversarial_v0.json`).
|
|
46
|
+
Add small labeled fixture tasks for **docs-only** and **config-only**
|
|
47
|
+
changes in the same synthetic style. Keep the existing floor-test
|
|
48
|
+
discipline: assert metric floors per fixture in tests so regressions
|
|
49
|
+
fail loudly.
|
|
50
|
+
|
|
51
|
+
### 3. `pcl eval retrieval --record-baseline`
|
|
52
|
+
|
|
53
|
+
- Runs the eval, then stores the full evaluation payload as a normal
|
|
54
|
+
evidence artifact under `.project-loop/evidence/retrieval-eval/`
|
|
55
|
+
(evidence row + JSONL event, following the receipt-recording
|
|
56
|
+
pattern).
|
|
57
|
+
- Baseline artifact carries a `baseline_provenance` block with ALL
|
|
58
|
+
SIX approved fields, each REQUIRED:
|
|
59
|
+
1. `fixture_path` + `fixture_content_hash` (sha256 of fixture
|
|
60
|
+
bytes),
|
|
61
|
+
2. `git_head` of the target repo,
|
|
62
|
+
3. `index_run_id` + `index_detail_hash` (hash of the index detail
|
|
63
|
+
artifact),
|
|
64
|
+
4. `code_context_config_hash` (hash of the effective `code_index`
|
|
65
|
+
config subtree from `pcl.yaml`, canonicalized),
|
|
66
|
+
5. `pcl_version` (the real `pcl.__version__`),
|
|
67
|
+
6. `eval_contract_version` (`retrieval-eval/v0`).
|
|
68
|
+
- Missing provenance input (e.g., no index run exists, not a git
|
|
69
|
+
repo) → typed error, nothing recorded. Broken provenance is not a
|
|
70
|
+
baseline.
|
|
71
|
+
|
|
72
|
+
### 4. `pcl eval retrieval --compare-baseline`
|
|
73
|
+
|
|
74
|
+
- Finds the LATEST recorded baseline whose `fixture_content_hash`
|
|
75
|
+
matches the current fixture; reports per-metric deltas
|
|
76
|
+
(precision, recall, missing-critical-context count,
|
|
77
|
+
false_positive_rate, token_cost_estimate) plus both provenance
|
|
78
|
+
blocks side by side.
|
|
79
|
+
- No baseline with a matching fixture hash → typed
|
|
80
|
+
"not comparable" error naming the nearest baseline and why it does
|
|
81
|
+
not match (hash mismatch is stated explicitly, never silently
|
|
82
|
+
compared).
|
|
83
|
+
- Comparison output is a report, not a verdict: deltas and facts
|
|
84
|
+
only. No pass/fail field, no threshold evaluation.
|
|
85
|
+
|
|
86
|
+
### 5. CI advisory comparison
|
|
87
|
+
|
|
88
|
+
- Extend `scripts/run_advisory_retrieval_eval.py` (or add a sibling
|
|
89
|
+
script wired the same way) so CI runs the eval and, when a
|
|
90
|
+
committed baseline reference exists, prints the comparison as an
|
|
91
|
+
advisory artifact.
|
|
92
|
+
- Metric regressions NEVER fail CI.
|
|
93
|
+
- Eval INFRASTRUCTURE failures DO fail CI: eval command error,
|
|
94
|
+
fixture contract violation, unreadable fixture, provenance
|
|
95
|
+
computation failure. Broken measurement is not advisory.
|
|
96
|
+
|
|
97
|
+
### 6. Documentation
|
|
98
|
+
|
|
99
|
+
- `docs/code-context.md` eval section: baseline lifecycle, the six
|
|
100
|
+
provenance fields, the advisory-vs-blocking boundary, and the
|
|
101
|
+
semantic promotion gate linkage (evidence decides promotion).
|
|
102
|
+
|
|
103
|
+
## Acceptance Criteria
|
|
104
|
+
|
|
105
|
+
- Recording twice on an unchanged repo+fixture yields identical
|
|
106
|
+
metrics and identical provenance except evidence id/created_at.
|
|
107
|
+
- Compare on matching hash reports per-metric deltas; compare after
|
|
108
|
+
editing the fixture → typed not-comparable error that names the
|
|
109
|
+
hash mismatch.
|
|
110
|
+
- Missing index run → typed error from `--record-baseline`, no
|
|
111
|
+
evidence row, no event.
|
|
112
|
+
- `false_positive_rate` and `token_cost_estimate` appear per task
|
|
113
|
+
and aggregate, `null` on empty denominators;
|
|
114
|
+
`token_cost_unestimated_paths` lists unindexed files.
|
|
115
|
+
- Docs-only and config-only fixture tasks exist with floor tests.
|
|
116
|
+
- CI script: a deliberately corrupted fixture fails the script
|
|
117
|
+
(blocking path test); a metric delta does not (advisory path
|
|
118
|
+
test).
|
|
119
|
+
- `pcl validate --strict` untouched by baselines beyond standard
|
|
120
|
+
evidence integrity.
|
|
121
|
+
- `ruff check .` passes; full `python3 -m pytest` passes; `pcl init`
|
|
122
|
+
smoke against a temp dir passes.
|
|
123
|
+
|
|
124
|
+
## Do Not
|
|
125
|
+
|
|
126
|
+
- Do not implement metric thresholds, pass/fail verdicts, or release
|
|
127
|
+
gates on metric values.
|
|
128
|
+
- Do not compare across different fixture hashes "helpfully".
|
|
129
|
+
- Do not read generated dashboard HTML, mutate state from the eval
|
|
130
|
+
path beyond the evidence row + event, or add raw SQL.
|
|
131
|
+
- Do not use hit/miss/usefulness vocabulary; token cost is an
|
|
132
|
+
estimate, not a price.
|
|
133
|
+
- Do not touch embeddings / Tree-sitter / call graphs — the semantic
|
|
134
|
+
promotion gate is unchanged.
|
|
@@ -55,6 +55,8 @@ Evaluate retrieval behavior against labels:
|
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
57
|
pcl eval retrieval --fixture tests/fixtures/retrieval_v0.json --json
|
|
58
|
+
pcl eval retrieval --fixture tests/fixtures/retrieval_v0.json --record-baseline --json
|
|
59
|
+
pcl eval retrieval --fixture tests/fixtures/retrieval_v0.json --compare-baseline --json
|
|
58
60
|
```
|
|
59
61
|
|
|
60
62
|
## Index Contract
|
|
@@ -109,6 +111,7 @@ needs every file, ignored path, and hash-skip entry on stdout.
|
|
|
109
111
|
"mtime": 123456789,
|
|
110
112
|
"sha256": "abc123",
|
|
111
113
|
"line_count": 10,
|
|
114
|
+
"token_estimate": 42,
|
|
112
115
|
"symbol_summary": {
|
|
113
116
|
"contract_version": "symbol-summary/v0",
|
|
114
117
|
"symbols": [{"type": "function", "name": "pack_context_for_job", "line": 1}]
|
|
@@ -629,6 +632,29 @@ Task fields:
|
|
|
629
632
|
`assertion_note`, and `must_not_retrieve`, are metadata and are ignored by
|
|
630
633
|
the evaluator.
|
|
631
634
|
|
|
635
|
+
Dogfood receipts become fixture candidates through a staging workflow:
|
|
636
|
+
|
|
637
|
+
```bash
|
|
638
|
+
pcl eval fixture propose --from-receipt E-0001 --json
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
The command reads a real `context_receipt` evidence row and writes an
|
|
642
|
+
UNLABELED `retrieval-fixture/v0` candidate to repo-root `fixtures/proposed/`.
|
|
643
|
+
That directory is a Git-tracked source staging area, not `.project-loop/`
|
|
644
|
+
runtime state. The candidate has one task, a synthetic unified diff touching
|
|
645
|
+
the receipt's `changed_files` paths, `diff_synthesized_from_receipt: true`, and
|
|
646
|
+
source receipt provenance (`evidence_id`, `created_at`, `diff_source`,
|
|
647
|
+
optional `base_ref`, and retrieved candidate paths). The synthetic diff is only
|
|
648
|
+
a replay handle because receipts do not store original diff text.
|
|
649
|
+
|
|
650
|
+
PLH never fabricates ground-truth labels. Proposed tasks carry
|
|
651
|
+
`labels_status: "unlabeled"` and empty `expected_files`, `expected_tests`, and
|
|
652
|
+
`critical_context` arrays. `pcl eval retrieval` refuses these candidates with a
|
|
653
|
+
typed error. A human must inspect the source receipt, fill the expected and
|
|
654
|
+
critical arrays, remove or change the unlabeled marker, and manually adopt the
|
|
655
|
+
fixture into `tests/fixtures/` with a normal Git move. There is no auto-labeling
|
|
656
|
+
or auto-adoption path.
|
|
657
|
+
|
|
632
658
|
Fixture evolution is additive in v0:
|
|
633
659
|
|
|
634
660
|
- New optional fields may be added without a version bump.
|
|
@@ -640,6 +666,8 @@ Fixture evolution is additive in v0:
|
|
|
640
666
|
|
|
641
667
|
The checked-in fixture families have different purposes:
|
|
642
668
|
|
|
669
|
+
- `retrieval_v0.json`: small synthetic coverage for code change, docs-only,
|
|
670
|
+
and config-only tasks.
|
|
643
671
|
- `real-history`: derived from actual repository changes, currently following
|
|
644
672
|
the `tests/fixtures/retrieval_real_history_v0.json` pattern. These fixtures
|
|
645
673
|
measure ordinary retrieval quality against real change history.
|
|
@@ -649,23 +677,88 @@ The checked-in fixture families have different purposes:
|
|
|
649
677
|
omission, stale-index signaling, and an annotated renamed-file baseline miss.
|
|
650
678
|
|
|
651
679
|
`pcl eval retrieval --fixture <path> --json` returns `retrieval-eval/v0` with
|
|
652
|
-
precision, recall,
|
|
680
|
+
precision, recall, false-positive rate, token cost estimate,
|
|
681
|
+
`missing_critical_context`, and per-task retrieved paths.
|
|
682
|
+
|
|
683
|
+
Metric fields:
|
|
684
|
+
|
|
685
|
+
- `precision`: true positives divided by retrieved paths, preserving the v0
|
|
686
|
+
empty-denominator rule.
|
|
687
|
+
- `recall`: true positives divided by expected files/tests, preserving the v0
|
|
688
|
+
empty-denominator rule.
|
|
689
|
+
- `false_positive_rate`: `(retrieved - true_positives) / retrieved`; empty
|
|
690
|
+
retrieved denominators yield `null`.
|
|
691
|
+
- `token_cost_estimate`: deterministic `charclass/v1` estimate over retrieved
|
|
692
|
+
paths' per-file `token_estimate` integers from
|
|
693
|
+
`.project-loop/cache/code-index-detail.json`. The estimate is computed at
|
|
694
|
+
index build time from indexed text already in memory; the detail artifact does
|
|
695
|
+
not store raw file contents. It is an estimate of indexed text volume, not a
|
|
696
|
+
price or billing signal.
|
|
697
|
+
- `token_cost_unestimated_paths`: retrieved paths that are absent from the
|
|
698
|
+
detail artifact or present without a `token_estimate`, including details
|
|
699
|
+
built by older versions. They contribute `0` to the estimate and are listed
|
|
700
|
+
explicitly.
|
|
701
|
+
- `missing_critical_context`: labeled critical paths not retrieved.
|
|
702
|
+
|
|
653
703
|
Task output may also include additive diagnostic fields used by adversarial
|
|
654
704
|
fixtures: `retrieval_source`, `staleness_warnings`,
|
|
655
705
|
`staleness_affected_paths`, `sensitive_omitted_count`,
|
|
656
706
|
`retrieved_snapshot_consistency`, `excluded_changed_files`, and
|
|
657
707
|
`expected_misses`.
|
|
658
708
|
|
|
709
|
+
### Baseline Lifecycle
|
|
710
|
+
|
|
711
|
+
`pcl eval retrieval --fixture <path> --record-baseline --json` runs the eval
|
|
712
|
+
and stores the full payload as normal evidence under
|
|
713
|
+
`.project-loop/evidence/retrieval-eval/`. Recording writes an evidence row and
|
|
714
|
+
the standard mirrored event in SQLite and `.project-loop/events.jsonl`; it does
|
|
715
|
+
not use a JSONL-only side channel.
|
|
716
|
+
|
|
717
|
+
Every baseline artifact carries `baseline_provenance` with these required
|
|
718
|
+
fields:
|
|
719
|
+
|
|
720
|
+
- `fixture_path`
|
|
721
|
+
- `fixture_content_hash` (sha256 of fixture bytes)
|
|
722
|
+
- `git_head`
|
|
723
|
+
- `index_run_id`
|
|
724
|
+
- `index_detail_hash` (sha256 of the code index detail artifact)
|
|
725
|
+
- `code_context_config_hash` (canonical effective `code_index` subtree)
|
|
726
|
+
- `pcl_version`
|
|
727
|
+
- `eval_contract_version` (`retrieval-eval/v0`)
|
|
728
|
+
|
|
729
|
+
Missing provenance inputs, such as no index run, missing index detail artifact,
|
|
730
|
+
or a non-Git target, are typed errors. In those cases no baseline artifact,
|
|
731
|
+
evidence row, or event is recorded.
|
|
732
|
+
|
|
733
|
+
`pcl eval retrieval --fixture <path> --compare-baseline --json` compares the
|
|
734
|
+
current eval with the latest recorded baseline whose `fixture_content_hash`
|
|
735
|
+
matches the current fixture. It reports current metrics, baseline metrics,
|
|
736
|
+
metric deltas, current provenance, and baseline provenance. The reported deltas
|
|
737
|
+
cover precision, recall, missing-critical-context count, false-positive rate,
|
|
738
|
+
and token cost estimate.
|
|
739
|
+
|
|
740
|
+
Comparison never crosses fixture hashes. If only a different fixture hash is
|
|
741
|
+
available, the command returns a typed not-comparable error naming the nearest
|
|
742
|
+
baseline and the hash mismatch. The comparison output is facts and deltas only:
|
|
743
|
+
no threshold logic, no verdict field, and no release gate.
|
|
744
|
+
|
|
659
745
|
CI runs `python3 scripts/run_advisory_retrieval_eval.py` after pytest. The step
|
|
660
746
|
initializes and indexes the checked-out project, evaluates the checked-in
|
|
661
747
|
real-history fixtures, evaluates the checked-in adversarial fixture against a
|
|
662
|
-
prepared temp project, and prints a compact JSON summary.
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
748
|
+
prepared temp project, and prints a compact JSON summary. When a local
|
|
749
|
+
retrieval-eval baseline exists, the script also prints the same comparison
|
|
750
|
+
facts as advisory output.
|
|
751
|
+
|
|
752
|
+
Advisory-vs-blocking boundary:
|
|
753
|
+
|
|
754
|
+
- Metric deltas never fail CI in v0.2.1.
|
|
755
|
+
- Eval infrastructure integrity failures do fail CI: eval command errors,
|
|
756
|
+
fixture contract violations, unreadable fixtures, and provenance computation
|
|
757
|
+
failures.
|
|
758
|
+
- Broken measurement is not advisory.
|
|
759
|
+
|
|
760
|
+
Semantic promotion gate: richer retrieval approaches such as semantic
|
|
761
|
+
retrieval, Tree-sitter parsing, or call-graph retrieval remain out of scope for
|
|
762
|
+
v0.2.1. Future promotion is evidence-driven: baseline history and fixture
|
|
763
|
+
coverage decide whether richer retrieval is justified, not enthusiasm. This
|
|
764
|
+
document deliberately defines no metric thresholds or release gates.
|
|
@@ -134,7 +134,7 @@ jobs:
|
|
|
134
134
|
- uses: actions/setup-python@v5
|
|
135
135
|
with:
|
|
136
136
|
python-version: "3.11"
|
|
137
|
-
- uses: owner/project-loop-harness/.github/actions/project-loop-validate@v0.2.
|
|
137
|
+
- uses: owner/project-loop-harness/.github/actions/project-loop-validate@v0.2.1
|
|
138
138
|
with:
|
|
139
139
|
root: "."
|
|
140
140
|
strict: "true"
|
{project_loop_harness-0.2.0 → project_loop_harness-0.2.1}/scripts/run_advisory_retrieval_eval.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import argparse
|
|
3
4
|
import json
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
import subprocess
|
|
@@ -23,22 +24,29 @@ ADVERSARIAL_SENSITIVE_FILES = {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def main() -> int:
|
|
27
|
+
def main(argv: list[str] | None = None) -> int:
|
|
28
|
+
args = _parse_args(argv)
|
|
29
|
+
root = Path(args.root).resolve()
|
|
30
|
+
fixtures = args.fixtures or [
|
|
31
|
+
str(root / "tests/fixtures/retrieval_v0.json"),
|
|
32
|
+
str(root / "tests/fixtures/retrieval_real_history_v0.json"),
|
|
33
|
+
]
|
|
27
34
|
fixture_summaries: list[dict[str, Any]] = []
|
|
28
|
-
_init_project(
|
|
29
|
-
_build_index(
|
|
30
|
-
for fixture in
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
_init_project(root)
|
|
36
|
+
_build_index(root)
|
|
37
|
+
for fixture in fixtures:
|
|
38
|
+
fixture_summaries.append(_evaluate_fixture(root, fixture))
|
|
39
|
+
|
|
40
|
+
if not args.skip_adversarial:
|
|
41
|
+
with tempfile.TemporaryDirectory(prefix="pcl-retrieval-adversarial-") as temp_dir:
|
|
42
|
+
adversarial_root = Path(temp_dir)
|
|
43
|
+
_prepare_adversarial_project(adversarial_root)
|
|
44
|
+
fixture_summaries.append(
|
|
45
|
+
_evaluate_fixture(
|
|
46
|
+
adversarial_root,
|
|
47
|
+
str(REPO_ROOT / "tests/fixtures/retrieval_adversarial_v0.json"),
|
|
48
|
+
)
|
|
49
|
+
)
|
|
42
50
|
|
|
43
51
|
print(
|
|
44
52
|
json.dumps(
|
|
@@ -46,6 +54,9 @@ def main() -> int:
|
|
|
46
54
|
"ok": True,
|
|
47
55
|
"mode": "advisory",
|
|
48
56
|
"note": "Metric values are reported for trend review; fixture or eval crashes fail this command.",
|
|
57
|
+
"baseline_note": (
|
|
58
|
+
"Baseline comparisons are advisory when present; metric deltas do not fail this command."
|
|
59
|
+
),
|
|
49
60
|
"fixtures": fixture_summaries,
|
|
50
61
|
},
|
|
51
62
|
indent=2,
|
|
@@ -55,6 +66,14 @@ def main() -> int:
|
|
|
55
66
|
return 0
|
|
56
67
|
|
|
57
68
|
|
|
69
|
+
def _parse_args(argv: list[str] | None) -> argparse.Namespace:
|
|
70
|
+
parser = argparse.ArgumentParser()
|
|
71
|
+
parser.add_argument("--root", default=str(REPO_ROOT))
|
|
72
|
+
parser.add_argument("--fixture", action="append", dest="fixtures", default=None)
|
|
73
|
+
parser.add_argument("--skip-adversarial", action="store_true")
|
|
74
|
+
return parser.parse_args(argv)
|
|
75
|
+
|
|
76
|
+
|
|
58
77
|
def _prepare_adversarial_project(root: Path) -> None:
|
|
59
78
|
_init_project(root)
|
|
60
79
|
(root / "src" / "pkg").mkdir(parents=True, exist_ok=True)
|
|
@@ -113,6 +132,9 @@ def _evaluate_fixture(root: Path, fixture: str) -> dict[str, Any]:
|
|
|
113
132
|
adversarial = _adversarial_summary(evaluation["tasks"])
|
|
114
133
|
if adversarial:
|
|
115
134
|
summary["adversarial"] = adversarial
|
|
135
|
+
comparison = _compare_fixture_if_baseline_exists(root, fixture)
|
|
136
|
+
if comparison:
|
|
137
|
+
summary["baseline_comparison"] = comparison
|
|
116
138
|
return summary
|
|
117
139
|
|
|
118
140
|
|
|
@@ -132,20 +154,49 @@ def _adversarial_summary(tasks: list[dict[str, Any]]) -> dict[str, Any]:
|
|
|
132
154
|
|
|
133
155
|
|
|
134
156
|
def _run_pcl(args: list[str]) -> dict[str, Any]:
|
|
135
|
-
completed =
|
|
157
|
+
completed = _run_pcl_completed(args)
|
|
158
|
+
if completed.returncode != 0:
|
|
159
|
+
_print_completed_output(completed)
|
|
160
|
+
raise SystemExit(completed.returncode)
|
|
161
|
+
return json.loads(completed.stdout)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _run_pcl_completed(args: list[str]) -> subprocess.CompletedProcess[str]:
|
|
165
|
+
return subprocess.run(
|
|
136
166
|
[PYTHON, "-m", "pcl", *args],
|
|
137
167
|
cwd=REPO_ROOT,
|
|
138
168
|
capture_output=True,
|
|
139
169
|
check=False,
|
|
140
170
|
text=True,
|
|
141
171
|
)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _compare_fixture_if_baseline_exists(root: Path, fixture: str) -> dict[str, Any] | None:
|
|
175
|
+
if not any((root / ".project-loop" / "evidence" / "retrieval-eval").glob("*.json")):
|
|
176
|
+
return None
|
|
177
|
+
completed = _run_pcl_completed(
|
|
178
|
+
[
|
|
179
|
+
"--root",
|
|
180
|
+
str(root),
|
|
181
|
+
"eval",
|
|
182
|
+
"retrieval",
|
|
183
|
+
"--fixture",
|
|
184
|
+
fixture,
|
|
185
|
+
"--compare-baseline",
|
|
186
|
+
"--json",
|
|
187
|
+
]
|
|
188
|
+
)
|
|
189
|
+
if completed.returncode == 0:
|
|
190
|
+
return json.loads(completed.stdout)["comparison"]
|
|
191
|
+
_print_completed_output(completed)
|
|
192
|
+
raise SystemExit(completed.returncode)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _print_completed_output(completed: subprocess.CompletedProcess[str]) -> None:
|
|
196
|
+
if completed.stdout:
|
|
197
|
+
print(completed.stdout, file=sys.stderr, end="")
|
|
198
|
+
if completed.stderr:
|
|
199
|
+
print(completed.stderr, file=sys.stderr, end="")
|
|
149
200
|
|
|
150
201
|
|
|
151
202
|
def _append_text(path: Path, text: str) -> None:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.1"
|
|
@@ -14,7 +14,10 @@ from .code_index import (
|
|
|
14
14
|
analyze_impact,
|
|
15
15
|
build_code_index,
|
|
16
16
|
code_index_status,
|
|
17
|
+
compare_retrieval_baseline,
|
|
17
18
|
evaluate_retrieval,
|
|
19
|
+
propose_retrieval_fixture,
|
|
20
|
+
record_retrieval_baseline,
|
|
18
21
|
search_code,
|
|
19
22
|
)
|
|
20
23
|
from .commands import (
|
|
@@ -618,6 +621,34 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
618
621
|
eval_sub = p_eval.add_subparsers(dest="eval_command", required=True)
|
|
619
622
|
p_eval_retrieval = eval_sub.add_parser("retrieval", help="Evaluate indexed retrieval")
|
|
620
623
|
p_eval_retrieval.add_argument("--fixture", required=True)
|
|
624
|
+
eval_retrieval_baseline = p_eval_retrieval.add_mutually_exclusive_group()
|
|
625
|
+
eval_retrieval_baseline.add_argument(
|
|
626
|
+
"--record-baseline",
|
|
627
|
+
action="store_true",
|
|
628
|
+
help="Store the retrieval eval payload as a provenance-bearing evidence baseline.",
|
|
629
|
+
)
|
|
630
|
+
eval_retrieval_baseline.add_argument(
|
|
631
|
+
"--compare-baseline",
|
|
632
|
+
action="store_true",
|
|
633
|
+
help="Compare against the latest recorded baseline with the same fixture hash.",
|
|
634
|
+
)
|
|
635
|
+
p_eval_fixture = eval_sub.add_parser("fixture", help="Manage retrieval fixture candidates")
|
|
636
|
+
eval_fixture_sub = p_eval_fixture.add_subparsers(dest="eval_fixture_command", required=True)
|
|
637
|
+
p_eval_fixture_propose = eval_fixture_sub.add_parser(
|
|
638
|
+
"propose",
|
|
639
|
+
help="Propose an unlabeled retrieval fixture from a context receipt.",
|
|
640
|
+
)
|
|
641
|
+
p_eval_fixture_propose.add_argument(
|
|
642
|
+
"--from-receipt",
|
|
643
|
+
required=True,
|
|
644
|
+
dest="from_receipt",
|
|
645
|
+
help="Context receipt evidence ID to stage as an unlabeled fixture candidate.",
|
|
646
|
+
)
|
|
647
|
+
p_eval_fixture_propose.add_argument(
|
|
648
|
+
"--force",
|
|
649
|
+
action="store_true",
|
|
650
|
+
help="Overwrite an existing proposed candidate after confirming no human labels will be lost.",
|
|
651
|
+
)
|
|
621
652
|
|
|
622
653
|
p_verification = sub.add_parser("verification", help="Record verification results")
|
|
623
654
|
verification_sub = p_verification.add_subparsers(dest="verification_command", required=True)
|
|
@@ -1878,13 +1909,38 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1878
1909
|
return 0
|
|
1879
1910
|
|
|
1880
1911
|
if args.command == "eval" and args.eval_command == "retrieval":
|
|
1881
|
-
|
|
1912
|
+
if args.record_baseline:
|
|
1913
|
+
result = record_retrieval_baseline(paths, fixture_path=args.fixture)
|
|
1914
|
+
elif args.compare_baseline:
|
|
1915
|
+
result = compare_retrieval_baseline(paths, fixture_path=args.fixture)
|
|
1916
|
+
else:
|
|
1917
|
+
result = evaluate_retrieval(paths, fixture_path=args.fixture)
|
|
1882
1918
|
if json_output:
|
|
1883
1919
|
_print_json(result)
|
|
1920
|
+
elif args.record_baseline:
|
|
1921
|
+
print(result["baseline"]["evidence_path"])
|
|
1922
|
+
elif args.compare_baseline:
|
|
1923
|
+
print(to_pretty_json(result["comparison"]))
|
|
1884
1924
|
else:
|
|
1885
1925
|
print(to_pretty_json(result["evaluation"]))
|
|
1886
1926
|
return 0
|
|
1887
1927
|
|
|
1928
|
+
if (
|
|
1929
|
+
args.command == "eval"
|
|
1930
|
+
and args.eval_command == "fixture"
|
|
1931
|
+
and args.eval_fixture_command == "propose"
|
|
1932
|
+
):
|
|
1933
|
+
result = propose_retrieval_fixture(
|
|
1934
|
+
paths,
|
|
1935
|
+
receipt_evidence_id=args.from_receipt,
|
|
1936
|
+
force=args.force,
|
|
1937
|
+
)
|
|
1938
|
+
if json_output:
|
|
1939
|
+
_print_json(result)
|
|
1940
|
+
else:
|
|
1941
|
+
print(result["fixture"]["path"])
|
|
1942
|
+
return 0
|
|
1943
|
+
|
|
1888
1944
|
if args.command == "verification" and args.verification_command == "record":
|
|
1889
1945
|
result = record_verification(
|
|
1890
1946
|
paths,
|
|
@@ -30,7 +30,10 @@ from .eval import (
|
|
|
30
30
|
_resolve_fixture_path,
|
|
31
31
|
_retrieved_paths_for_fixture_task,
|
|
32
32
|
_string_set,
|
|
33
|
+
compare_retrieval_baseline,
|
|
33
34
|
evaluate_retrieval,
|
|
35
|
+
propose_retrieval_fixture,
|
|
36
|
+
record_retrieval_baseline,
|
|
34
37
|
)
|
|
35
38
|
from .impact import (
|
|
36
39
|
IMPACT_CONTRACT_VERSION,
|